fix(docker): detect and refresh stale build-artefact volumes

codeman-node-modules and codeman-dist (docker-compose.yaml) are seeded
from the image only while empty, so a rebuilt image's fresh dist/
node_modules sat unused behind old volume content until something
cleared it. The in-app self-updater never hit this (it rebuilds INSIDE
the running container, into the very volume already in use), but a
`docker compose build` triggered from outside it — Start-Codeman.sh,
after a manual `git pull` — did: the container came back up looking
unchanged, serving stale compiled routes against current source.

Start-Codeman.sh now compares the checkout's HEAD commit and
package-lock.json hash against a recorded marker
(docker-build-source.json) and clears just the affected volume(s)
before its own --build when either moved.

The in-place self-update path writes that same marker after a
successful build, so the two mechanisms agree on what the volumes
currently reflect — without it, the next plain Start-Codeman.sh run
would see the HEAD self-update just checked out, not recognise it as
already accounted for, and wipe the volumes self-update just correctly
rebuilt right back to the older baked image.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R9ZSTEenc8soSu9bTi8Xru
This commit is contained in:
Devvyn
2026-09-13 17:41:31 +08:00
co-authored by Claude Sonnet 5
parent 89e2cb5814
commit 8fe3f34fc5
3 changed files with 104 additions and 0 deletions
+23
View File
@@ -193,6 +193,29 @@ run_step "installing" "Installing dependencies" npm install --no-fund --no-audit
# 5) Build (gate the restart on success — never restart into a torn dist/).
run_step "building" "Building" npm run build || rollback_and_fail "Build failed"
# Docker Compose only: record what HEAD/package-lock.json the freshly-built
# codeman-dist/codeman-node-modules volumes now reflect. `Start-Codeman.sh`
# reads this same file (`$appdata_path/.codeman/…`, i.e. this container's own
# $HOME/.codeman since that path IS the appdata bind mount) to detect source
# changes an EXTERNAL `docker compose build` made and refresh those volumes —
# without this, the next plain `Start-Codeman.sh` run would see the HEAD this
# update just checked out, not recognise it as already accounted for, and wipe
# the volumes this update just correctly rebuilt right back to the OLDER image.
if [[ "$SUPERVISOR" == "docker-compose" ]]; then
build_source_file="$HOME/.codeman/docker-build-source.json"
mkdir -p -- "$HOME/.codeman"
build_head=$(git rev-parse HEAD 2>/dev/null || true)
build_lockfile_sha=''
if command -v sha256sum >/dev/null 2>&1; then
build_lockfile_sha=$(sha256sum -- package-lock.json 2>/dev/null | cut -d' ' -f1)
elif command -v shasum >/dev/null 2>&1; then
build_lockfile_sha=$(shasum -a 256 package-lock.json 2>/dev/null | cut -d' ' -f1)
fi
printf '{\n "headCommit": "%s",\n "lockfileSha256": "%s"\n}\n' \
"$build_head" "$build_lockfile_sha" >"$build_source_file.tmp" \
&& mv -- "$build_source_file.tmp" "$build_source_file"
fi
# 6) Restart the service so the new code loads. Write the terminal pre-restart
# marker FIRST so the freshly-booted server can reconcile it deterministically.
write_status "restarting" "Restarting Codeman…"