Pin node images to the checked-out source tree (node-26#19)
CI / lint (push) Successful in 10s
CI / test-op25 (push) Successful in 16s
CI / test (push) Failing after 49s
CI / lint (pull_request) Successful in 8s
CI / test-op25 (pull_request) Successful in 18s
CI / test (pull_request) Failing after 46s

Nodes checked out v1 code but pulled :latest/:stable images built from main.
Each image is now tagged src-<git tree hash of its build directory>: CI adds
that tag in every build-*.yml, and scripts/image-tags.sh computes the same
value on the node and writes it into .env as DRB_TAG_*, which
docker-compose.yml uses (falls back to :latest/:stable for local dev).
Tree hashes stay exact even though each image only rebuilds when its own
directory changes.

- install.sh pins after checkout and fails loudly if a pinned image is
  missing (compose pull only warns for services with a build: section).
- drb-update.sh pulls the target commit's pinned images BEFORE checkout,
  using the target's own compose file, so a missing image changes nothing
  (uvicorn --reload would otherwise run new code on old images). It then
  checks out, writes the pins, recreates, and removes superseded src- tags.
- New "Release images" workflow (v* tags + manual dispatch) builds any
  src- image a release ref is missing; it skips ones already published.

The live v1 tag is not moved.

Refs #19

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-10-04 18:09:29 -04:00
co-authored by Claude Opus 5.5
parent 80a0e88fc4
commit 6c8289f3f3
10 changed files with 263 additions and 20 deletions
+24 -8
View File
@@ -10,7 +10,7 @@
# --track-main for the owner's own always-latest nodes)
#
# Hosting, the pinned ref, and setup.sh's retirement are settled (owner
# decisions, 2026-09-06). One standing hazard remains — D3 below.
# decisions, 2026-09-06). D3 below is mitigated by image pinning (node-26#19).
#
# What it does, in order:
# 1. Preflight (root, arch, apt, SDR present)
@@ -48,15 +48,18 @@
# the mint panel / node-26#4). DEFAULT_REF below is `v1`. `--track-main`
# stays as an opt-in for the owner's own nodes.
#
# STANDING HAZARD
# MITIGATED HAZARD
#
# D3 SOURCE-OVERLAY. docker-compose.yml bind-mounts ./drb-edge-node/app and
# ./op25-container/app OVER the image's /app. So even in prebuilt mode
# the running Python is the CLONED REF's code against the pulled image's
# dependencies. `v1` == the commit CI built the current :latest/:stable
# from, so today they match — but the moment `v1` and the image tags
# diverge this silently mixes them. Fix: drop those two mounts from the
# prod compose path, or always retag images from the same ref as `v1`.
# ./op25-container/app OVER the image's /app, so the running Python is
# the CLONED REF's code against the pulled image's dependencies. Those
# used to be :latest/:stable, built from main — silently a different
# commit from `v1`. Since node-26#19 every image is pinned to
# src-<git tree hash of its own directory at the checked-out ref>
# (scripts/image-tags.sh, written into .env as DRB_TAG_*), which CI tags
# identically. The mounts now overlay the same code the image was built
# from. A pinned image that was never built fails the pull loudly; the
# "Release images" workflow builds any a release tag is missing.
# ---------------------------------------------------------------------------
set -euo pipefail
@@ -334,6 +337,14 @@ EOF
ok ".env written for '$NODE_ID'"
fi
# Pin every image to the source just checked out (node-26#19, D3). Written
# into .env — fresh or preserved — so later manual `docker compose` runs on
# this node use the same pins.
# shellcheck source=scripts/image-tags.sh
. "$INSTALL_DIR/scripts/image-tags.sh"
drb_pin_image_tags "$INSTALL_DIR/.env" "$INSTALL_DIR" "$RESOLVED_SHA" || die "could not write image pins to .env"
ok "images pinned to the source at ${RESOLVED_SHA:0:12} (DRB_TAG_* in .env)"
# ── 5. Enrollment ───────────────────────────────────────────────────────────
# Client half of Server/drb-c2-core/app/routers/enrollment.py. It does NOT
# exist in the edge-node app today (mqtt_manager.py:73-85 says so explicitly),
@@ -570,6 +581,11 @@ if [ "$DO_START" = 1 ]; then
requirement or a transient network error: re-run with DRB_REGISTRY_USER /
DRB_REGISTRY_PASS set, or with --build to compile on the Pi (~1h for op25)."
fi
# compose pull only WARNS for a missing image that also has a build:
# section, so check the pinned images really arrived.
drb_images_present || die "the images pinned to $REF (${RESOLVED_SHA:0:12}) are not in
$REGISTRY yet. CI may still be building them — wait and re-run; the owner can
run the 'Release images' workflow on that ref; or re-run with --build."
docker compose up --no-build -d
fi
# ALWAYS recreate edge-node (node-26#14): `up -d` leaves an already-running