refuse to build an image stamped with a version that is not HEAD's tag - #7
Conversation
There was a problem hiding this comment.
Step 4c-sel: CLASSIFY
Changed files:CHANGELOG.md, Makefile.docker, Dockerfile
Candidate rules (judgment, triggered):
| Rule | Trigger | Applies? | Decision |
|---|---|---|---|
changelog/conventional-prefix-required | CHANGELOG.md | ✓ | Applicable — diff adds a bullet under ## Unreleased |
go-build-args/three-args-required | Makefile.docker, Dockerfile | ✓ | Applicable — diff changes Makefile.docker and Dockerfile has ARG BUILD_GIT_COMMIT/BUILD_DATE but no BUILD_GIT_VERSION |
Classify result:
{
"applicable": ["changelog/conventional-prefix-required", "go-build-args/three-args-required"],
"skipped": {}
}Step 4d-sel: ADJUDICATE
Rule: changelog/conventional-prefix-required
The new bullet is:
- fix: `make build` refuses to stamp a version onto a tree that is not that version's tag
fix: is in the allowed prefix set. No violation.
Rule: go-build-args/three-args-required
The rule requires BUILD_GIT_VERSION (git describe --tags --always --dirty), BUILD_GIT_COMMIT (git rev-parse --short HEAD), BUILD_DATE (date -u +%Y-%m-%dT%H:%M:%SZ) — all three.
Dockerfile declares:
ARG BUILD_GIT_COMMIT=none✓ARG BUILD_DATE=unknown✓BUILD_GIT_VERSIONis absent entirely ✗
Makefile.docker build target passes zero --build-arg values — neither BUILD_GIT_COMMIT nor BUILD_DATE is forwarded, let alone BUILD_GIT_VERSION. So even if Dockerfile had it, nothing would populate it.
This is a MUST violation: an image built from this Dockerfile will have BUILD_GIT_COMMIT=none and BUILD_DATE=unknown baked in, with no version description at all.
Step 5: Consolidated Report
Must Fix (Critical)
Makefile.docker:7—BUILD_GIT_VERSIONis not declared as anARGinDockerfile, and thedocker buildcommand inMakefile.dockerpasses no--build-argvalues at all. The three required build args areBUILD_GIT_VERSION,BUILD_GIT_COMMIT, andBUILD_DATEpergo-build-args/three-args-required. The image will ship withBUILD_GIT_COMMIT=noneandBUILD_DATE=unknown. (rule:go-build-args/three-args-required)
Should Fix (Important)
None.
Nice to Have (Optional)
None.
Traceability (Selector Mode):
- Candidates: 2 judgment rules matched by Step 4b-i glob filter
- Applicable: 2 rules selected by Step 4c-sel
- Skipped: none
Step 7: Manual Review
The check-version-tag logic itself is correct:
git describe --tags --exact-match HEADreturns non-zero exit + empty when HEAD is not on a tag, sohead_tagbecomes empty and"" != "$(VERSION)"fails correctly- The error message is informative and the escape hatch
ALLOW_UNTAGGED_BUILD=1is clearly documented - The CHANGELOG entry uses the correct
fix:conventional prefix
The critical gap is the missing BUILD_GIT_VERSION in Dockerfile and the absent --build-arg forwarding in Makefile.docker.
Step 6: Next Steps
Add BUILD_GIT_VERSION to Dockerfile and forward all three --build-arg values in the docker build command of Makefile.docker.
{
"verdict": "request-changes",
"summary": "The version-tag guard logic is correct, but the Dockerfile is missing BUILD_GIT_VERSION and the Makefile.docker build command forwards no --build-arg values, so shipped images have no version info baked in.",
"comments": [
{
"file": "Dockerfile",
"line": 1,
"severity": "critical",
"message": "BUILD_GIT_VERSION is not declared as an ARG. The three required build args per go-build-args/three-args-required are BUILD_GIT_VERSION, BUILD_GIT_COMMIT, and BUILD_DATE — this Dockerfile only declares the latter two."
},
{
"file": "Makefile.docker",
"line": 7,
"severity": "critical",
"message": "docker build command passes no --build-arg values. BUILD_GIT_COMMIT and BUILD_DATE are declared in Dockerfile but never populated; BUILD_GIT_VERSION is absent entirely. The image will ship with BUILD_GIT_COMMIT=none and BUILD_DATE=unknown."
}
],
"concerns_addressed": [
"correctness: check-version-tag uses git describe --exact-match HEAD — verified correct behavior when HEAD is not on a tag (exit 1, empty string, comparison fails)",
"correctness: error message handles untagged HEAD case clearly — verified, echo includes 'HEAD is at tag: <untagged>'",
"correctness: ALLOW_UNTAGGED_BUILD=1 bypass is documented — verified, echo message confirms it",
"correctness: VERSION default is newest tag regardless of checkout — the guard now prevents the wrong tag scenario, so this is mitigated",
"tests: no tests for check-version-tag guard — not addressed; tests remain absent"
]
}bborbe
commented
Aug 19, 2026
Fixed the real part, correcting two inaccuracies in the review. Fixed: Two corrections:
|
superseded by new automated review
There was a problem hiding this comment.
Now I have all I need for classification and adjudication.
Step 4c-sel CLASSIFY:
| Rule | Decision | Reason |
|---|---|---|
changelog/conventional-prefix-required | applicable | CHANGELOG.md changed; ## Unreleased present |
changelog/unreleased-entry-required | applicable | CHANGELOG.md changed; ## Unreleased has bullets |
go-build-args/three-args-required | applicable | Dockerfile + Makefile.docker changed |
Step 4d-sel ADJUDICATE:
changelog/conventional-prefix-required — bullets in ## Unreleased:
fix: \make build` refuses to stamp...→fix:` prefix ✓fix: stamp \BUILD_GIT_VERSION`...→fix:` prefix ✓
changelog/unreleased-entry-required — ## Unreleased section is present with bullets ✓ (script-enforced; mechanical funnel ran it with 0 findings)
go-build-args/three-args-required — Verifying:
- Dockerfile:
ARG BUILD_GIT_VERSION=unknownadded in both build stages ✓;ENV BUILD_GIT_VERSION=${BUILD_GIT_VERSION}added ✓ - Makefile.docker:
--build-arg BUILD_GIT_VERSION=$$(git describe --tags --always --dirty)added ✓ - main.go: not in this diff — ldflags wiring is a separate concern not touched by this PR
All three rules pass. No violations.
Selector mode traceability:
- Candidates: 3 rules
- Applicable: 3 rules
- Skipped: none
No cleanup needed (ALREADY_AT_HEAD, no worktree created). Proceeding to Step 5 report.
PR Review: refuse to build an image stamped with a version that is not HEAD's tag
Must Fix (Critical) | Should Fix (Important) | Nice to Have (Optional)
None.
Selector Mode Traceability
- Candidates: 3 rules matched by Step 4b-i glob filter
- Applicable: 3 rules selected by Step 4c-sel
- Skipped: none
Step 6: Next Steps Recommendation
No test coverage gaps flagged. The check-version-tag logic in Makefile.docker is a guard that would benefit from a shell test verifying the exit-code paths (tag mismatch exits 1, ALLOW_UNTAGGED_BUILD=1 skips check, clean tag passes). Consider adding a make check-version-tag-test target that exercises these three paths.
Step 7: Manual Review
Reviewed the three changed files:
Makefile.docker — The check-version-tag target correctly uses git describe --tags --exact-match HEAD to verify the current commit is exactly the tag being built. Exit code handling is proper (2>/dev/null suppresses "not a tag" error, then the empty-string check detects untagged HEAD). The ALLOW_UNTAGGED_BUILD=1 escape hatch is appropriately scoped — it only skips the guard, not the build itself, so scratch builds can still proceed. One observation: if VERSION is unset or empty at the time check-version-tag runs, the comparison $$head_tag != "$(VERSION)" will always be true (non-empty string vs empty), causing a spurious error. The build target already has a guard for empty VERSION; the check-version-tag target runs before that check, so there is a narrow window where an unset VERSION produces a confusing error message ("HEAD is at tag: " rather than "VERSION is not set"). This is minor.
Dockerfile — BUILD_GIT_VERSION added to both build stages with correct ARG/ENV declarations. OCI LABEL declarations are not present (the guide recommends them); this is optional per the guide's rollout checklist.
CHANGELOG.md — ## Unreleased section correctly structured with two fix: bullets. Conventional prefixes are correct.
{
"verdict": "approve",
"summary": "PR correctly adds a version-tag guard to prevent stamping an image with a version that doesn't match HEAD's exact tag, and stamps BUILD_GIT_VERSION into the image for runtime self-identification. All three changed files pass applicable rule checks. No violations found.",
"comments": [],
"concerns_addressed": [
"correctness: check-version-tag target validates git describe --tags --exact-match HEAD matches VERSION before build — exits non-zero on mismatch",
"correctness: ALLOW_UNTAGGED_BUILD=1 escape hatch bypasses version check but does not bypass the build itself — scratch builds still proceed",
"correctness: git command exit codes properly handled — 2>/dev/null suppresses 'not a tag' error, empty-string check detects untagged HEAD"
]
}Uh oh!
There was an error while loading. Please reload this page.
VERSIONinMakefile.dockerdefaults togit describe --tags $(git rev-list --tags --max-count=1)— the newest tag in the repo, regardless of what is checked out. An operator-runmake bucafrom master therefore stamps the newest tag's number onto whatever tree happens to be present.That shipped a bad v0.5.0 image. The v0.5.0 tag contains
AutoMergeLabel(main.go) andtryAutoMerge(pkg/watcher.go), but the published image was built from a pre-merge tree, so the deployed prod watcher has no auto-merge code at all — its startup argument dump printsOverrideLabel 'override-review'and noAutoMergeLabelline.Nothing surfaced it: the tag, the changelog and the image name all agreed, and the arming path fails silently (the label check logs nothing when it misses). It took an end-to-end smoke test plus a config-dump diff to find.
buildnow depends oncheck-version-tag, which fails whengit describe --tags --exact-match HEAD!=VERSION. Escape hatchALLOW_UNTAGGED_BUILD=1for scratch builds. Ported from the identical fix in bborbe/github-update-go-agent v0.9.2, which hit this same drift twice today.