diff --git a/test/e2e/val-switch.sh b/test/e2e/val-switch.sh index 3b41a25..929a3a0 100755 --- a/test/e2e/val-switch.sh +++ b/test/e2e/val-switch.sh @@ -53,16 +53,16 @@ else git restore --staged docker-compose.yml fi -# The appview DB only has to match the branch's MIGRATION LEVEL, not the -# branch. Across the whole stack there are five distinct levels (27 for batches -# 00-08, 28 for 09-11, 29 for 12-13, 32 for 14, 34 for 15), so a per-level -# snapshot is reused instead of re-migrating from empty every time. +# Resetting the DB matters because ui.db holds the OAuth sessions AND the +# appview's oauth_p256/jwt_rsa keys, so wiping it logs out the registry +# credential helper and costs an interactive `docker-credential-atcr login`. +# +# Going FORWARD (the normal case — batches are validated in stack order) needs +# no reset at all: migrations are forward-only and the appview applies whatever +# is missing on boot, so Air rebuilding into the next branch migrates the live +# DB in place and the session survives. Only going BACKWARD needs a reset, since +# the DB then carries migrations the branch's code has never heard of. # -# This matters because ui.db holds the OAuth sessions AND the appview's -# oauth_p256/jwt_rsa keys. Nuking it logs out the registry credential helper, so -# a naive wipe-per-switch costs an interactive `docker-credential-atcr login` on -# every batch. Restoring a snapshot keeps the session alive across every batch -# that shares a level. # Key the snapshot on the FULL SET of migrations present, not the highest one. # Batching reorders migrations: val/04 carries e75b2e2, whose migration is 0031, # while 0028-0030 arrive later in batches 09, 12 and 14. So val/04 holds @@ -77,21 +77,38 @@ FINGERPRINT=$(printf '%s\n' "$MIGS" | md5sum | cut -c1-8) SNAP="$SNAPDIR/ui.level-${LEVEL}-${FINGERPRINT}.db" echo "==> branch migrations: max ${LEVEL}, set ${FINGERPRINT} ($(printf '%s\n' "$MIGS" | wc -l) files)" -echo "==> tearing down appview" -docker compose rm -sf atcr-appview +# Which migrations does the live DB already carry that this branch does not know? +APPLIED=$(docker exec atcr-appview sh -c \ + 'sqlite3 /var/lib/atcr/ui.db "select version from schema_migrations"' 2>/dev/null || true) +EXTRA=$(python3 -c ' +import sys +applied = {v.strip().lstrip("0") or "0" for v in sys.argv[1].split() if v.strip()} +branch = {v.strip().lstrip("0") or "0" for v in sys.argv[2].split() if v.strip()} +print(" ".join(sorted(applied - branch, key=int))) +' "$APPLIED" "$MIGS" 2>/dev/null || echo "") -if [ -f "$SNAP" ]; then - echo "==> restoring DB snapshot for level ${LEVEL} (keeps OAuth sessions alive)" - docker run --rm -v "${UI_VOLUME}:/v" -v "$SNAPDIR:/s:ro" alpine sh -c \ - 'rm -f /v/ui.db /v/ui.db-wal /v/ui.db-shm && cp /s/'"$(basename "$SNAP")"' /v/ui.db' +if [ -z "$APPLIED" ]; then + echo "==> no live DB; starting fresh" + docker compose rm -sf atcr-appview + docker volume rm "$UI_VOLUME" 2>/dev/null || true + docker compose up -d atcr-appview +elif [ -z "$EXTRA" ]; then + echo "==> moving forward; leaving the DB alone (Air migrates it in place, session survives)" + docker compose up -d atcr-appview >/dev/null 2>&1 || true else - echo "==> no snapshot for level ${LEVEL}; starting from an empty DB" - docker volume rm "$UI_VOLUME" 2>/dev/null || echo " (volume already gone)" + echo "==> moving BACKWARD; the DB carries migrations this branch lacks: ${EXTRA}" + docker compose rm -sf atcr-appview + if [ -f "$SNAP" ]; then + echo " restoring snapshot ${LEVEL}-${FINGERPRINT} (keeps OAuth sessions alive)" + docker run --rm -v "${UI_VOLUME}:/v" -v "$SNAPDIR:/s:ro" alpine sh -c \ + 'rm -f /v/ui.db /v/ui.db-wal /v/ui.db-shm && cp /s/'"$(basename "$SNAP")"' /v/ui.db' + else + echo " no snapshot for this migration set; starting from an empty DB (expect a re-login)" + docker volume rm "$UI_VOLUME" 2>/dev/null || true + fi + docker compose up -d atcr-appview fi -echo "==> starting appview" -docker compose up -d atcr-appview - echo "==> waiting for appview to answer on 127.0.0.1:5000" for i in $(seq 1 90); do code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/v2/ || true)