From f41ca5405ac058cb6cebe57f8ab730dfc7e753c5 Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 28 Apr 2026 17:25:46 +0200 Subject: [PATCH] fix(deploy): safe-db-push cleanup trap also removes snapshot + journal The previous version of the cleanup trap only deleted SQL files left by drizzle-kit's probe-generate, but not the matching `_snapshot.json` (56 KB per service) or the journal entry. Each deploy then leaked one snapshot file into the runner's working tree. Surfaced after my own local smoke-test: ran the script against mana-auth, found a 56 KB \`drizzle/meta/0000_snapshot.json\` left behind that I had to clean up manually. The trap now: - Computes the full set of files added under \`drizzle/\` during this run (not just SQL) and removes every one of them. - Strips the probe's journal entry via jq. - If the \`drizzle/\` dir didn't exist before the run, removes it entirely. Otherwise sweeps empty meta/ subdirs the run created. Smoke-tested locally: working tree is clean after each run regardless of whether drizzle/ existed beforehand. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/mac-mini/safe-db-push.sh | 61 +++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/scripts/mac-mini/safe-db-push.sh b/scripts/mac-mini/safe-db-push.sh index f331cc6ea..96ede0467 100755 --- a/scripts/mac-mini/safe-db-push.sh +++ b/scripts/mac-mini/safe-db-push.sh @@ -70,19 +70,51 @@ if ! pnpm exec drizzle-kit --version >/dev/null 2>&1; then exit 0 fi -# Snapshot the existing migration set before we generate. Anything new -# afterwards is the diff this push would apply. -PRE_GEN_FILES=$(find drizzle -maxdepth 2 -name '*.sql' 2>/dev/null | sort || true) +# Snapshot the existing drizzle/ artifacts before we generate. Anything +# new (or newly modified) afterwards is the probe trail this run left +# behind, which the cleanup trap will tear down — even on failure — so +# the runner doesn't accumulate pseudo-migration files between deploys. +DRIZZLE_DIR_EXISTED=false +[ -d drizzle ] && DRIZZLE_DIR_EXISTED=true +PRE_GEN_FILES=$(find drizzle -maxdepth 3 -type f 2>/dev/null | sort || true) # Generate-only — does not touch the database. echo "[safe-db-push] $SVC: generating diff…" GEN_OUT=$(pnpm exec drizzle-kit generate --name "__ci_safety_check_$$" 2>&1 || true) echo "$GEN_OUT" | tail -20 -POST_GEN_FILES=$(find drizzle -maxdepth 2 -name '*.sql' 2>/dev/null | sort || true) +POST_GEN_FILES=$(find drizzle -maxdepth 3 -type f 2>/dev/null | sort || true) +NEW_GEN_FILES=$(comm -13 <(echo "$PRE_GEN_FILES") <(echo "$POST_GEN_FILES") | grep -v '^$' || true) + +# Trap so we always tear down the probe trail (SQL + snapshot + journal +# + the whole drizzle/ tree if we created it), even on failure or +# early-exit. Without this the runner accumulates one pseudo-migration +# pair per deploy and `git status` slowly turns into noise. +cleanup() { + # Remove every file we wrote. Sorted in reverse so deeper paths go + # before their parents (relevant for the rmdir cleanup below). + for f in $(echo "$NEW_GEN_FILES" | tr ' ' '\n' | sort -r); do + [ -n "$f" ] && rm -f "$f" + done + # Strip our probe entry from the journal so legitimate generates + # don't see a phantom "__ci_safety_check" tag. + if [ -f drizzle/meta/_journal.json ] && command -v jq >/dev/null 2>&1; then + tmp=$(mktemp) + jq '.entries |= map(select(.tag | test("__ci_safety_check") | not))' \ + drizzle/meta/_journal.json > "$tmp" && mv "$tmp" drizzle/meta/_journal.json || true + fi + # Drop empty dirs we may have created (drizzle/, drizzle/meta/). + if [ "$DRIZZLE_DIR_EXISTED" = "false" ]; then + rm -rf drizzle 2>/dev/null || true + else + # Existing dir — only sweep empty subdirs we touched. + rmdir drizzle/meta 2>/dev/null || true + fi +} +trap cleanup EXIT # New SQL files = the diff -NEW_SQL=$(comm -13 <(echo "$PRE_GEN_FILES") <(echo "$POST_GEN_FILES") | grep -v '^$' || true) +NEW_SQL=$(echo "$NEW_GEN_FILES" | grep '\.sql$' || true) if [ -z "$NEW_SQL" ]; then echo "[safe-db-push] $SVC: no schema changes — clean." @@ -92,25 +124,6 @@ fi echo "[safe-db-push] $SVC: schema diff detected:" echo "$NEW_SQL" -# Trap so we always remove the generated probe files, even on failure. -cleanup() { - for f in $NEW_SQL; do - rm -f "$f" - done - # drizzle-kit also writes a meta entry; remove the most recent one. - if [ -f drizzle/meta/_journal.json ]; then - # Best-effort cleanup — strip the entry that references our probe tag. - # If jq isn't available, leave it; the next legitimate `db:push` will - # overwrite anyway. - if command -v jq >/dev/null 2>&1; then - tmp=$(mktemp) - jq '.entries |= map(select(.tag | test("__ci_safety_check") | not))' \ - drizzle/meta/_journal.json > "$tmp" && mv "$tmp" drizzle/meta/_journal.json || true - fi - fi -} -trap cleanup EXIT - # Refuse to auto-apply destructive changes. The operator must review # and either fix the schema (if the diff was unintentional) or run # `drizzle-kit push --force` manually after taking a fresh pg_dump.