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
38 changes: 32 additions & 6 deletions internal/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,42 @@ var Version = "0.0.0-dev"
// devVersion is the source default, present when no release version was stamped in.
const devVersion = "0.0.0-dev"

// released returns the release version this binary carries, or "" when it is an
// unstamped development build. A goreleaser build sets Version through ldflags; a
// `go install <module>@<version>` build does not, but Go records the module version in
// the build info, so that is used as the fallback. "(devel)" is Go's placeholder for a
// build from a local checkout and is treated as unversioned.
func released() string {
if Version != devVersion {
return Version
}
if bi, ok := debug.ReadBuildInfo(); ok {
return moduleVersion(bi.Main.Version)
}
return ""
}

// moduleVersion returns v when it is a usable module version, or "" for an absent
// version or Go's "(devel)" placeholder (a build from a local checkout).
func moduleVersion(v string) string {
if v == "" || v == "(devel)" {
return ""
}
return v
}

// IsDev reports whether this is an unstamped development build (no release version linked
// in). A dev build keeps its durable state apart from a release build's, so an
// in-progress schema change on a branch never touches a real installation.
func IsDev() bool { return Version == devVersion }
// in-progress schema change on a branch never touches a real installation. A build
// installed with `go install ...@<version>` is a real release, not a dev build.
func IsDev() bool { return released() == "" }

// String returns a human-readable version, appending the VCS revision when the
// binary was built from a git checkout and no explicit version was set.
// String returns a human-readable version: the release version when one is present,
// otherwise the source default with the VCS revision appended when the binary was built
// from a git checkout.
func String() string {
if Version != devVersion {
return Version
if v := released(); v != "" {
return v
}
if bi, ok := debug.ReadBuildInfo(); ok {
for _, s := range bi.Settings {
Expand Down
30 changes: 30 additions & 0 deletions internal/version/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,33 @@ func TestIsDevOnUnstampedBuild(t *testing.T) {
t.Fatal("IsDev() = false for an unstamped build, want true")
}
}

// TestStampedVersionWins confirms a release version linked in through ldflags is reported
// verbatim and is not treated as a development build.
func TestStampedVersionWins(t *testing.T) {
old := Version
defer func() { Version = old }()
Version = "v9.9.9"
if got := String(); got != "v9.9.9" {
t.Fatalf("String() = %q, want v9.9.9", got)
}
if IsDev() {
t.Fatal("IsDev() = true for a stamped build, want false")
}
}

// TestModuleVersionFallback confirms the `go install <module>@<version>` path: when no
// release version is stamped through ldflags, a real module version from the build info
// is used, while an absent version or Go's "(devel)" placeholder is not.
func TestModuleVersionFallback(t *testing.T) {
for in, want := range map[string]string{
"v0.1.1": "v0.1.1",
"v1.2.3-rc1": "v1.2.3-rc1",
"(devel)": "",
"": "",
} {
if got := moduleVersion(in); got != want {
t.Errorf("moduleVersion(%q) = %q, want %q", in, got, want)
}
}
}
Loading