Pin images by digest; add update, smoke-test and status scripts

Goal: at every session start, run exactly the application the repo says — and be able
to move to a new upstream release deliberately, with a way back.

The old setup (v1-latest + pull_policy: always) was unreliable in both directions.
`docker compose up -d` silently pulled a new application, including an irreversible
SurrealDB schema migration, while a reboot (restart: always) or `docker compose start`
kept running the old image without pulling anything. The running version was effectively
unpredictable.

Note v1-latest tracks releases, not main: the current image (v1.10.0, 2026-06-18) IS the
latest release — the repo being "ahead" is unreleased code, which is explicitly not
wanted here. So this is about guaranteeing and detecting, not catching up.

- docker-compose.yml: both images pinned by digest, pull_policy: missing.
- scripts/check_updates.sh: reports running version/digest vs the latest release and
  registry digest. Changes nothing; meant for the session-start ritual.
- scripts/update_stack.sh: resolve new digest -> stop -> back up surreal_data,
  notebook_data and docker-compose.yml -> repin -> start -> smoke test -> roll back data
  AND compose file if the smoke test fails. The data backup is the point: the app migrates
  the DB on startup and an older app cannot read a migrated DB, so a bad update would
  otherwise be a one-way door. tar runs inside a container because the data dirs are
  root-owned and the host user cannot restore over them.
- scripts/smoke_test.sh: checks what actually breaks here, not just "does it start".
  Every local adaptation leans on upstream internals and can break silently: the
  prompts/podcast directory mount masks the image's directory (a new template upstream
  would be invisible), config/content_core.yaml freezes content-core's defaults (because
  CCORE_CONFIG_PATH replaces rather than merges), and the env-var workarounds depend on
  current content-core/esperanto/podcast_creator behaviour. So it verifies those
  assumptions explicitly, plus API, TTS audio, STT and a real YouTube transcript.

Both scripts read the digest from the lfnovo/open_notebook line specifically. A naive
"first sha256 in the file" grep matches surrealdb (listed first) — caught while testing:
check_updates.sh falsely reported an update, and update_stack.sh would have rewritten the
database image instead of the application.

Verified: smoke test green against the current stack, and red (exit 1) when failures are
injected (dead TTS port, removed template variable). Backup/restore mechanics exercised
separately: 44 MB in 3.3s, restore yields 13 DB files and 91 notebook files. The update
path itself cannot be exercised end to end until a newer image exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-07-11 14:12:31 +02:00
commit 4805fe75ed
7 changed files with 434 additions and 7 deletions

View file

@ -1,8 +1,21 @@
# Images sind bewusst auf einen Digest gepinnt, nicht auf einen beweglichen Tag.
#
# Warum: "v1-latest" ist ein wandernder Zeiger. Mit pull_policy: always zog jedes
# `docker compose up -d` unbemerkt eine neue Anwendung — inklusive Schema-Migration der
# SurrealDB, die sich nicht ohne Weiteres zurueckdrehen laesst. Gleichzeitig griff das
# nur bei `up -d`: nach einem Reboot startete `restart: always` weiter das ALTE Image.
# Also beides unzuverlaessig: mal heimlich neu, mal heimlich alt.
#
# Mit Digest-Pin laeuft immer exakt der Stand, der hier im Repo steht. Aktualisiert wird
# bewusst und mit Rueckfallpunkt:
# ./scripts/check_updates.sh -> gibt es ein neues Release? (aendert nichts)
# ./scripts/update_stack.sh -> Backup, Update, Rauchtest, bei Fehler Rollback
services:
surrealdb:
image: surrealdb/surrealdb:v2
# surrealdb:v2 (Digest-Pin: auch ein Minor-Sprung kann das Storage-Format anfassen)
image: surrealdb/surrealdb:v2@sha256:d653f6c8a89e81f865ee31cd2f587c50f50ace922175e04150b1e385d2f86011
restart: always
pull_policy: always
pull_policy: missing
command: start --log info --user ${SURREAL_USER:-root} --pass ${SURREAL_PASSWORD:-root} rocksdb:/mydata/mydatabase.db
user: root # Required for bind mounts on Linux
environment:
@ -13,9 +26,11 @@ services:
- ./surreal_data:/mydata
open_notebook:
image: lfnovo/open_notebook:v1-latest
# Open Notebook v1.10.0 (Release vom 2026-06-18, Image vom selben Tag).
# Digest von scripts/update_stack.sh gepflegt — nicht von Hand aendern.
image: lfnovo/open_notebook:v1-latest@sha256:c8112fbd4b8fee7f2a20d3bdbea24e7d72267acfc990b803fd0ff4de30899b57
restart: always
pull_policy: always
pull_policy: missing
depends_on:
- surrealdb
ports: