mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
Show git commit and build time in vintner version
Uses Go's automatic VCS build-info stamping (vcs.revision/vcs.time/ vcs.modified, embedded by `go build` since Go 1.18 - no -ldflags changes needed, works the same for a CI release build and a plain local `go build`). Knowing the exact commit a bug report's binary was built from, not just the X.Y.Z tag, is the point - two builds of the same tag could still differ. Split the pure formatting logic (formatVersion) from the debug.ReadBuildInfo() call so it's actually unit-testable: `go test` binaries don't get VCS stamping the way `go build` ones do, so there was no way to exercise the revision-formatting branch through versionString() itself.
This commit is contained in:
+1
-1
@@ -50,7 +50,7 @@ func runCLI(args []string) int {
|
|||||||
case "completion":
|
case "completion":
|
||||||
return runCompletion(args[1:])
|
return runCompletion(args[1:])
|
||||||
case "version", "v", "--version":
|
case "version", "v", "--version":
|
||||||
fmt.Println("vintner " + version)
|
fmt.Println(versionString())
|
||||||
return 0
|
return 0
|
||||||
case "-h", "--help", "help", "h":
|
case "-h", "--help", "help", "h":
|
||||||
printUsage()
|
printUsage()
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
// versionString renders "vintner <version>" plus, when available, the git
|
||||||
|
// commit and build time Go's toolchain embeds automatically (since Go
|
||||||
|
// 1.18, `go build` stamps vcs.revision/vcs.time/vcs.modified into the
|
||||||
|
// binary on its own - no -ldflags needed for this part, so it works the
|
||||||
|
// same whether the binary came from CI or a plain local `go build`).
|
||||||
|
// Knowing the exact commit a reported bug was built from, not just the
|
||||||
|
// X.Y.Z tag, is the point: two builds of the same tag could still differ
|
||||||
|
// if the tag was ever moved, or if someone built from an uncommitted tree.
|
||||||
|
//
|
||||||
|
// Note for anyone testing this: `go build` stamps vcs.* build settings,
|
||||||
|
// but `go test` binaries don't get them - there's no environment where a
|
||||||
|
// `go test` run can exercise the revision-formatting branch below, hence
|
||||||
|
// formatVersion is split out and tested directly instead.
|
||||||
|
func versionString() string {
|
||||||
|
revision, buildTime, dirty := "", "", false
|
||||||
|
if info, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
for _, s := range info.Settings {
|
||||||
|
switch s.Key {
|
||||||
|
case "vcs.revision":
|
||||||
|
revision = s.Value
|
||||||
|
case "vcs.time":
|
||||||
|
buildTime = s.Value
|
||||||
|
case "vcs.modified":
|
||||||
|
dirty = s.Value == "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatVersion(version, revision, buildTime, dirty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatVersion is the pure part of versionString: given a revision (full
|
||||||
|
// git SHA, may be empty), it's shortened to 12 chars and marked "-dirty" if
|
||||||
|
// the build tree had uncommitted changes.
|
||||||
|
func formatVersion(ver, revision, buildTime string, dirty bool) string {
|
||||||
|
v := "vintner " + ver
|
||||||
|
if revision == "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if len(revision) > 12 {
|
||||||
|
revision = revision[:12]
|
||||||
|
}
|
||||||
|
if dirty {
|
||||||
|
revision += "-dirty"
|
||||||
|
}
|
||||||
|
if buildTime != "" {
|
||||||
|
return fmt.Sprintf("%s (%s, %s)", v, revision, buildTime)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (%s)", v, revision)
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFormatVersion(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
ver, revision, buildTime string
|
||||||
|
dirty bool
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no VCS info at all",
|
||||||
|
ver: "dev",
|
||||||
|
want: "vintner dev",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clean build with full info",
|
||||||
|
ver: "0.3.0",
|
||||||
|
revision: "6186837fd23616335ba8aff830801692a756799c",
|
||||||
|
buildTime: "2026-07-25T01:33:41Z",
|
||||||
|
want: "vintner 0.3.0 (6186837fd236, 2026-07-25T01:33:41Z)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dirty tree",
|
||||||
|
ver: "0.3.0",
|
||||||
|
revision: "6186837fd23616335ba8aff830801692a756799c",
|
||||||
|
dirty: true,
|
||||||
|
want: "vintner 0.3.0 (6186837fd236-dirty)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "short revision left untouched",
|
||||||
|
ver: "dev",
|
||||||
|
revision: "abc123",
|
||||||
|
want: "vintner dev (abc123)",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got := formatVersion(tc.ver, tc.revision, tc.buildTime, tc.dirty)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Errorf("formatVersion(%q, %q, %q, %v) = %q, want %q",
|
||||||
|
tc.ver, tc.revision, tc.buildTime, tc.dirty, got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user