name: Ops cron (backup + IndexNow) # Scheduled operational jobs that don't belong in the app's own worker-tier # scheduler (notifications/scheduler.py, Track A chunk 5) because they're # infra/ops concerns rather than app-domain background work -- see the job # classification table in # docs/architecture/repo-topology-and-infrastructure.md ยง7. # # Both jobs SSH into the prod host and run inside the already-running compose # stack (docker compose exec), the same way deploy.sh already runs its own # post-deploy IndexNow ping -- no new network exposure, no separate dependency # install. Runs on the `docker` label (the always-on Swarm-hosted runner # deploy/forgejo/ stood up) and reuses the same SSH secrets deploy.yml already # has (SSH_HOST/SSH_USER/SSH_KEY/SSH_PORT) -- inherits, and will automatically # benefit from a fix to, deploy.yml's own flagged branch/host mismatch (that # workflow's header comment: it still targets `main`, while # terraform.tfvars.example names prod's branch `release`). on: schedule: # 03:00 UTC daily -- a low-traffic window for both jobs. - cron: '0 3 * * *' workflow_dispatch: {} jobs: backup: name: pg_dump backup runs-on: docker steps: - name: Dump the prod database over SSH uses: https://github.com/appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} port: ${{ secrets.SSH_PORT }} script: | set -euo pipefail cd /opt/thermograph backup_dir="$HOME/thermograph-backups" mkdir -p "$backup_dir" stamp="$(date -u +%Y%m%dT%H%M%SZ)" out="$backup_dir/thermograph-$stamp.dump" docker compose exec -T db pg_dump -U thermograph -d thermograph \ --format=custom > "$out" echo "wrote $out ($(du -h "$out" | cut -f1))" # The dumps are the disaster-recovery copy, not a versioned # archive -- keep the last 14 days and let the rest age out. find "$backup_dir" -name 'thermograph-*.dump' -mtime +14 -delete indexnow: name: IndexNow ping runs-on: docker steps: - name: Ping IndexNow if the URL set changed uses: https://github.com/appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} port: ${{ secrets.SSH_PORT }} script: | set -euo pipefail cd /opt/thermograph set -a; . /etc/thermograph.env 2>/dev/null || true; set +a docker compose exec -T app python indexnow.py --if-changed \ "${THERMOGRAPH_BASE_URL:-https://thermograph.org}"