Skip to content

fix: Report a valid version string with unreleased builds - #278

Open
drewr wants to merge 3 commits into
mainfrom
fix/nix-flake-version
Open

fix: Report a valid version string with unreleased builds#278
drewr wants to merge 3 commits into
mainfrom
fix/nix-flake-version

Conversation

@drewr

@drewr drewr commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

I mostly use datumctl from nix, built from the main branch. That means the version looks like:

% datumctl version
Client Version: v0.0.0-master+$Format:%H$
Kustomize Version: v5.7.1
Server Version: v1.35.0-milo.0.0.0-main-20260827-152801+7f292ebbe69c742d14842af7e3e6a9130226625b
error: client version error: could not parse pre-release/metadata (-master+$Format:%H$) in version "v0.0.0-master+$Format:%H$"

This fixes it to look like:

Client Version: v0.18.2+931f022-dev
[...]

Background

This happens because k8s.io/component-base/version (which backs the version command) was never given a real version via ldflags, so it kept its compiled-in placeholder. The flake's existing git describe fallback for the version was also silently broken: nix flakes always strip .git from the copied source, so it produced dev instead of a real value.

This PR embeds a real, always-valid version in nix builds: the commit sha comes from self.dirtyRev/self.rev (populated by nix's own git metadata, no .git needed), and the last tag comes from an optional untracked VERSION file that task nix-build generates and stages before building (nix flakes only see files git knows about). The result (<tag>+<sha>[-dev]) feeds the k8s.io/component-base/version ldflags, matching what .goreleaser.yaml already does for tagged releases.

The same placeholder shows up building outside nix and outside a release too — a plain go build/go run during local dev. Since ldflags aren't involved there, this adds internal/version.ApplyFallback, called at startup: it detects the placeholder and derives a valid v0.0.0+<sha>[-dev] version from the VCS info Go's toolchain already stamps into the binary automatically. It's a no-op whenever ldflags already provided a real version, so nix and release builds are unaffected. Also drops the flake's -X main.version=... ldflag, which targeted a variable that doesn't exist in package main.

ApplyFallback's runtime patch can never surface a real release tag, though — component-base's SetDynamicVersion rejects any version whose major/minor/patch don't match the compiled-in placeholder's (0.0.0), and Go's VCS stamping has no notion of the nearest tag anyway. So a plain go build always showed v0.0.0 even when a real tag was available. task build closes that gap: it embeds the real <last-tag>+<sha>[-dev] version via ldflags at compile time (same mechanism nix/goreleaser use), so local development builds get a proper version instead of the v0.0.0 fallback.

Test plan

  • task nix-build succeeds and embeds git describe --tags's value plus the commit sha
  • task build succeeds and embeds the real last tag plus the commit sha
  • A plain go build/go run (no ldflags) produces a valid v0.0.0+<sha>[-dev] fallback instead of the placeholder
  • datumctl version --client prints a valid version with no parse error, in all three cases
  • go test ./... and the nix build's test suite pass
  • A dirty working tree produces a -dev suffix, not -dirty

https://claude.ai/code/session_01PKKmeZhi8VV2EcvknMV9Qv

k8s.io/component-base/version's gitVersion was never set via ldflags in
flake.nix, so `datumctl version` crashed parsing its compiled-in
placeholder "v0.0.0-master+$Format:%H$". The prior git-describe fallback
was also dead: nix flakes strip .git from the copied source, so it
silently produced "dev" instead of a real version.

Now the git sha comes from self.dirtyRev/rev (reliable without .git),
the last tag from an optional untracked VERSION file staged via
`git add -N` (flake sources only see files git knows about), and both
feed main.version and the component-base ldflags so the result is
always a valid <tag>+<sha>[-dev] version string.
@drewr
drewr enabled auto-merge September 5, 2026 20:04
@drewr
drewr requested a review from scotwells September 5, 2026 20:14
k8s.io/component-base/version's GitVersion is only real when ldflags set
it (goreleaser or the nix flake); a plain `go build`/`go run` in local
dev still left it at the unparseable compiled-in placeholder, which the
plugin compatibility check and update checker both read as a version.

Add internal/version.ApplyFallback, called at startup, which detects the
placeholder and derives a valid v0.0.0+<sha>[-dev] version from the VCS
info Go's toolchain already stamps into the binary automatically. It's a
no-op whenever ldflags already provided a real version. Also drop the
flake's `-X main.version=...` ldflag, which targeted a variable that
doesn't exist in package main.
@drewr drewr changed the title fix: Make nix flake builds report a valid version fix: Report a valid version string with unreleased builds Sep 6, 2026
internal/version.ApplyFallback's runtime patch can only ever produce
"v0.0.0+<sha>": component-base's SetDynamicVersion rejects any version
whose major/minor/patch don't match the compiled-in placeholder's
(0.0.0), and Go's automatic VCS stamping has no notion of the nearest
tag anyway. So a plain `go build`/`go run` always showed v0.0.0 even
when a real release tag was available.

Add `task build`, which embeds the real <last-tag>+<sha>[-dev] version
via ldflags at compile time (like nix/goreleaser already do), so local
development builds get a proper version instead of the v0.0.0 fallback.

Claude-Session: https://claude.ai/code/session_01PKKmeZhi8VV2EcvknMV9Qv
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