Automate production deploys of main (Maestro 85cca5ad)
CI / ci (push) Successful in 10m25s

deploy/auto-deploy.sh runs deploy/RUNBOOK.md unattended: no-op when
origin/main is the last deployed SHA in the deploy record, wait while its
Gitea CI status is pending, refuse (exit 1) on failure or no status, then
run every runbook block (rollback SHA from the running /health, clean
checkout, make docker, Litestream restore point, boot-required
configuration, retag, restart, version and PWA verification) and append a
deploy record line. --ref <ref> deploys an explicit ref through the same
blocks, bypassing the gates. A failed deploy is recorded and never retried
by the timer.

mealquest-auto-deploy.service/.timer run it as overseer every 15 minutes
(+5 min random delay), skipped while mealquest.service is not active.

deploy/RUNBOOK.md is the PR #203 runbook (feature/launch-readiness, not yet
on dev) plus an "Automated deploys" section; AGENTS.md describes deploy/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 06:58:51 -04:00
co-authored by Claude Opus 5.5
parent 34e84964a3
commit 2b9b0cb399
5 changed files with 640 additions and 1 deletions
@@ -0,0 +1,23 @@
# Deploys origin/main to production when it is new and its CI passed
# (/usr/local/sbin/mealquest-auto-deploy, from deploy/auto-deploy.sh).
# Triggered by mealquest-auto-deploy.timer; output goes to the journal.
# Installing it and the manual override: deploy/RUNBOOK.md "Automated deploys".
#
# ExecCondition= skips the run while mealquest.service is not active, so a
# stack stopped on purpose (a database or media restore) is never redeployed
# under the operator. It is deliberately not Requires=/Requisite= on
# mealquest.service: those would start the stack, or stop this run when the
# deploy itself restarts mealquest.service. Runs as overseer, who owns the
# checkout and uses sudo for the runbook's privileged steps.
[Unit]
Description=MealQuest automated deploy of main
Wants=network-online.target
After=network-online.target docker.service mealquest.service
[Service]
Type=oneshot
User=overseer
Group=overseer
WorkingDirectory=/home/overseer/mealquest
ExecCondition=/usr/bin/systemctl is-active --quiet mealquest.service
ExecStart=/usr/local/sbin/mealquest-auto-deploy
@@ -0,0 +1,17 @@
# Checks for a new main every 15 minutes (plus up to 5 minutes' random
# delay), starting 2 minutes after the timer starts. OnUnitActiveSec= alone
# never fires before the service's first run, so OnActiveSec= starts the
# cycle at boot and at `enable --now`. Persistent= is set as the task
# specified; systemd applies it only to OnCalendar= triggers, so with these
# monotonic triggers it has no effect.
[Unit]
Description=Periodic MealQuest automated deploy
[Timer]
OnActiveSec=2min
OnUnitActiveSec=15min
RandomizedDelaySec=5min
Persistent=true
[Install]
WantedBy=timers.target