diff --git a/README.md b/README.md index 7dcb19f..6312e75 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,8 @@ refuses and exits with status 1. A mnemonic that fails the BIP-39 checksum is refused with a message saying so. Every command takes `--index` / `-n` and `--mnemonic-command`, and has `--help`. -`keyfunc --version` prints the version set at build time. +`keyfunc --version` prints the version. `make build` stamps it; a binary +installed with `go install` reports the module version instead. ## SSH keys: `keyfunc ssh` diff --git a/internal/cli/cli.go b/internal/cli/cli.go index c6d63d0..57b4da1 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "runtime/debug" "git.eeqj.de/sneak/keyfunc/internal/cli/age" "git.eeqj.de/sneak/keyfunc/internal/cli/mnemonic" @@ -13,20 +14,43 @@ import ( "github.com/spf13/cobra" ) -// Version is what --version prints. The build sets it. +// devVersion is what Version holds until a build stamps a real one. +const devVersion = "dev" + +// Version is what --version prints. make build stamps it with -ldflags. // //nolint:gochecknoglobals // set at build time with -ldflags -var Version = "dev" +var Version = devVersion + +// resolveVersion chooses what --version reports. A value stamped at +// build time wins. Otherwise, for a binary from go install, the module +// version recorded in the build info is used, unless that is empty or +// the "(devel)" of a local build. When neither names a version, the +// "dev" fallback stays. +func resolveVersion(stamped string, info *debug.BuildInfo) string { + if stamped != devVersion { + return stamped + } + + if info != nil && info.Main.Version != "" && + info.Main.Version != "(devel)" { + return info.Main.Version + } + + return devVersion +} // Root returns the whole command tree. func Root() *cobra.Command { + info, _ := debug.ReadBuildInfo() + root := &cobra.Command{ Use: "keyfunc", Short: "derive key pairs from a BIP-39 mnemonic", Long: "keyfunc turns a BIP-39 mnemonic into key pairs that can " + "be recreated from that mnemonic at any time. The same " + "mnemonic, key type and index always give the same key.", - Version: Version, + Version: resolveVersion(Version, info), SilenceUsage: true, SilenceErrors: true, } diff --git a/internal/cli/version_internal_test.go b/internal/cli/version_internal_test.go new file mode 100644 index 0000000..9c01c91 --- /dev/null +++ b/internal/cli/version_internal_test.go @@ -0,0 +1,37 @@ +package cli + +import ( + "runtime/debug" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestResolveVersion(t *testing.T) { + t.Parallel() + + release := &debug.BuildInfo{Main: debug.Module{Version: "v1.2.3"}} + local := &debug.BuildInfo{Main: debug.Module{Version: "(devel)"}} + empty := &debug.BuildInfo{} + + t.Run("stamped value wins over build info", func(t *testing.T) { + t.Parallel() + require.Equal(t, "v0.1.0", resolveVersion("v0.1.0", release)) + }) + + t.Run("go install reports the module version", func(t *testing.T) { + t.Parallel() + require.Equal(t, "v1.2.3", resolveVersion(devVersion, release)) + }) + + t.Run("a local build stays dev", func(t *testing.T) { + t.Parallel() + require.Equal(t, devVersion, resolveVersion(devVersion, local)) + }) + + t.Run("no version anywhere stays dev", func(t *testing.T) { + t.Parallel() + require.Equal(t, devVersion, resolveVersion(devVersion, empty)) + require.Equal(t, devVersion, resolveVersion(devVersion, nil)) + }) +}