From c7efcdc5ede16250e710aa653cf5122668600d2a Mon Sep 17 00:00:00 2001 From: Ion Alpha Date: Tue, 7 Jul 2026 05:16:06 +0000 Subject: [PATCH 1/2] fix(version): report the module version for a go install build, not 0.0.0-dev A binary built with go install @ does not get the release version through ldflags the way a goreleaser build does, so flynn --version printed 0.0.0-dev even when installed at a real tag. Fall back to the module version Go records in the build info when no ldflags version is set, and treat such a build as a real release (not a dev build) for durable-state isolation. A plain source checkout still reports the dev default with its VCS revision. --- internal/version/version.go | 38 +++++++++++++++++++++++++++----- internal/version/version_test.go | 30 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/internal/version/version.go b/internal/version/version.go index dbc06d2..ea98bfb 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -11,16 +11,42 @@ var Version = "0.0.0-dev" // devVersion is the source default, present when no release version was stamped in. const devVersion = "0.0.0-dev" +// released returns the release version this binary carries, or "" when it is an +// unstamped development build. A goreleaser build sets Version through ldflags; a +// `go install @` build does not, but Go records the module version in +// the build info, so that is used as the fallback. "(devel)" is Go's placeholder for a +// build from a local checkout and is treated as unversioned. +func released() string { + if Version != devVersion { + return Version + } + if bi, ok := debug.ReadBuildInfo(); ok { + return moduleVersion(bi.Main.Version) + } + return "" +} + +// moduleVersion returns v when it is a usable module version, or "" for an absent +// version or Go's "(devel)" placeholder (a build from a local checkout). +func moduleVersion(v string) string { + if v == "" || v == "(devel)" { + return "" + } + return v +} + // IsDev reports whether this is an unstamped development build (no release version linked // in). A dev build keeps its durable state apart from a release build's, so an -// in-progress schema change on a branch never touches a real installation. -func IsDev() bool { return Version == devVersion } +// in-progress schema change on a branch never touches a real installation. A build +// installed with `go install ...@` is a real release, not a dev build. +func IsDev() bool { return released() == "" } -// String returns a human-readable version, appending the VCS revision when the -// binary was built from a git checkout and no explicit version was set. +// String returns a human-readable version: the release version when one is present, +// otherwise the source default with the VCS revision appended when the binary was built +// from a git checkout. func String() string { - if Version != devVersion { - return Version + if v := released(); v != "" { + return v } if bi, ok := debug.ReadBuildInfo(); ok { for _, s := range bi.Settings { diff --git a/internal/version/version_test.go b/internal/version/version_test.go index 7061975..c98abc9 100644 --- a/internal/version/version_test.go +++ b/internal/version/version_test.go @@ -13,3 +13,33 @@ func TestIsDevOnUnstampedBuild(t *testing.T) { t.Fatal("IsDev() = false for an unstamped build, want true") } } + +// TestStampedVersionWins confirms a release version linked in through ldflags is reported +// verbatim and is not treated as a development build. +func TestStampedVersionWins(t *testing.T) { + old := Version + defer func() { Version = old }() + Version = "v9.9.9" + if got := String(); got != "v9.9.9" { + t.Fatalf("String() = %q, want v9.9.9", got) + } + if IsDev() { + t.Fatal("IsDev() = true for a stamped build, want false") + } +} + +// TestModuleVersionFallback confirms the `go install @` path: when no +// release version is stamped through ldflags, a real module version from the build info +// is used, while an absent version or Go's "(devel)" placeholder is not. +func TestModuleVersionFallback(t *testing.T) { + for in, want := range map[string]string{ + "v0.1.1": "v0.1.1", + "v1.2.3-rc1": "v1.2.3-rc1", + "(devel)": "", + "": "", + } { + if got := moduleVersion(in); got != want { + t.Errorf("moduleVersion(%q) = %q, want %q", in, got, want) + } + } +} From 5619e777cc9337f360e242a3d0cb096ae78fa833 Mon Sep 17 00:00:00 2001 From: Ion Alpha Date: Tue, 7 Jul 2026 05:20:00 +0000 Subject: [PATCH 2/2] style(version): gofumpt the version test map literal --- internal/version/version_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/version/version_test.go b/internal/version/version_test.go index c98abc9..175456d 100644 --- a/internal/version/version_test.go +++ b/internal/version/version_test.go @@ -33,10 +33,10 @@ func TestStampedVersionWins(t *testing.T) { // is used, while an absent version or Go's "(devel)" placeholder is not. func TestModuleVersionFallback(t *testing.T) { for in, want := range map[string]string{ - "v0.1.1": "v0.1.1", - "v1.2.3-rc1": "v1.2.3-rc1", - "(devel)": "", - "": "", + "v0.1.1": "v0.1.1", + "v1.2.3-rc1": "v1.2.3-rc1", + "(devel)": "", + "": "", } { if got := moduleVersion(in); got != want { t.Errorf("moduleVersion(%q) = %q, want %q", in, got, want)