Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 56 additions & 5 deletions internal/cli/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""
Expand All @@ -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
}
47 changes: 47 additions & 0 deletions internal/cli/version_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}