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
34 changes: 32 additions & 2 deletions cmd/skill-sync/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,45 @@ package main

import (
"os"
"runtime/debug"

"github.com/eezzekl/skill-sync/internal/cli"
)

// devVersion is the placeholder reported when no build stamped a real version.
const devVersion = "dev"

// version is set at build time via -ldflags "-X main.version=vX.Y.Z"
var version = "dev"
var version = devVersion

// resolveVersion decides which version string to report. A GoReleaser build
// stamps ldflagVersion and wins outright. A `go install` build cannot receive
// those ldflags, so it falls back to the module version the toolchain embeds.
// A plain local build has neither and reports devVersion.
func resolveVersion(ldflagVersion, moduleVersion string) string {
if ldflagVersion != "" && ldflagVersion != devVersion {
return ldflagVersion
}
// "(devel)" is what the toolchain reports for a build outside a tagged
// module version, which carries no more information than devVersion.
if moduleVersion != "" && moduleVersion != "(devel)" {
return moduleVersion
}
return devVersion
}

// moduleVersion returns the module version the Go toolchain embeds in the
// binary, or an empty string when build information is unavailable.
func moduleVersion() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return ""
}
return info.Main.Version
}

func main() {
if err := cli.NewRootCmd(version).Execute(); err != nil {
if err := cli.NewRootCmd(resolveVersion(version, moduleVersion())).Execute(); err != nil {
os.Exit(1)
}
}
75 changes: 75 additions & 0 deletions cmd/skill-sync/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package main

import "testing"

// TestResolveVersion covers both ways this binary gets built. GoReleaser stamps
// the version through -ldflags, but `go install` cannot receive a maintainer's
// ldflags: it compiles from the module proxy with its own flags. In that path the
// only version available is the one the Go toolchain embeds in the binary, which
// is why the ldflag value alone is not enough.
func TestResolveVersion(t *testing.T) {
tests := []struct {
name string
ldflagVersion string
moduleVersion string
want string
}{
{
name: "ldflag wins when GoReleaser stamped it",
ldflagVersion: "1.1.0",
moduleVersion: "v1.1.0+dirty",
want: "1.1.0",
},
{
name: "falls back to module version on go install",
ldflagVersion: devVersion,
moduleVersion: "v1.1.0",
want: "v1.1.0",
},
{
// The toolchain stamps VCS state by default, so a local build in a
// git worktree yields a pseudo-version rather than "(devel)". It is
// reported as-is on purpose: it pins the exact commit and flags a
// dirty tree, which is precisely what a bug report needs.
name: "reports the pseudo-version of a local VCS-stamped build",
ldflagVersion: devVersion,
moduleVersion: "v1.1.1-0.20261002231447-f9c2b733d696+dirty",
want: "v1.1.1-0.20261002231447-f9c2b733d696+dirty",
},
{
// Reachable with -buildvcs=false.
name: "stays dev for a build with no VCS stamping",
ldflagVersion: devVersion,
moduleVersion: "(devel)",
want: devVersion,
},
{
name: "stays dev when build info is unavailable",
ldflagVersion: devVersion,
moduleVersion: "",
want: devVersion,
},
{
name: "falls back when the ldflag is stamped empty",
ldflagVersion: "",
moduleVersion: "v1.1.0",
want: "v1.1.0",
},
{
name: "reports dev when neither source knows the version",
ldflagVersion: "",
moduleVersion: "",
want: devVersion,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := resolveVersion(tt.ldflagVersion, tt.moduleVersion)
if got != tt.want {
t.Errorf("resolveVersion(%q, %q) = %q, want %q",
tt.ldflagVersion, tt.moduleVersion, got, tt.want)
}
})
}
}
Loading