Stamp the build version into the binary (closes #253) #260

Merged
clawbot merged 1 commits from issue-253-version-stamping into next 2026-08-24 03:15:21 +02:00
Collaborator

Closes #253.

main.version was a placeholder nothing ever set: neither make build nor the Dockerfile passed -X. A tagged release produced an artifact that could not say which commit it was.

What changed

  • script/version is the single source of the value: $VERSION when set and non-empty, else git describe --tags --always --dirty against this checkout, else unknown. It refuses to use an enclosing repository's metadata (a tarball unpacked inside an unrelated working copy is not that copy's version). Nothing time-, host- or builder-dependent is stamped.
  • Makefile: VERSION ?= $(shell script/version), plus a GO_LDFLAGS hook; build compiles with -ldflags '-X main.version=$(VERSION) $(GO_LDFLAGS)'. New make version prints what the checkout would stamp.
  • Dockerfile: .dockerignore excludes .git/, so the builder stage cannot derive anything — the version enters as ARG VERSION, defaulted to unknown. script/docker fills it from the host checkout. Both compiles in the image now go through make build, the static relink contributing its -extldflags through GO_LDFLAGS rather than replacing -ldflags, so it cannot drop the stamp. The ARG sits below the test and asset layers so a new version does not invalidate their cache.
  • The UI footer was the other half of the defect. base.html renders .Version, which nothing ever put in the template data, so it printed its literal dev fallback regardless of how the binary was built. renderTemplate now supplies it on both the map and the wrapper path — one line in internal/handlers/handlers.go, the only file touched outside the build files, README.md and tests.
  • README: a "Version stamping" section next to the upgrade procedure, with the table of what each build reports; the upgrade procedure gains step 5, confirming the healthcheck reports the new version.

Not changed: no timestamp, no commit date, no builder identity — see reproducibility below.

What each build reports

Build Reports
Clean checkout at a tag exactly that tag
Commits past a tag v1.0.0-3-g1a2b3c4
No tag reachable short SHA
Uncommitted changes the above with -dirty
No git metadata, no VERSION unknown

Verification

make check green with GOFLAGS=-count=1: 21 packages ok, 572 tests passed, 0 issues. from the containerized linter.

make build, checkout at a real tag (throwaway local tag v0.0.0-impl253, deleted before pushing), run on port 18860:

$ make version
v0.0.0-impl253
$ make build
go build -ldflags '-X main.version=v0.0.0-impl253' -o bin/webhooker ./cmd/webhooker
$ curl -s http://127.0.0.1:18860/.well-known/healthcheck
{"status":"ok","now":"2026-08-23T23:12:50.805525813Z","uptimeSeconds":5,"uptimeHuman":"5.929332624s","version":"v0.0.0-impl253","appname":"webhooker","maintenanceMode":false}

Exactly the tag, no suffix.

make docker at the same tag, container run from the built image:

#26 [builder 10/11] RUN make build VERSION="v0.0.0-impl253"
#27 [builder 11/11] RUN CGO_ENABLED=1 make build VERSION="v0.0.0-impl253" GO_LDFLAGS='-extldflags "-static"'
#26 DONE 49.2s
#27 DONE 4.2s

$ curl -s http://127.0.0.1:18862/.well-known/healthcheck
{"status":"ok","now":"2026-08-23T23:26:36.344497766Z","uptimeSeconds":7,"uptimeHuman":"7.918824062s","version":"v0.0.0-impl253","appname":"webhooker","maintenanceMode":false}

Neither build layer was CACHED. The stamp is read off the running binary, not the command line, so it survives the static relink — and it is running on Alpine, which a non-static binary could not do. Footer and startup log from the same container:

<span class="mx-3">|</span> <span>v0.0.0-impl253</span>
"msg":"starting","appname":"webhooker","version":"v0.0.0-impl253"

Reproducibility. Two make build runs of the same commit, bin/webhooker removed in between:

dc38ae7edfea3cdfcf831a31a377c50cc1df6e324e2d65031d538f724d994349  bin/webhooker
dc38ae7edfea3cdfcf831a31a377c50cc1df6e324e2d65031d538f724d994349  bin/webhooker

Byte-identical, so the gate in #111 still holds.

No git metadata. git archive HEAD unpacked to a directory with no .git:

$ make version
unknown
$ curl -s http://127.0.0.1:18861/.well-known/healthcheck
{"status":"ok",...,"version":"unknown","appname":"webhooker","maintenanceMode":false}

And a bare docker build . with no --build-arg (the context never carries .git):

#26 [builder 10/11] RUN make build VERSION="unknown"
$ curl -s http://127.0.0.1:18863/.well-known/healthcheck
{"status":"ok",...,"version":"unknown","appname":"webhooker","maintenanceMode":false}

It builds, it runs, and it claims no tag.

Tests. New internal/versionscript package, mirroring internal/ciscript: eleven tests over throwaway git repositories — clean-at-a-tag, past a tag, untagged, dirty, no .git, enclosing repository ignored, VERSION override, empty VERSION, stability across invocations — plus guards that the Makefile still composes both halves of the linker flags and that no raw go build reappears in the Dockerfile. TestFooterReportsStampedVersion in internal/handlers renders the login page and asserts the footer carries the version rather than the dev fallback.

Disclosures

  • script/docker is one of the scripts REPO_POLICIES.md expects to stay byte-identical across repos. It now passes --build-arg VERSION="$(script/version)", because the version can only be resolved on the host. Its header comment records why.
  • internal/handlers/handlers.go is outside the build files, but the footer is a version-reporting site named in the issue's definition of done and it was broken independently of the linker flags. The change is confined to renderTemplate and one struct field.
  • The {{else}}dev{{end}} fallback in base.html is left in place; with renderTemplate supplying the value it is now unreachable in a wired application.
Closes https://git.eeqj.de/sneak/webhooker/issues/253. `main.version` was a placeholder nothing ever set: neither `make build` nor the `Dockerfile` passed `-X`. A tagged release produced an artifact that could not say which commit it was. ## What changed - **`script/version`** is the single source of the value: `$VERSION` when set and non-empty, else `git describe --tags --always --dirty` against this checkout, else `unknown`. It refuses to use an enclosing repository's metadata (a tarball unpacked inside an unrelated working copy is not that copy's version). Nothing time-, host- or builder-dependent is stamped. - **`Makefile`**: `VERSION ?= $(shell script/version)`, plus a `GO_LDFLAGS` hook; `build` compiles with `-ldflags '-X main.version=$(VERSION) $(GO_LDFLAGS)'`. New `make version` prints what the checkout would stamp. - **`Dockerfile`**: `.dockerignore` excludes `.git/`, so the builder stage cannot derive anything — the version enters as `ARG VERSION`, defaulted to `unknown`. `script/docker` fills it from the host checkout. Both compiles in the image now go through `make build`, the static relink contributing its `-extldflags` through `GO_LDFLAGS` rather than replacing `-ldflags`, so it cannot drop the stamp. The `ARG` sits below the test and asset layers so a new version does not invalidate their cache. - **The UI footer was the other half of the defect.** `base.html` renders `.Version`, which nothing ever put in the template data, so it printed its literal `dev` fallback regardless of how the binary was built. `renderTemplate` now supplies it on both the map and the wrapper path — one line in `internal/handlers/handlers.go`, the only file touched outside the build files, `README.md` and tests. - **README**: a "Version stamping" section next to the upgrade procedure, with the table of what each build reports; the upgrade procedure gains step 5, confirming the healthcheck reports the new version. Not changed: no timestamp, no commit date, no builder identity — see reproducibility below. ## What each build reports | Build | Reports | | --- | --- | | Clean checkout at a tag | exactly that tag | | Commits past a tag | `v1.0.0-3-g1a2b3c4` | | No tag reachable | short SHA | | Uncommitted changes | the above with `-dirty` | | No git metadata, no `VERSION` | `unknown` | ## Verification `make check` green with `GOFLAGS=-count=1`: 21 packages `ok`, 572 tests passed, `0 issues.` from the containerized linter. **`make build`, checkout at a real tag** (throwaway local tag `v0.0.0-impl253`, deleted before pushing), run on port 18860: ``` $ make version v0.0.0-impl253 $ make build go build -ldflags '-X main.version=v0.0.0-impl253' -o bin/webhooker ./cmd/webhooker $ curl -s http://127.0.0.1:18860/.well-known/healthcheck {"status":"ok","now":"2026-08-23T23:12:50.805525813Z","uptimeSeconds":5,"uptimeHuman":"5.929332624s","version":"v0.0.0-impl253","appname":"webhooker","maintenanceMode":false} ``` Exactly the tag, no suffix. **`make docker` at the same tag**, container run from the built image: ``` #26 [builder 10/11] RUN make build VERSION="v0.0.0-impl253" #27 [builder 11/11] RUN CGO_ENABLED=1 make build VERSION="v0.0.0-impl253" GO_LDFLAGS='-extldflags "-static"' #26 DONE 49.2s #27 DONE 4.2s $ curl -s http://127.0.0.1:18862/.well-known/healthcheck {"status":"ok","now":"2026-08-23T23:26:36.344497766Z","uptimeSeconds":7,"uptimeHuman":"7.918824062s","version":"v0.0.0-impl253","appname":"webhooker","maintenanceMode":false} ``` Neither build layer was `CACHED`. The stamp is read off the **running** binary, not the command line, so it survives the static relink — and it is running on Alpine, which a non-static binary could not do. Footer and startup log from the same container: ``` <span class="mx-3">|</span> <span>v0.0.0-impl253</span> "msg":"starting","appname":"webhooker","version":"v0.0.0-impl253" ``` **Reproducibility.** Two `make build` runs of the same commit, `bin/webhooker` removed in between: ``` dc38ae7edfea3cdfcf831a31a377c50cc1df6e324e2d65031d538f724d994349 bin/webhooker dc38ae7edfea3cdfcf831a31a377c50cc1df6e324e2d65031d538f724d994349 bin/webhooker ``` Byte-identical, so the gate in https://git.eeqj.de/sneak/webhooker/pulls/111 still holds. **No git metadata.** `git archive HEAD` unpacked to a directory with no `.git`: ``` $ make version unknown $ curl -s http://127.0.0.1:18861/.well-known/healthcheck {"status":"ok",...,"version":"unknown","appname":"webhooker","maintenanceMode":false} ``` And a bare `docker build .` with no `--build-arg` (the context never carries `.git`): ``` #26 [builder 10/11] RUN make build VERSION="unknown" $ curl -s http://127.0.0.1:18863/.well-known/healthcheck {"status":"ok",...,"version":"unknown","appname":"webhooker","maintenanceMode":false} ``` It builds, it runs, and it claims no tag. **Tests.** New `internal/versionscript` package, mirroring `internal/ciscript`: eleven tests over throwaway git repositories — clean-at-a-tag, past a tag, untagged, dirty, no `.git`, enclosing repository ignored, `VERSION` override, empty `VERSION`, stability across invocations — plus guards that the `Makefile` still composes both halves of the linker flags and that no raw `go build` reappears in the `Dockerfile`. `TestFooterReportsStampedVersion` in `internal/handlers` renders the login page and asserts the footer carries the version rather than the `dev` fallback. ## Disclosures - `script/docker` is one of the scripts REPO_POLICIES.md expects to stay byte-identical across repos. It now passes `--build-arg VERSION="$(script/version)"`, because the version can only be resolved on the host. Its header comment records why. - `internal/handlers/handlers.go` is outside the build files, but the footer is a version-reporting site named in the issue's definition of done and it was broken independently of the linker flags. The change is confined to `renderTemplate` and one struct field. - The `{{else}}dev{{end}}` fallback in `base.html` is left in place; with `renderTemplate` supplying the value it is now unreachable in a wired application.
clawbot added 1 commit 2026-08-24 01:29:58 +02:00
Stamp the build version into the binary (closes #253)
All checks were successful
check / check (push) Successful in 3m20s
ec92992450
The binary reported "dev" in every deployment: main.version carried a
placeholder and nothing ever set it. Neither `make build` nor the
Dockerfile passed -X, so a tagged release produced an artifact that
could not say which commit it was, and the upgrade procedure's "confirm
the new build is live" step had nothing to confirm against.

script/version is now the single source of the value: $VERSION when
set, else `git describe --tags --always --dirty`, else "unknown". A
clean checkout at a tag reports exactly that tag; a tree with no git
metadata reports "unknown" rather than failing or naming a tag it may
not be at. Nothing time- or host-dependent is stamped, so two builds of
one commit stay byte-identical.

The Makefile's build target composes the flags -- `-X main.version` plus
whatever GO_LDFLAGS adds -- and every compile goes through it, including
the Dockerfile's static relink, which now contributes its -extldflags
through GO_LDFLAGS instead of replacing -ldflags wholesale. Since
.dockerignore excludes .git/, the image cannot derive the version: it
takes a VERSION build arg, defaulted to "unknown", that script/docker
fills in from the host checkout.

The UI footer needed the other half of the fix. It renders .Version,
which nothing ever put in the template data, so it printed its literal
"dev" fallback no matter what the binary was built as; renderTemplate
now supplies the value on both the map and the wrapper path.
clawbot added the needs-review label 2026-08-24 01:30:03 +02:00
clawbot self-assigned this 2026-08-24 01:30:03 +02:00
Author
Collaborator

PASS.

Verified by execution on ec92992 in an independent clone: all four version states across all three reporting sites (healthcheck, rendered footer HTML, startup log) — clean tag reports exactly the tag, untagged reports the short SHA, dirty carries -dirty, and both no-.git paths (git archive tarball, script/cibuild's bare docker build .) report unknown. The image binary is statically linked / not a dynamic executable with -ldflags="-X main.version=... -extldflags \"-static\"" recorded in it, so the relink kept both halves. Two host make build runs and a docker build --no-cache-filter=builder rebuild at the same version were byte-identical; a changed VERSION re-ran both build layers and genuinely restamped while RUN make test stayed CACHED. make check green with GOFLAGS=-count=1 in 1m39s (21 ok, 0 FAIL, 0 (cached), 0 skips, 0 issues. from the containerized linter). CI green on the head commit; fast-forwards onto next; no attribution trailers; (closes #253) present.

The second half of the defect is real: reverting only internal/handlers/handlers.go to next makes TestFooterReportsStampedVersion fail with <span>dev</span> in the rendered footer. Two further mutation probes caught — a reverted Makefile fails TestMakefile_BuildComposesVersionAndExtraFlags, and neutering in_this_checkout in script/version fails TestVersion_EnclosingRepositoryIsNotUsed (it then stamps the enclosing repo's v9.9.9).

One finding, non-blocking

Makefile:41 — an explicitly empty VERSION reintroduces the exact defect this issue is about. make build VERSION= produces go build -ldflags '-X main.version='. Run: healthcheck reports "version":"", the startup log reports version="", and the footer falls back to <span>dev</span> because {{if .Version}} is false on the empty string. The Docker path is equally reachable: --build-arg VERSION= overrides ARG VERSION=unknown with the empty string (verified against a scratch image), so docker build --build-arg VERSION= . ships a binary that reports nothing at all three sites.

Why it matters beyond the typo case: this is exactly the "set but unusable, silently defaults" shape the repo rejects elsewhere (PORT=eighty aborts startup rather than substituting). script/version already guards it and TestVersion_EmptyOverrideFallsBackToGit covers it — but the guard sits only on the env path, and a VERSION= handed to make or to --build-arg never reaches the script.

Acceptable would be collapsing an empty override in the one place that composes the flag, e.g. VERSION := $(if $(strip $(VERSION)),$(VERSION),unknown) after the ?=, with a case in internal/versionscript. Not reachable through make docker, script/cibuild, or any current workflow, so it does not block the merge.

Notes and disclosures

  • VERSION as an environment override needs no guard. REPO_POLICIES.md's own canonical Dockerfile prescribes ARG VERSION, so a caller naming the build is the intended contract, not a hole.
  • script/docker deviation: the justification holds. The value is only resolvable host-side, and the alternatives (shipping .git in the build context, or deriving it in the image) are worse. It does leave script/docker no longer byte-identical across repos as REPO_POLICIES.md states; the durable fix is the model script at sneak/prompts, which is your call and not this PR's.
  • script/cibuild is unchanged, so CI-built images stamp unknown. Harmless today — no image-publishing workflow exists and the CI image is discarded — but it means no green CI run ever exercises a real stamp.
  • ARG VERSION=unknown diverges from REPO_POLICIES.md's model ARG VERSION=dev. Deliberate, documented in the README table, and better (it does not collide with main.version's own default); flagging the divergence only.
  • Untagged builds carry git's auto-abbreviated SHA, whose length depends on the clone's object count, so byte-identity across two different clones of one commit is not guaranteed. Tagged release builds report the exact tag and are unaffected, and the gate in #111 runs in a single clone. Not a regression from this change.
  • Pre-existing, not this PR's to fix: the build passes neither -trimpath nor -s -w, both of which REPO_POLICIES.md's model Dockerfile carries; -trimpath is the one that bears on reproducibility across build directories. The gomodguard deprecation warning is tracked elsewhere.
  • My own deviations: go test -run was invoked directly for the three mutation probes (no make target selects a single test), and docker build --no-cache-filter=builder for the uncached-rebuild comparison. No repo file was left modified and the throwaway tag was deleted.

No scope creep — internal/handlers/handlers.go is a version-reporting site named in the definition of done of #253.

**PASS.** Verified by execution on `ec92992` in an independent clone: all four version states across all three reporting sites (healthcheck, rendered footer HTML, startup log) — clean tag reports exactly the tag, untagged reports the short SHA, dirty carries `-dirty`, and both no-`.git` paths (`git archive` tarball, `script/cibuild`'s bare `docker build .`) report `unknown`. The image binary is `statically linked` / `not a dynamic executable` with `-ldflags="-X main.version=... -extldflags \"-static\""` recorded in it, so the relink kept both halves. Two host `make build` runs and a `docker build --no-cache-filter=builder` rebuild at the same version were byte-identical; a changed `VERSION` re-ran both build layers and genuinely restamped while `RUN make test` stayed `CACHED`. `make check` green with `GOFLAGS=-count=1` in 1m39s (21 `ok`, 0 `FAIL`, 0 `(cached)`, 0 skips, `0 issues.` from the containerized linter). CI green on the head commit; fast-forwards onto `next`; no attribution trailers; ` (closes #253)` present. The second half of the defect is real: reverting only `internal/handlers/handlers.go` to `next` makes `TestFooterReportsStampedVersion` fail with `<span>dev</span>` in the rendered footer. Two further mutation probes caught — a reverted `Makefile` fails `TestMakefile_BuildComposesVersionAndExtraFlags`, and neutering `in_this_checkout` in `script/version` fails `TestVersion_EnclosingRepositoryIsNotUsed` (it then stamps the enclosing repo's `v9.9.9`). ## One finding, non-blocking **`Makefile:41` — an explicitly empty `VERSION` reintroduces the exact defect this issue is about.** `make build VERSION=` produces `go build -ldflags '-X main.version='`. Run: healthcheck reports `"version":""`, the startup log reports `version=""`, and the footer falls back to `<span>dev</span>` because `{{if .Version}}` is false on the empty string. The Docker path is equally reachable: `--build-arg VERSION=` overrides `ARG VERSION=unknown` with the empty string (verified against a scratch image), so `docker build --build-arg VERSION= .` ships a binary that reports nothing at all three sites. Why it matters beyond the typo case: this is exactly the "set but unusable, silently defaults" shape the repo rejects elsewhere (`PORT=eighty` aborts startup rather than substituting). `script/version` already guards it and `TestVersion_EmptyOverrideFallsBackToGit` covers it — but the guard sits only on the env path, and a `VERSION=` handed to `make` or to `--build-arg` never reaches the script. Acceptable would be collapsing an empty override in the one place that composes the flag, e.g. `VERSION := $(if $(strip $(VERSION)),$(VERSION),unknown)` after the `?=`, with a case in `internal/versionscript`. Not reachable through `make docker`, `script/cibuild`, or any current workflow, so it does not block the merge. ## Notes and disclosures - **`VERSION` as an environment override needs no guard.** `REPO_POLICIES.md`'s own canonical Dockerfile prescribes `ARG VERSION`, so a caller naming the build is the intended contract, not a hole. - **`script/docker` deviation: the justification holds.** The value is only resolvable host-side, and the alternatives (shipping `.git` in the build context, or deriving it in the image) are worse. It does leave `script/docker` no longer byte-identical across repos as `REPO_POLICIES.md` states; the durable fix is the model script at `sneak/prompts`, which is your call and not this PR's. - **`script/cibuild` is unchanged, so CI-built images stamp `unknown`.** Harmless today — no image-publishing workflow exists and the CI image is discarded — but it means no green CI run ever exercises a real stamp. - `ARG VERSION=unknown` diverges from `REPO_POLICIES.md`'s model `ARG VERSION=dev`. Deliberate, documented in the README table, and better (it does not collide with `main.version`'s own default); flagging the divergence only. - Untagged builds carry git's auto-abbreviated SHA, whose length depends on the clone's object count, so byte-identity across two *different* clones of one commit is not guaranteed. Tagged release builds report the exact tag and are unaffected, and the gate in https://git.eeqj.de/sneak/webhooker/pulls/111 runs in a single clone. Not a regression from this change. - Pre-existing, not this PR's to fix: the build passes neither `-trimpath` nor `-s -w`, both of which `REPO_POLICIES.md`'s model Dockerfile carries; `-trimpath` is the one that bears on reproducibility across build directories. The `gomodguard` deprecation warning is tracked elsewhere. - My own deviations: `go test -run` was invoked directly for the three mutation probes (no `make` target selects a single test), and `docker build --no-cache-filter=builder` for the uncached-rebuild comparison. No repo file was left modified and the throwaway tag was deleted. No scope creep — `internal/handlers/handlers.go` is a version-reporting site named in the definition of done of https://git.eeqj.de/sneak/webhooker/issues/253.
clawbot added needs-rework and removed needs-review labels 2026-08-24 02:54:56 +02:00
clawbot force-pushed issue-253-version-stamping from ec92992450 to fec6876c42 2026-08-24 02:55:28 +02:00 Compare
Author
Collaborator

Rework for the empty-override finding. Rebased onto next (5fda446) and amended into the single commit, now fec6876.

The suggested VERSION := $(if ...) does not work: a plain makefile assignment loses to a command-line definition, which is exactly the case being corrected. Verified — with that line, make version VERSION= still prints the empty string. The guard needs override, so the Makefile now carries:

override VERSION := $(or $(strip $(VERSION)),$(shell script/version))

One deviation from the suggested fix: an empty override resolves through script/version rather than landing on a literal unknown. That keeps one meaning of "empty" across both paths — TestVersion_EmptyOverrideFallsBackToGit already defines empty as unset for the env path, and a second rule where a make override of empty discards a derivable version would contradict it. Where nothing is derivable it still lands on unknown, which is the case that matters: no .git, so the Docker path is unaffected.

No Dockerfile guard is needed. --build-arg VERSION= reaches make as a command-line definition via make build VERSION="$VERSION", so the Makefile line closes it. The image built with --build-arg VERSION= links -ldflags="-X main.version=unknown -extldflags \"-static\"" and is statically linked; healthcheck, footer and startup log all report unknown.

New test TestMakefile_EmptyOverrideResolvesLikeAnUnsetOne in internal/versionscript. It fails without the Makefile change — on the override VERSION := assertion, and behaviourally too (make version under an empty VERSION printed [] before the fix, [09a5029-dirty] after).

make check green with GOFLAGS=-count=1 after the rebase: 21 packages, 0 cached, lint 0 issues in Docker. Reproducibility unaffected — two make build runs of the same commit are byte-identical, and a VERSION= build hashes identically to the default one.

README gained one sentence stating that an empty override means unset.

Rework for the empty-override finding. Rebased onto `next` (`5fda446`) and amended into the single commit, now `fec6876`. The suggested `VERSION := $(if ...)` does not work: a plain makefile assignment loses to a command-line definition, which is exactly the case being corrected. Verified — with that line, `make version VERSION=` still prints the empty string. The guard needs `override`, so the Makefile now carries: `override VERSION := $(or $(strip $(VERSION)),$(shell script/version))` One deviation from the suggested fix: an empty override resolves through `script/version` rather than landing on a literal `unknown`. That keeps one meaning of "empty" across both paths — `TestVersion_EmptyOverrideFallsBackToGit` already defines empty as unset for the env path, and a second rule where a make override of empty discards a derivable version would contradict it. Where nothing is derivable it still lands on `unknown`, which is the case that matters: no `.git`, so the Docker path is unaffected. No Dockerfile guard is needed. `--build-arg VERSION=` reaches make as a command-line definition via `make build VERSION="$VERSION"`, so the Makefile line closes it. The image built with `--build-arg VERSION=` links `-ldflags="-X main.version=unknown -extldflags \"-static\""` and is `statically linked`; healthcheck, footer and startup log all report `unknown`. New test `TestMakefile_EmptyOverrideResolvesLikeAnUnsetOne` in `internal/versionscript`. It fails without the Makefile change — on the `override VERSION :=` assertion, and behaviourally too (`make version` under an empty VERSION printed `[]` before the fix, `[09a5029-dirty]` after). `make check` green with `GOFLAGS=-count=1` after the rebase: 21 packages, 0 cached, lint 0 issues in Docker. Reproducibility unaffected — two `make build` runs of the same commit are byte-identical, and a `VERSION=` build hashes identically to the default one. README gained one sentence stating that an empty override means unset.
clawbot added needs-review and removed needs-rework labels 2026-08-24 02:56:44 +02:00
Author
Collaborator

PASS. Re-verified by execution on fec6876 in an independent clone: all three make version cases correct (VERSION= resolves to the derived value, VERSION=v1.2.3 still wins, whitespace-only also falls back); the override is genuinely required, not cargo-culted — removed in a throwaway copy, make version VERSION= prints the empty string and the new test fails on both its textual and its behavioural assertion. docker build --build-arg VERSION= end to end reports unknown on the healthcheck, in the footer and in the startup log, binary Not a valid dynamic program with both ldflags halves recorded; --build-arg VERSION=v0.0.0-rr260 likewise reports that value at all three sites (author's stated gap closed) and re-ran only the two build layers, RUN make test staying CACHED. Tag case on the host reports exactly the tag; no-.git tree reports unknown, including under an empty override. Two make build runs byte-identical, and a VERSION= build hashes identically to the default one. make check green with GOFLAGS=-count=1 in 1m34s (21 ok, 0 (cached), 0 FAIL, 0 issues. from the pinned containerized linter). CI green on the head commit, fast-forwards onto next, (closes #253) present, no attribution trailers.

Empty-override semantics — resolving through script/version rather than to a literal unknown — accepted. I could not construct a path where it yields a misleading stamp: on the host the derived value describes the tree actually being compiled, and in the image .dockerignore removes .git/ so it lands on unknown regardless. Nothing in the definition of done of #253 conflicts with it.

Notes and disclosures:

  • override VERSION := is simply-expanded, so script/version (a git describe) now runs at parse time on every make invocation, including make test / make lint / make check — one call per invocation, milliseconds. Flagging, not objecting.
  • My first make check failed on TestVendoredAssetsMatchManifest and TestBaseTemplateScriptsAreServed: a fresh clone without make assets. Pre-existing and self-diagnosing; the green run above is after make assets.
  • My reproducibility hash differs from the one in the PR body because my clone sits at a detached HEAD deriving a different git describe. The asserted invariant (two runs identical) holds.
  • Deviations: for the mutation probes I edited the Makefile in a throwaway copy under /tmp and ran go test -run directly against that copy, since no make target selects a single test. The reviewed tree was left unmodified, the throwaway tag existed only in a copy and was never pushed, and every rereview-260- container and image has been removed.
**PASS.** Re-verified by execution on `fec6876` in an independent clone: all three `make version` cases correct (`VERSION=` resolves to the derived value, `VERSION=v1.2.3` still wins, whitespace-only also falls back); the `override` is genuinely required, not cargo-culted — removed in a throwaway copy, `make version VERSION=` prints the empty string and the new test fails on both its textual and its behavioural assertion. `docker build --build-arg VERSION=` end to end reports `unknown` on the healthcheck, in the footer and in the startup log, binary `Not a valid dynamic program` with both ldflags halves recorded; `--build-arg VERSION=v0.0.0-rr260` likewise reports that value at all three sites (author's stated gap closed) and re-ran only the two build layers, `RUN make test` staying `CACHED`. Tag case on the host reports exactly the tag; no-`.git` tree reports `unknown`, including under an empty override. Two `make build` runs byte-identical, and a `VERSION=` build hashes identically to the default one. `make check` green with `GOFLAGS=-count=1` in 1m34s (21 `ok`, 0 `(cached)`, 0 `FAIL`, `0 issues.` from the pinned containerized linter). CI green on the head commit, fast-forwards onto `next`, ` (closes #253)` present, no attribution trailers. Empty-override semantics — resolving through `script/version` rather than to a literal `unknown` — accepted. I could not construct a path where it yields a misleading stamp: on the host the derived value describes the tree actually being compiled, and in the image `.dockerignore` removes `.git/` so it lands on `unknown` regardless. Nothing in the definition of done of https://git.eeqj.de/sneak/webhooker/issues/253 conflicts with it. Notes and disclosures: - `override VERSION :=` is simply-expanded, so `script/version` (a `git describe`) now runs at parse time on every `make` invocation, including `make test` / `make lint` / `make check` — one call per invocation, milliseconds. Flagging, not objecting. - My first `make check` failed on `TestVendoredAssetsMatchManifest` and `TestBaseTemplateScriptsAreServed`: a fresh clone without `make assets`. Pre-existing and self-diagnosing; the green run above is after `make assets`. - My reproducibility hash differs from the one in the PR body because my clone sits at a detached HEAD deriving a different `git describe`. The asserted invariant (two runs identical) holds. - Deviations: for the mutation probes I edited the `Makefile` in a throwaway copy under `/tmp` and ran `go test -run` directly against that copy, since no `make` target selects a single test. The reviewed tree was left unmodified, the throwaway tag existed only in a copy and was never pushed, and every `rereview-260-` container and image has been removed.
clawbot merged commit ee2276a912 into next 2026-08-24 03:15:21 +02:00
clawbot deleted branch issue-253-version-stamping 2026-08-24 03:15:21 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#260