From aa40a746d0ccb2477f68c3a62e587913ce7cb2ea Mon Sep 17 00:00:00 2001 From: Hammad Majid Date: Thu, 17 Sep 2026 12:31:25 +0500 Subject: [PATCH 1/2] fix(cli): report the version from Go build info when ldflags are absent go install builds never receive GoReleaser's ldflags, so every module install said "cdd dev (dev)". Fall back to the module version Go embeds, then to the VCS revision for checkout builds. Co-Authored-By: Claude Fable 5.1 --- internal/cli/version.go | 61 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/internal/cli/version.go b/internal/cli/version.go index 779feb7..f98c02c 100644 --- a/internal/cli/version.go +++ b/internal/cli/version.go @@ -2,13 +2,15 @@ package cli import ( "fmt" + "runtime/debug" + "strings" "github.com/spf13/cobra" ) // version, commit, and date are set at build time via -ldflags (see // .goreleaser.yaml). They default to values that make an unflagged build -// identify itself as a dev build. +// fall back to the build information Go embeds in every binary. var ( version = "dev" commit = "" @@ -29,11 +31,60 @@ func newVersionCmd() *cobra.Command { } } -// versionString formats the version, commit, and date set by ldflags. When -// none of them were set, it reports "cdd dev (dev)". +// versionString formats the version from ldflags when GoReleaser set them, +// otherwise from the build information Go embeds: the module version for +// a `go install module@tag` build, the VCS revision for a build from a +// checkout, and "cdd dev (dev)" when neither is known. func versionString() string { - if version == "dev" && commit == "" && date == "" { + info, _ := debug.ReadBuildInfo() + return formatVersion(version, commit, date, info) +} + +// formatVersion is the pure core of versionString, split out so tests can +// feed it ldflags values and build information directly. +func formatVersion(version, commit, date string, info *debug.BuildInfo) string { + if version != "dev" || commit != "" || date != "" { + return fmt.Sprintf("cdd %s (%s %s)", version, commit, date) + } + if info == nil { return "cdd dev (dev)" } - return fmt.Sprintf("cdd %s (%s %s)", version, commit, date) + + if v := info.Main.Version; v != "" && v != "(devel)" { + return fmt.Sprintf("cdd %s (go install)", strings.TrimPrefix(v, "v")) + } + + rev, when, modified := vcsSettings(info) + if rev == "" { + return "cdd dev (dev)" + } + if modified { + rev += "+dirty" + } + if when == "" { + return fmt.Sprintf("cdd dev (%s)", rev) + } + return fmt.Sprintf("cdd dev (%s %s)", rev, when) +} + +// vcsSettings extracts the short revision, commit date, and dirty flag that +// Go records when building from a version-controlled checkout. +func vcsSettings(info *debug.BuildInfo) (rev, when string, modified bool) { + for _, s := range info.Settings { + switch s.Key { + case "vcs.revision": + rev = s.Value + if len(rev) > 7 { + rev = rev[:7] + } + case "vcs.time": + when = s.Value + if len(when) > 10 { + when = when[:10] + } + case "vcs.modified": + modified = s.Value == "true" + } + } + return rev, when, modified } From 9387f2840f46575781a519e4cc579fa2455e19aa Mon Sep 17 00:00:00 2001 From: Hammad Majid Date: Thu, 17 Sep 2026 12:31:25 +0500 Subject: [PATCH 2/2] test(cli): cover version formatting for ldflags, go install, checkout, and unknown builds Co-Authored-By: Claude Fable 5.1 --- internal/cli/version_test.go | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 internal/cli/version_test.go diff --git a/internal/cli/version_test.go b/internal/cli/version_test.go new file mode 100644 index 0000000..ed19008 --- /dev/null +++ b/internal/cli/version_test.go @@ -0,0 +1,47 @@ +package cli + +import ( + "runtime/debug" + "testing" +) + +func TestFormatVersion(t *testing.T) { + vcs := &debug.BuildInfo{ + Main: debug.Module{Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "vcs.revision", Value: "b72f0117d2c4a9e1f0c3"}, + {Key: "vcs.time", Value: "2026-09-17T07:20:28Z"}, + {Key: "vcs.modified", Value: "false"}, + }, + } + dirty := &debug.BuildInfo{ + Main: debug.Module{Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "vcs.revision", Value: "b72f011"}, + {Key: "vcs.modified", Value: "true"}, + }, + } + + tests := []struct { + name string + version, commit, date string + info *debug.BuildInfo + want string + }{ + {name: "ldflags from GoReleaser", version: "0.1.1", commit: "b72f011", date: "2026-09-17", info: vcs, want: "cdd 0.1.1 (b72f011 2026-09-17)"}, + {name: "go install of a tagged module", version: "dev", info: &debug.BuildInfo{Main: debug.Module{Version: "v0.1.1"}}, want: "cdd 0.1.1 (go install)"}, + {name: "build from a clean checkout", version: "dev", info: vcs, want: "cdd dev (b72f011 2026-09-17)"}, + {name: "build from a dirty checkout", version: "dev", info: dirty, want: "cdd dev (b72f011+dirty)"}, + {name: "no build information", version: "dev", info: nil, want: "cdd dev (dev)"}, + {name: "devel without vcs", version: "dev", info: &debug.BuildInfo{Main: debug.Module{Version: "(devel)"}}, want: "cdd dev (dev)"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := formatVersion(tt.version, tt.commit, tt.date, tt.info) + if got != tt.want { + t.Errorf("formatVersion() = %q, want %q", got, tt.want) + } + }) + } +}