42bf40bd2c
gates / gates (push) Successful in 29s
- Twelve steps published on both venues (catalog): first ladders for calibre-web, gitea, wger, crafty-controller, uptime-kuma, zipline (two steps); within-major emby, ghost, home-assistant, outline, rallly. immich's step v3.0.3 -> v3.2.2 re-proven at 768M (Part D). - Part C: the night leg skips a digest-only change AND the catalog never records a same-tag re-test, so a same-name upstream fix reaches no box. Row R-740; the decision in STATUS; `09` decision 30 carries a dated note (the decision itself unchanged). - Rows: 369 -> 377. Opened R-735..R-742; closed R-735, R-738, R-742; narrowed R-462, R-624, R-446, R-440, R-734, R-732. The currency audit gains §1b (and corrects its 31+1 to 30+2). Evidence: documentation/audits/more-night-apps-2026-09-30/. Report: REPORT-more-night-apps-2026-09-30.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
63 lines
3.9 KiB
Python
63 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""undocase.py <app> <sub> <svc> <pg-user> <db> — the brief's ONE undo case per app on 9202 (scratch guest).
|
|
|
|
The load failing is the case (the docmost method, `audits/night-2026-09-26/D/Da-*`): an object the OLD major
|
|
has and 18 does not — the `adminpack` extension (removed in PostgreSQL 17) — is created in the app's own
|
|
database, the guarded Update is pressed (the drill catalog's step carries the conversion mark), the load
|
|
into 18 must fail, and the BOX must undo it by itself: old pin, old datadir, `undone`, the seed read back.
|
|
The extension is dropped again afterwards, so the real move that follows converts clean data.
|
|
|
|
Evidence: box/<app>/undo.txt + box/<app>/undo-verdict.json. Never prints a secret.
|
|
"""
|
|
import json, os, sys, time
|
|
import walk as w
|
|
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
|
|
import upgrade_fixtures_box as fixtures, upgrade_fixtures_box28 as _f28
|
|
FX = dict(_f28.FIXTURES28); FX.update(fixtures.FIXTURES) # the box set wins, as upgrade_boxport.get() decides
|
|
|
|
app, sub, svc, user, db = sys.argv[1:6]
|
|
EVD = f"{w.EV}/box/{app}"; os.makedirs(EVD, exist_ok=True)
|
|
log = open(f"{EVD}/undo.txt", "a", buffering=1)
|
|
|
|
|
|
def say(*a):
|
|
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
|
|
|
|
|
|
w.login()
|
|
toks = json.load(open(w.SC + "/seed-tokens-box.json"))
|
|
if isinstance(toks.get(app), dict) and toks[app].get("__generated"):
|
|
w.GENERATED[app] = toks[app]["__generated"]
|
|
psql = lambda sql: w.guest(f"docker exec {svc} psql -U {user} -d {db} -Atc \"{sql}\" 2>&1").strip()
|
|
pg = lambda: w.guest(f"docker exec {svc} sh -c 'cat $PGDATA/PG_VERSION' 2>&1").strip()
|
|
say(f"# undo case (the load fails) — {time.strftime('%Y-%m-%dT%H:%M:%S%z')}")
|
|
st = w.stack(app); before = (st.get("app_config") or {}).get("pinned_images")
|
|
say(f"before: PG_VERSION={pg()} pinned={before}")
|
|
say("before: seed reads back through the front door =", FX[app].verify(w, sub, toks[app], say))
|
|
say("setup: CREATE EXTENSION adminpack ->", psql("CREATE EXTENSION IF NOT EXISTS adminpack"))
|
|
say("setup: extensions now:", psql("select string_agg(extname, ' ' order by extname) from pg_extension"))
|
|
ctrl = w.sh(["ssh", "demo-hp", "pct exec 9202 -- docker run --rm postgres:18-alpine sh -c 'ls /usr/local/share/postgresql/extension/ | grep -c ^adminpack || true'"]).stdout.strip()
|
|
say(f"control: 18 has no adminpack: {ctrl}")
|
|
since = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
|
|
res = w.press_update(app, poll=1, cap_s=1800)
|
|
for p in res.get("phases", []):
|
|
say(f" phase +{p['t']}s {p['phase']} | err={p['error']}")
|
|
say("result:", json.dumps({k: res.get(k) for k in ("accepted", "http", "duration_s", "final_phase", "update_error", "hold_reason", "state")}))
|
|
time.sleep(10)
|
|
st = w.stack(app); after = (st.get("app_config") or {}).get("pinned_images")
|
|
after_pg = pg()
|
|
read = FX[app].verify(w, sub, toks[app], say)
|
|
say(f"after: PG_VERSION={after_pg} pinned={after} state={st.get('state')}")
|
|
say("after: seed reads back through the front door =", read)
|
|
conv = w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'update {app}|CONVERT|UNDO' | grep -v DEBUG | cut -c1-400")
|
|
log.write("controller lines:\n" + conv + "\n")
|
|
say("cleanup: DROP EXTENSION adminpack ->", psql("DROP EXTENSION IF EXISTS adminpack"))
|
|
verdict = {"app": app, "case": "the load into 18 fails (adminpack, an object 18 lacks)", "venue": "box 9202, the product's guarded Update",
|
|
"final_phase": res.get("final_phase"), "duration_s": res.get("duration_s"),
|
|
"pg_version_after": after_pg, "pinned_before": before, "pinned_after": after, "seed_read_after": read,
|
|
"undone_line": next((l.split("] ", 2)[-1] for l in conv.splitlines() if "UNDONE" in l), ""),
|
|
"ok": res.get("final_phase") == "undone" and read is True and after == before,
|
|
"measured_at": since}
|
|
json.dump(verdict, open(f"{EVD}/undo-verdict.json", "w"), indent=2)
|
|
say(f"RESULT undo case ok={verdict['ok']} final_phase={res.get('final_phase')} PG_VERSION={after_pg} seed_after={read}")
|