Skip to content

Commit 5ca33c2

Browse files
Fall back to runtime/debug.ReadBuildInfo for version info (#71) (#72)
When ldflags are not set (e.g. `go install`), populate Version, Commit, and Date from Go's embedded build info instead of showing "dev". Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4f6770a commit 5ca33c2

2 files changed

Lines changed: 143 additions & 0 deletions

File tree

internal/cli/version.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"fmt"
5+
"runtime/debug"
56

67
"github.com/devrimcavusoglu/skern/internal/output"
78
"github.com/spf13/cobra"
@@ -14,6 +15,38 @@ var (
1415
Date = "unknown"
1516
)
1617

18+
func init() {
19+
initVersionFromBuildInfo(debug.ReadBuildInfo)
20+
}
21+
22+
// initVersionFromBuildInfo populates Version, Commit, and Date from Go build
23+
// info when ldflags have not been set. The reader parameter allows tests to
24+
// inject a fake ReadBuildInfo.
25+
func initVersionFromBuildInfo(reader func() (*debug.BuildInfo, bool)) {
26+
if Version != "dev" {
27+
return // ldflags already set
28+
}
29+
info, ok := reader()
30+
if !ok {
31+
return
32+
}
33+
if info.Main.Version != "" && info.Main.Version != "(devel)" {
34+
Version = info.Main.Version
35+
}
36+
for _, s := range info.Settings {
37+
switch s.Key {
38+
case "vcs.revision":
39+
if s.Value != "" {
40+
Commit = s.Value
41+
}
42+
case "vcs.time":
43+
if s.Value != "" {
44+
Date = s.Value
45+
}
46+
}
47+
}
48+
}
49+
1750
func newVersionCmd() *cobra.Command {
1851
return &cobra.Command{
1952
Use: "version",

internal/cli/version_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"bytes"
5+
"runtime/debug"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
@@ -36,6 +37,115 @@ func TestVersionCommand_JSON(t *testing.T) {
3637
require.NoError(t, err)
3738
}
3839

40+
func TestInitVersionFromBuildInfo(t *testing.T) {
41+
tests := []struct {
42+
name string
43+
initialVersion string
44+
buildInfo *debug.BuildInfo
45+
buildInfoOk bool
46+
expectedVersion string
47+
expectedCommit string
48+
expectedDate string
49+
}{
50+
{
51+
name: "ldflags already set",
52+
initialVersion: "v1.0.0",
53+
buildInfo: &debug.BuildInfo{Main: debug.Module{Version: "v2.0.0"}},
54+
buildInfoOk: true,
55+
expectedVersion: "v1.0.0",
56+
expectedCommit: "none",
57+
expectedDate: "unknown",
58+
},
59+
{
60+
name: "build info unavailable",
61+
initialVersion: "dev",
62+
buildInfo: nil,
63+
buildInfoOk: false,
64+
expectedVersion: "dev",
65+
expectedCommit: "none",
66+
expectedDate: "unknown",
67+
},
68+
{
69+
name: "go install with version and vcs info",
70+
initialVersion: "dev",
71+
buildInfo: &debug.BuildInfo{
72+
Main: debug.Module{Version: "v0.1.1"},
73+
Settings: []debug.BuildSetting{
74+
{Key: "vcs.revision", Value: "abc123def456"},
75+
{Key: "vcs.time", Value: "2026-01-15T10:30:00Z"},
76+
},
77+
},
78+
buildInfoOk: true,
79+
expectedVersion: "v0.1.1",
80+
expectedCommit: "abc123def456",
81+
expectedDate: "2026-01-15T10:30:00Z",
82+
},
83+
{
84+
name: "devel version is ignored",
85+
initialVersion: "dev",
86+
buildInfo: &debug.BuildInfo{
87+
Main: debug.Module{Version: "(devel)"},
88+
Settings: []debug.BuildSetting{
89+
{Key: "vcs.revision", Value: "abc123"},
90+
},
91+
},
92+
buildInfoOk: true,
93+
expectedVersion: "dev",
94+
expectedCommit: "abc123",
95+
expectedDate: "unknown",
96+
},
97+
{
98+
name: "version set but no vcs info",
99+
initialVersion: "dev",
100+
buildInfo: &debug.BuildInfo{
101+
Main: debug.Module{Version: "v0.2.0"},
102+
},
103+
buildInfoOk: true,
104+
expectedVersion: "v0.2.0",
105+
expectedCommit: "none",
106+
expectedDate: "unknown",
107+
},
108+
{
109+
name: "empty vcs values are ignored",
110+
initialVersion: "dev",
111+
buildInfo: &debug.BuildInfo{
112+
Main: debug.Module{Version: "v0.3.0"},
113+
Settings: []debug.BuildSetting{
114+
{Key: "vcs.revision", Value: ""},
115+
{Key: "vcs.time", Value: ""},
116+
},
117+
},
118+
buildInfoOk: true,
119+
expectedVersion: "v0.3.0",
120+
expectedCommit: "none",
121+
expectedDate: "unknown",
122+
},
123+
}
124+
125+
for _, tt := range tests {
126+
t.Run(tt.name, func(t *testing.T) {
127+
// Save and restore globals
128+
origVersion, origCommit, origDate := Version, Commit, Date
129+
t.Cleanup(func() {
130+
Version, Commit, Date = origVersion, origCommit, origDate
131+
})
132+
133+
Version = tt.initialVersion
134+
Commit = "none"
135+
Date = "unknown"
136+
137+
reader := func() (*debug.BuildInfo, bool) {
138+
return tt.buildInfo, tt.buildInfoOk
139+
}
140+
initVersionFromBuildInfo(reader)
141+
142+
assert.Equal(t, tt.expectedVersion, Version)
143+
assert.Equal(t, tt.expectedCommit, Commit)
144+
assert.Equal(t, tt.expectedDate, Date)
145+
})
146+
}
147+
}
148+
39149
func TestRootCommand_Help(t *testing.T) {
40150
cmd := NewRootCmd()
41151
buf := new(bytes.Buffer)

0 commit comments

Comments
 (0)