From 459ee6ec282f9d69eb4fd7f9cfad271a146e9c3a Mon Sep 17 00:00:00 2001 From: dev_agent Date: Wed, 7 Oct 2026 17:54:42 -0400 Subject: [PATCH] Decide the rollback path from applied migration versions The rollback check diffed all of migrations/, so a change to the sqlc queries in migrations/queries/ sent the operator down the restore path and discarded household changes made since the pre-deploy point. Compare the versions in the live database's schema_migrations with the top-level *.sql files at the previous SHA instead, the rule internal/store/migrate.go enforces at start-up. Co-Authored-By: Claude Opus 5.5 --- deploy/RUNBOOK.md | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/deploy/RUNBOOK.md b/deploy/RUNBOOK.md index eaba571..fe8278c 100644 --- a/deploy/RUNBOOK.md +++ b/deploy/RUNBOOK.md @@ -568,14 +568,37 @@ the restart; the service fails fast at boot naming any missing required variable ### Rollback to a previous SHA -First check whether the rollback crosses a migration: compare -`migrations/` between the two SHAs. +First check whether the rollback crosses a migration, with the rule the +service applies at start-up (`internal/store/migrate.go`): the older build +refuses a database holding a version in `schema_migrations` that is not one +of its own migration files — the `*.sql` files directly in `migrations/` +(`migrations/queries/` holds sqlc queries, not migrations). Compare the +versions the live database has applied with the files the previous SHA has: ```sh cd /home/overseer/mealquest -git diff --stat HEAD -- migrations/ # empty: no migration crossed +D=/var/lib/docker/volumes/mealquest-data/_data +P= +rm -rf /tmp/mqcheck && mkdir /tmp/mqcheck +sudo sh -c "cp -a $D/mealquest.db* /tmp/mqcheck/" && sudo chown -R "$USER" /tmp/mqcheck +git ls-tree --name-only "$P" migrations/ > /tmp/mqcheck/known # top level only +python3 - /tmp/mqcheck/mealquest.db /tmp/mqcheck/known <<'PY' +import sqlite3, sys +applied = {v for (v,) in sqlite3.connect(sys.argv[1]).execute("select version from schema_migrations")} +known = {n[len("migrations/"):-len(".sql")] for n in open(sys.argv[2]).read().split() if n.endswith(".sql")} +if not applied or not known: + sys.exit("no versions read: check the database copy and the previous SHA, then run again") +unknown = sorted(applied - known) +print("unknown to the previous SHA:", " ".join(unknown) if unknown else "none") +PY +rm -rf /tmp/mqcheck ``` +It prints `none` when no migration crossed; otherwise it lists the versions +the older build does not know, and the migration-crossing path below is +required. If it exits with an error, stop and fix the cause — do not pick a +path. + **No migration crossed** — rebuild and restart the older SHA: ```sh