From 042b27502e1d2938c116e9aa4563e6523b66202a Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Mon, 11 Mar 2024 14:27:41 +0000 Subject: [PATCH] internal/cueversion: new package This CL factors out the version-determination logic from cmd/cue. This will enable it to be used by other packages, including putting inside the User-Agent header when making HTTP requests. This does make one of the existing cmd/cue tests not possible to write, but the logic is simple enough (and it doesn't take any additional code paths) that this seems like it should be OK. Signed-off-by: Roger Peppe Change-Id: Ic2accbe29d64bf9cc62cdca413c66edd1ce19e4b Dispatch-Trailer: {"type":"trybot","CL":1178030,"patchset":2,"ref":"refs/changes/30/1178030/2","targetBranch":"master"} --- cmd/cue/cmd/mod.go | 3 +- .../script/modtidy_with_vcsinfo.txtar | 32 --------------- cmd/cue/cmd/version.go | 36 +++++------------ internal/cueversion/version.go | 39 +++++++++++++++++++ internal/cueversion/version_test.go | 19 +++++++++ 5 files changed, 69 insertions(+), 60 deletions(-) delete mode 100644 cmd/cue/cmd/testdata/script/modtidy_with_vcsinfo.txtar create mode 100644 internal/cueversion/version.go create mode 100644 internal/cueversion/version_test.go diff --git a/cmd/cue/cmd/mod.go b/cmd/cue/cmd/mod.go index ebd9fc695..49544ecc4 100644 --- a/cmd/cue/cmd/mod.go +++ b/cmd/cue/cmd/mod.go @@ -192,8 +192,7 @@ func backport(mod, cwd string) error { } func versionForModFile() string { - bi, _ := readBuildInfo() - version := cueVersion(bi) + version := cueVersion() if gomodule.IsPseudoVersion(version) { // If we have a version like v0.7.1-0.20240130142347-7855e15cb701 // we want it to turn into the base version (v0.7.0 in that example). diff --git a/cmd/cue/cmd/testdata/script/modtidy_with_vcsinfo.txtar b/cmd/cue/cmd/testdata/script/modtidy_with_vcsinfo.txtar deleted file mode 100644 index 1e189f6d2..000000000 --- a/cmd/cue/cmd/testdata/script/modtidy_with_vcsinfo.txtar +++ /dev/null @@ -1,32 +0,0 @@ -# Check that cue mod tidy does not add the language version -# to the module.cue file when it's a dev version created -# by cue-internal logic. - -env CUE_VERSION_TEST_CFG='[{"Key":"vcs","Value":"git"},{"Key":"vcs.revision","Value":"47b7032385cb490fab7d47b89fca36835cf13d39"},{"Key":"vcs.time","Value":"2022-05-10T04:58:46Z"},{"Key":"vcs.modified","Value":"true"}]' -exec cue mod tidy -cmp cue.mod/module.cue want-module - -# Check that the resulting module evaluates as expected. -exec cue export . -cmp stdout want-stdout --- want-stdout -- -{ - "x": 1 -} --- want-module -- -module: "main.org@v0" -language: { - version: "v0.8.0-alpha.5" -} --- cue.mod/module.cue -- -module: "main.org@v0" - --- main.cue -- -package main -x: 1 - --- _registry/example.com_v0.0.1/cue.mod/module.cue -- -module: "example.com@v0" - --- _registry/example.com_v0.0.1/top.cue -- -package main diff --git a/cmd/cue/cmd/version.go b/cmd/cue/cmd/version.go index 737cf31c9..221458165 100644 --- a/cmd/cue/cmd/version.go +++ b/cmd/cue/cmd/version.go @@ -23,6 +23,8 @@ import ( "runtime/debug" "github.com/spf13/cobra" + + "cuelang.org/go/internal/cueversion" ) func newVersionCmd(c *Command) *cobra.Command { @@ -44,15 +46,6 @@ func newVersionCmd(c *Command) *cobra.Command { // considered legacy. var version string -// fallbackVersion is used as-is when the -ldflags above isn't used -// and when there isn't a recorded main module version, -// for example when building via `go install ./cmd/cue`. -// It should reflect the last release in the current branch. -// -// TODO: remove once Go stamps local builds with a main module version -// derived from the local VCS information per https://go.dev/issue/50603. -const fallbackVersion = "v0.8.0-alpha.5" - func runVersion(cmd *Command, args []string) error { w := cmd.OutOrStdout() @@ -62,7 +55,7 @@ func runVersion(cmd *Command, args []string) error { // shouldn't happen return errors.New("unknown error reading build-info") } - fmt.Fprintf(w, "cue version %s\n\n", cueVersion(bi)) + fmt.Fprintf(w, "cue version %s\n\n", cueVersion()) fmt.Fprintf(w, "go version %s\n", runtime.Version()) for _, s := range bi.Settings { if s.Value == "" { @@ -83,26 +76,17 @@ func runVersion(cmd *Command, args []string) error { // cueVersion returns the version of the CUE module as much // as can reasonably be determined. If no version can be // determined, it returns the empty string. -func cueVersion(bi *debug.BuildInfo) string { - if v := os.Getenv("CUE_VERSION_OVERRIDE"); v != "" && inTest { - return v +func cueVersion() string { + if inTest { + if v := os.Getenv("CUE_VERSION_OVERRIDE"); v != "" { + return v + } } - v := version - if v != "" { + if v := version; v != "" { // The global version variable has been configured via ldflags. return v } - if bi == nil { - return fallbackVersion - } - switch bi.Main.Version { - case "": // missing version - case "(devel)": // local build - case "v0.0.0-00010101000000-000000000000": // build via a directory replace directive - default: - return bi.Main.Version - } - return fallbackVersion + return cueversion.Version() } func readBuildInfo() (*debug.BuildInfo, bool) { diff --git a/internal/cueversion/version.go b/internal/cueversion/version.go new file mode 100644 index 000000000..3ec0b76c3 --- /dev/null +++ b/internal/cueversion/version.go @@ -0,0 +1,39 @@ +// Package cueversion provides access to the version of the +// cuelang.org/go module. +package cueversion + +import ( + "runtime/debug" + "sync" +) + +// fallbackVersion is used when there isn't a recorded main module +// version, for example when building via `go install ./cmd/cue`. It +// should reflect the last release in the current branch. +// +// TODO: remove once Go stamps local builds with a main module version +// derived from the local VCS information per +// https://go.dev/issue/50603. +const fallbackVersion = "v0.8.0-alpha.5" + +// Version returns the version of the cuelang.org/go module as best +// as can reasonably be determined. The result is always a valid Go +// semver version. +func Version() string { + return versionOnce() +} + +var versionOnce = sync.OnceValue(func() string { + bi, ok := debug.ReadBuildInfo() + if !ok { + return fallbackVersion + } + switch bi.Main.Version { + case "": // missing version + case "(devel)": // local build + case "v0.0.0-00010101000000-000000000000": // build via a directory replace directive + default: + return bi.Main.Version + } + return fallbackVersion +}) diff --git a/internal/cueversion/version_test.go b/internal/cueversion/version_test.go new file mode 100644 index 000000000..e1779166c --- /dev/null +++ b/internal/cueversion/version_test.go @@ -0,0 +1,19 @@ +package cueversion + +import ( + "testing" + + "github.com/go-quicktest/qt" + "golang.org/x/mod/semver" +) + +func TestVersion(t *testing.T) { + // This is just a smoke test to make sure that things + // are wired up OK. It would be possible to unit + // test the logic inside Version, but it's simple + // enough that that would amount to creating invariants + // that just match the code, not providing any more + // assurance of correctness. + vers := Version() + qt.Assert(t, qt.Satisfies(vers, semver.IsValid)) +}