Skip to content

fix(smoke): pin the eject smoke's psql image to the digest compose carries - #269

Merged
jouwdan merged 1 commit into
mainfrom
claude/mei-102-pin-eject-smoke-psql-image
Aug 27, 2026
Merged

fix(smoke): pin the eject smoke's psql image to the digest compose carries#269
jouwdan merged 1 commit into
mainfrom
claude/mei-102-pin-eject-smoke-psql-image

Conversation

@jouwdan

@jouwdan jouwdan commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Closes the last open item of MEI-102. Items 1–4 of that ticket were already fixed on main; this is item 5, the undigested postgres image in the eject smoke.

The problem

scripts/board-eject-smoke.mts shelled out to a bare postgres:18-alpine for its psql client, while every base image under docker/ is pinned by digest.

Why it isn't just "add the digest"

The ticket justified the fix as consistency — "every other postgres reference in the repo is digest-pinned". That premise no longer holds, so I checked what the rule actually is before patching:

  • docs/contributing/release.md scopes the policy to "the Dockerfiles and compose files", and closes with the reason it is affordable: "Dependabot moves all of these pins … so the pinning costs review, not staleness."
  • .github/dependabot.yml scopes its docker and docker-compose ecosystems to /docker. Its github-actions ecosystem updates action refs, not a workflow's services.image.

So a digest hand-written in scripts/ or in ci.yml is a digest nothing ever moves — it buys determinism today and pays staleness forever, which is the trade the policy exists to avoid.

What this does instead

The smoke reads the pinned reference rather than repeating it:

const PSQL_IMAGE = await pinnedComposeImage('postgres')

scripts/compose-images.mts pulls the digest-pinned image out of docker/compose.yml's postgres service. The smoke now runs a digest and follows Dependabot's existing weekly compose bump, with no second copy to drift. This mirrors the same file's existing codeVersion() helper, which already reads CODE_VERSION out of a source file rather than hardcoding it.

The five bare postgres:18-alpine references in .github/workflows/ci.yml are left alone deliberately — they are services: containers created empty for one job and discarded with it, and no ecosystem reads a workflow's services: block. docs/contributing/release.md now says so, so the next audit reads them as a decision rather than re-filing this:

A bare tag in a services: block is a decision, not an oversight; a bare tag in a Dockerfile, a compose file, or a script that reads one is the bug.

Changes

File Change
scripts/compose-images.mts New. pinnedComposeImage(service) — reads the digest-pinned image from docker/compose.yml, refusing an unknown service or an unpinned one.
scripts/compose-images.test.ts New. Asserts the shape, that the returned string is byte-identical to what compose carries, and the unknown-service refusal.
scripts/board-eject-smoke.mts Uses pinnedComposeImage('postgres') for the psql client.
docs/contributing/release.md States where a pin belongs and why, and that CI services: containers stay on a bare tag on purpose.

Validation

  • pnpm verify passes on this branch (exit 0) — 467 test files, 8187 tests, plus lint, guards, all three typechecks and every generated-doc check.
  • The three new compose-images tests pass.
  • pnpm docs:links:check passes, so the new release.md prose resolves.
  • Formatted only the files touched, per AGENTS.md.

Not run here: pnpm board:eject:smoke itself needs Docker and a reachable Postgres, neither available in this container. The changed line is the image argument to the existing docker run, and the test pins the value it now receives.


Generated by Claude Code

…rries

board-eject-smoke.mts shelled out to a bare `postgres:18-alpine` for its
psql client while every image in docker/ is pinned by digest. Rather than
hand-write a second digest that nothing would move — Dependabot's docker
and docker-compose ecosystems are scoped to docker/ — the smoke now reads
the pinned reference out of docker/compose.yml's postgres service, so it
runs a digest and still follows the weekly bump.

release.md now states the scope the pins actually have, so the bare tag on
a GitHub Actions `services:` container reads as the decision it is rather
than the oversight it looked like.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GVdrZfcwVhvJpZWUKWxYs9
@jouwdan
jouwdan merged commit 062add4 into main Aug 27, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants