- Sign-up, login, the invitation flows and password recovery allow ten requests a minute per client address; the inboxes give each sending origin (the keyId's) a bucket of 300 that refills at 300 a minute, and answer 429 beyond it, which peers retry. - deploy.yml dumps the PrivaPub database to /var/backups before it stops the service (the last seven are kept), and after the swap checks that Swagger answers 404 and that the shared inbox answers junk with 400 and an unsigned activity with 401. - ActivityPubClient and PostBoost, never used, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
108 lines
5.0 KiB
YAML
108 lines
5.0 KiB
YAML
name: Deploy
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
|
|
env:
|
|
UNIT: privapub
|
|
WEB_ROOT: /var/www/privapub.thepra.dev
|
|
BACKUPS: /var/backups/privapub.thepra.dev
|
|
LOCAL_URL: http://127.0.0.1:6970
|
|
PUBLIC_URL: https://privapub.thepra.dev
|
|
MONGO_URI: mongodb://127.0.0.1:27022
|
|
MONGO_DB: PrivaPub
|
|
|
|
jobs:
|
|
site:
|
|
name: privapub.thepra.dev
|
|
runs-on: build
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Resolve the build identity
|
|
run: |
|
|
echo "BUILD_COMMIT=$(echo "$GITHUB_SHA" | cut -c1-8)" >> "$GITHUB_ENV"
|
|
echo "BUILD_REF=${GITHUB_REF_NAME:-master}" >> "$GITHUB_ENV"
|
|
echo "BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
|
|
|
|
- name: Test
|
|
run: dotnet test PrivaPub.sln -c Release
|
|
|
|
- name: Publish
|
|
run: |
|
|
rm -rf "$GITHUB_WORKSPACE/publish"
|
|
dotnet publish PrivaPub/PrivaPub.csproj -c Release -r linux-x64 --self-contained true \
|
|
-o "$GITHUB_WORKSPACE/publish" \
|
|
-p:BuildCommit="$BUILD_COMMIT" -p:BuildRef="$BUILD_REF" -p:BuildTimeUtc="$BUILD_TIME"
|
|
|
|
- name: Assert the publish actually produced a build
|
|
run: |
|
|
P="$GITHUB_WORKSPACE/publish"
|
|
for f in PrivaPub PrivaPub.dll PrivaPub.ClientModels.dll appsettings.Production.json Data/languagesNative.json; do
|
|
[ -e "$P/$f" ] || { echo "::error::publish output is missing $f"; exit 1; }
|
|
done
|
|
grep -q '"includedFrameworks"' "$P/PrivaPub.runtimeconfig.json" \
|
|
|| { echo "::error::publish is not self-contained"; exit 1; }
|
|
age=$(( $(date +%s) - $(stat -c %Y "$P/PrivaPub.dll") ))
|
|
[ "$age" -lt 3600 ] || { echo "::error::PrivaPub.dll is ${age}s old - the publish reused a stale artifact"; exit 1; }
|
|
echo "publish OK, $(du -sh "$P" | cut -f1)"
|
|
|
|
- name: Snapshot the live directory
|
|
run: |
|
|
STAMP=$(date +%Y%m%d-%H%M%S)
|
|
rsync -a "$WEB_ROOT/" "$BACKUPS/site-$STAMP/"
|
|
echo "SNAPSHOT=$BACKUPS/site-$STAMP" >> "$GITHUB_ENV"
|
|
ls -1dt "$BACKUPS"/site-* 2>/dev/null | tail -n +4 | xargs -r rm -rf || true
|
|
|
|
- name: Dump the database
|
|
run: |
|
|
DUMP="$BACKUPS/mongo-$(date +%Y%m%d-%H%M%S).archive.gz"
|
|
mongodump --quiet --uri "$MONGO_URI" --db "$MONGO_DB" --gzip --archive="$DUMP"
|
|
echo "::notice::database dumped to $DUMP ($(du -h "$DUMP" | cut -f1))"
|
|
ls -1t "$BACKUPS"/mongo-*.archive.gz 2>/dev/null | tail -n +8 | xargs -r rm -f || true
|
|
|
|
- name: Stop, sync, start
|
|
run: |
|
|
sudo systemctl stop "$UNIT"
|
|
rsync -a --delete --exclude 'appsettings.Development.json' "$GITHUB_WORKSPACE/publish/" "$WEB_ROOT/"
|
|
chgrp www-data "$WEB_ROOT/appsettings.Production.json"
|
|
chmod 640 "$WEB_ROOT/appsettings.Production.json"
|
|
chmod +x "$WEB_ROOT/PrivaPub"
|
|
sudo systemctl start "$UNIT"
|
|
|
|
- name: Health check, roll back on failure
|
|
run: |
|
|
ok=0
|
|
for i in $(seq 1 40); do
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' "$LOCAL_URL/build.json" || true)
|
|
if [ "$code" = "200" ]; then ok=1; break; fi
|
|
sleep 3
|
|
done
|
|
if [ "$ok" != "1" ]; then
|
|
echo "::error::the site did not come back healthy - rolling back to $SNAPSHOT"
|
|
sudo systemctl stop "$UNIT"
|
|
rsync -a --delete "$SNAPSHOT/" "$WEB_ROOT/"
|
|
sudo systemctl start "$UNIT" || true
|
|
exit 1
|
|
fi
|
|
|
|
- name: Verify what is being served
|
|
run: |
|
|
served=$(curl -fsS "$PUBLIC_URL/build.json" | python3 -c "import json,sys; print(json.load(sys.stdin).get('commit',''))")
|
|
[ "$served" = "$BUILD_COMMIT" ] || { echo "::error::served build is '$served', expected '$BUILD_COMMIT'"; exit 1; }
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept: application/activity+json' "$PUBLIC_URL/peasants/privapub")
|
|
[ "$code" = "200" ] || { echo "::error::the instance actor answered $code"; exit 1; }
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' "$PUBLIC_URL/.well-known/nodeinfo")
|
|
[ "$code" = "200" ] || { echo "::error::nodeinfo answered $code"; exit 1; }
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' "$PUBLIC_URL/swagger/index.html")
|
|
[ "$code" = "404" ] || { echo "::error::swagger answered $code in production"; exit 1; }
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/activity+json' --data '{"junk":' "$PUBLIC_URL/human-centipede")
|
|
[ "$code" = "400" ] || { echo "::error::a junk inbox POST answered $code"; exit 1; }
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/activity+json' \
|
|
--data '{"type":"Follow","actor":"https://example.org/users/x","object":"'"$PUBLIC_URL"'/peasants/privapub"}' "$PUBLIC_URL/human-centipede")
|
|
[ "$code" = "401" ] || { echo "::error::an unsigned inbox POST answered $code"; exit 1; }
|
|
echo "::notice::serving $served"
|