adventurelog: v0.12.1 -> v0.13.0 with its health and world-data fixes in the same commit (R-655, 09 decision 41)

Written by upgrade-test.py --write-ladder: bench (harness v4) healthy in 217 s, the world-data file present and reused ("Latest data already imported"), 6 migration lines, seed read back, memory watch anon peak 73.0 %, 0 restarts; box 9202 through the guarded Update in 204 s with the product's default 5-min wait (a first try under the drill's 90 s wait was UNDONE correctly). Same commit: (1) the frontend healthcheck override is dropped — v0.13.0 brings its own (node at /usr/bin/node); (2) a world-data file that does not parse to its end is renamed aside before start so download-countries fetches it again — with an import pending, the image's own start crash-looped 10 times on IncompleteJSONError, with the step it was set aside, downloaded and healthy. Evidence felhom.eu/documentation/audits/version-travel-2026-09-26/C/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-27 12:55:12 +02:00
parent 53b84cadb9
commit 06ea7da7ba
2 changed files with 32 additions and 13 deletions
+7 -1
View File
@@ -10,7 +10,7 @@ subdomain: "travel"
slug: "adventurelog"
# catalog_since: the date THIS repo last changed this app's pinned images. Any commit that
# changes an image: line must set this to the same day (see CLAUDE.md).
catalog_since: "2026-07-18"
catalog_since: "2026-09-27"
# --- Resource hints (displayed on deploy screen) ---
resources:
@@ -110,3 +110,9 @@ i18n:
label: 'Database password'
- env_var: SECRET_KEY
label: 'Encryption key'
# update_ladder — the test record: one tested step per line, oldest first (JSON flow mappings,
# `09-update-architecture.md` §6.4 part 4). WRITTEN BY scripts/upgrade-test.py, never by hand;
# gated by scripts/check-test-record.py. An image: move without a proven entry here is refused.
update_ladder:
- {"from": {"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.12.1", "adventurelog-postgres": "postgis/postgis:16-3.5-alpine", "adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1"}, "to": {"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.13.0", "adventurelog-postgres": "postgis/postgis:16-3.5-alpine", "adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0"}, "digest": {"adventurelog": "sha256:0250d9cb0d745a8d768f31d3cdc68ac14bfa8b43dedfe0f550158e3861c12db7", "adventurelog-frontend": "sha256:51ee22428b4192f4372b54f84c8b90cd710311ad99993c5045f53b51717dd3f7", "adventurelog-postgres": "sha256:47e961a569fd52ff31f0fe205ed91eeab17d9f5fff6722e6d7ea6b588748b293"}, "verdict": "proven", "tested_at": "2026-09-27T10:29:35Z", "harness_version": 4, "evidence": "felhom.eu/documentation/audits/version-travel-2026-09-26/C/apps/adventurelog/bench/evidence/MV-adventurelog/verdict.json", "box_evidence": "felhom.eu/documentation/audits/version-travel-2026-09-26/C/apps/adventurelog/box/box-verdict-adventurelog.json", "memory_peak_pct": 73.0, "marks": {"files_may_change": false, "needs_person": null, "memory_tight": false}, "memory_basis": "anon", "memory_cgroup_peak_pct": 100.0}
+25 -12
View File
@@ -10,7 +10,7 @@
services:
adventurelog:
image: ghcr.io/seanmorley15/adventurelog-backend:v0.12.1
image: ghcr.io/seanmorley15/adventurelog-backend:v0.13.0
container_name: adventurelog
restart: unless-stopped
depends_on:
@@ -30,6 +30,25 @@ services:
- PUBLIC_URL=https://${SUBDOMAIN}.${DOMAIN}
- CSRF_TRUSTED_ORIGINS=https://${SUBDOMAIN}.${DOMAIN}
- FRONTEND_URL=https://${SUBDOMAIN}.${DOMAIN}
# `09` §3 decision 41 (R-655): the world-data download stays. v0.13.0 runs `download-countries` at EVERY
# start and fetches only when the file is ABSENT — a cut-off file from an interrupted download (v0.12.1
# ignored such failures) makes every start fail with `IncompleteJSONError`. Before the image's own
# entrypoint, a file that does not parse to its end is SET ASIDE (renamed, never deleted), so the
# command downloads it again. The same entrypoint and command as the image's (both versions, measured).
entrypoint:
- /bin/bash
- -c
- |
for f in /code/media/countries+regions+states-*.json; do
[ -s "$$f" ] || continue
if ! python3 -c 'import ijson,sys; [0 for _ in ijson.items(open(sys.argv[1],"rb"),"item")]' "$$f" 2>/dev/null; then
mv "$$f" "$$f.cutoff-$$(date +%Y%m%d%H%M%S)"
echo "felhom: $$f did not parse to its end - set aside, it is downloaded again" >&2
fi
done
exec /code/entrypoint.sh "$$@"
- --
command: ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
volumes:
- adventurelog_media:/code/media
networks:
@@ -88,7 +107,7 @@ services:
start_period: 20s
adventurelog-frontend:
image: ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1
image: ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0
container_name: adventurelog-frontend
restart: unless-stopped
environment:
@@ -110,16 +129,10 @@ services:
resources:
limits:
memory: 256M
healthcheck:
# A frontend image distroless: nincs benne shell, wget vagy curl — a node
# bináris is csak abszolút úton érhető el (nem a PATH-on). A korábbi wget
# próba ezért soha nem futott le, a konténer véglegesen unhealthy lett, és
# a Traefik nem irányított rá forgalmat (404 az ügyfélnek).
test: ["CMD", "/nodejs/bin/node", "-e", "fetch('http://127.0.0.1:3000').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
# No healthcheck override (R-655): v0.12.1's image has none and keeps node at /nodejs/bin/node only;
# v0.13.0's image brings its own (`node` on the PATH, /usr/bin/node, fetching /health, which needs the
# backend). No single exec path works on both versions (measured 2026-09-27), and the old override
# (/nodejs/bin/node) left v0.13.0 unhealthy forever.
volumes:
adventurelog_media: