Skip to content

docker: stamp the version into the image, and assert it on every build - #562

Merged
ohohoreilly merged 1 commit into
masterfrom
ohohoreilly/image-version-stamp
Sep 17, 2026
Merged

ohohoreilly merged 1 commit into
masterfrom
ohohoreilly/image-version-stamp

Conversation

@ohohoreilly

Copy link
Copy Markdown
Member

Found by the v0.1.0-rc1 candidate, which is exactly what it was cut for.

The symptom

$ docker run --rm --entrypoint orlyc ghcr.io/orlyatomics/orly:v0.1.0-rc1 --help
Build: unknown

A release candidate whose binary cannot say which release it is.

Why

make version derives the string from git describe --tags --match 'v[0-9]*', and .dockerignore excludes .git — deliberately; it is build context nothing in the image needs. So inside the image build there has never been anything to describe, and the Dockerfile said so:

make version degrades to "unknown" without git -- fine for an image whose tag carries the version.

That was a reasonable trade and it held while the image was only ever :latest. It stops holding at a release. The tag carries the version only for someone who pulled a tag; the README tells people to run :latest, and they have no way to ask the binary what they have.

The change

  • ARG ORLY_VERSION — when set, writes orly/build_version.h directly; when not, falls back to make version exactly as before, so a bare docker build . is unaffected.
  • The workflow resolves it: the tag on a release, dev-<short sha> otherwise. So every image can name itself, not only tagged ones — which is the case that actually bothered me, since :latest moves.
  • An assertion on every image build, pull requests included:
got=$(docker run --rm --entrypoint orlyc "$IMAGE" --help 2>&1 | sed -n 's/^Build: //p')
[ "$got" = "$ORLY_VERSION" ]

That placement is deliberate. The manifest check in this same workflow was gated to publishes only, so it never ran and failed a correct manifest the first time it did (#559). A check that only runs on the rare path is a check you find out about at the worst moment.

Verification

Built the image locally on native arm64 with the arg set, then ran the assertion exactly as the workflow spells it:

orlyc reports: v0.1.0-rc2-probe
ASSERTION PASSES

Both branches of the conditional were checked for selection, so the no-arg fallback still takes the make version path.

Sequence

v0.1.0-rc2 after this lands, to confirm the stamp survives a real tagged publish, then v0.1.0.

`orlyc --help` in the published image reported "Build: unknown", including in
the v0.1.0-rc1 release candidate. `make version` derives the string from
`git describe`, .dockerignore excludes .git, so inside the image build there
was never anything to describe.

That was a documented trade ("fine for an image whose tag carries the
version") and it held while the image was only ever :latest. It stops holding
at a release: someone running :latest -- which is what the README tells them
to run -- has no way to ask the binary what it is.

So pass the version in. New ORLY_VERSION build arg writes orly/build_version.h
directly when set and falls back to `make version` when it is not, so a bare
`docker build .` behaves exactly as before. The workflow resolves it to the
tag on a release and dev-<short sha> otherwise, which means every image can
name itself, not just tagged ones.

The assertion runs on every image build, pull requests included, rather than
only on a publish. That is the #559 lesson applied: the manifest check in this
same workflow was gated to publishes, never ran, and failed a correct manifest
the first time it did.

Verified by building the image locally on arm64 with the arg set: orlyc
reports the stamped string and the assertion as written passes.
@ohohoreilly ohohoreilly self-assigned this Sep 17, 2026
@ohohoreilly
ohohoreilly merged commit 275fd09 into master Sep 17, 2026
13 checks passed
@ohohoreilly
ohohoreilly deleted the ohohoreilly/image-version-stamp branch September 17, 2026 21:11
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.

1 participant