Skip to content

Commit

Permalink
cmd/cue: add version to module.cue file
Browse files Browse the repository at this point in the history
We build on https://cuelang.org/cl/1173894 by providing the
version when available. We now support the `$CUE_VERSION_OVERRIDE`
environment variable mainly for testing, but also because it might
potentially be useful in the future.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I081a056864a28456b3a8f63fdbdb14482649ac4d
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1176194
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
  • Loading branch information
rogpeppe committed Jan 30, 2024
1 parent ec7ca0a commit 9ceec10
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 46 deletions.
13 changes: 12 additions & 1 deletion cmd/cue/cmd/modtidy.go
Expand Up @@ -22,6 +22,7 @@ import (
"path/filepath"

"github.com/spf13/cobra"
gomodule "golang.org/x/mod/module"

"cuelang.org/go/internal/mod/modload"
)
Expand Down Expand Up @@ -58,7 +59,17 @@ func runModTidy(cmd *Command, args []string) error {
if err != nil {
return err
}
mf, err := modload.Tidy(ctx, os.DirFS(modRoot), ".", reg, "")
bi, _ := readBuildInfo()
version := cueVersion(bi)
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).
// If there's no base version (e.g. v0.0.0-...) then PseudoVersionBase
// will return the empty string, which is exactly what we want
// because we don't want to put v0.0.0 in a module.cue file.
version, _ = gomodule.PseudoVersionBase(version)
}
mf, err := modload.Tidy(ctx, os.DirFS(modRoot), ".", reg, version)
if err != nil {
return err
}
Expand Down
35 changes: 35 additions & 0 deletions cmd/cue/cmd/testdata/script/modtidy_with_existing_version.txtar
@@ -0,0 +1,35 @@
# Check that cue mod tidy won't change the language
# version when the field is already present in the module.cue file.

env CUE_VERSION_OVERRIDE=v0.9.9
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.1.2"
}
-- cue.mod/module.cue --
module: "main.org@v0"
language: {
version: "v0.1.2"
}

-- 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
31 changes: 31 additions & 0 deletions cmd/cue/cmd/testdata/script/modtidy_with_pseudoversion.txtar
@@ -0,0 +1,31 @@
# Check that cue mod tidy chooses a correct base version
# when provided with a pseudo-version.

env CUE_VERSION_OVERRIDE=v0.7.1-0.20240130142347-7855e15cb701
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.7.0"
}
-- 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
29 changes: 29 additions & 0 deletions cmd/cue/cmd/testdata/script/modtidy_with_vcsinfo.txtar
@@ -0,0 +1,29 @@
# 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"
-- 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
36 changes: 36 additions & 0 deletions cmd/cue/cmd/testdata/script/modtidy_with_version.txtar
@@ -0,0 +1,36 @@
# Check that cue mod tidy adds the language version to the
# module.cue file when there is one. Note that because
# the version is taken from the build info, we need to use
# the CUE_VERSION_OVERRIDE environment variable.
# We get confidence in the actual buildinfo logic because exactly
# the same code is used behind the scenes for the `cue version`
# implementation too.

env CUE_VERSION_OVERRIDE=v0.1.2
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.1.2"
}
-- 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
116 changes: 71 additions & 45 deletions cmd/cue/cmd/version.go
Expand Up @@ -39,65 +39,28 @@ func newVersionCmd(c *Command) *cobra.Command {

const defaultVersion = "(devel)"

// version be set by a builder using
// version can be set by a builder using
// -ldflags='-X cuelang.org/go/cmd/cue/cmd.version=<version>'.
// However, people should prefer building via a mechanism which
// resolves cuelang.org/go as a dependency (and not the main
// module), in which case the version information is determined
// from the *debug.BuildInfo (see below). So this mechanism is
// really considered legacy.
var version = defaultVersion
// considered legacy.
var version string

func runVersion(cmd *Command, args []string) error {
w := cmd.OutOrStdout()

// read in build info
bi, ok := debug.ReadBuildInfo()
bi, ok := readBuildInfo()
if !ok {
// shouldn't happen
return errors.New("unknown error reading build-info")
}

// test-based overrides
if v := os.Getenv("CUE_VERSION_TEST_CFG"); v != "" {
var extra []debug.BuildSetting
if err := json.Unmarshal([]byte(v), &extra); err != nil {
return err
}
bi.Settings = append(bi.Settings, extra...)
version := cueVersion(bi)
if version == "" {
version = defaultVersion
}

// prefer ldflags `version` override
if version == defaultVersion {
// no version provided via ldflags, try buildinfo
if bi.Main.Version != "" && bi.Main.Version != defaultVersion {
version = bi.Main.Version
}
}

if version == defaultVersion {
// a specific version was not provided by ldflags or buildInfo
// attempt to make our own
var vcsTime time.Time
var vcsRevision string
for _, s := range bi.Settings {
switch s.Key {
case "vcs.time":
// If the format is invalid, we'll print a zero timestamp.
vcsTime, _ = time.Parse(time.RFC3339Nano, s.Value)
case "vcs.revision":
vcsRevision = s.Value
// module.PseudoVersion recommends the revision to be a 12-byte
// commit hash prefix, which is what cmd/go uses as well.
if len(vcsRevision) > 12 {
vcsRevision = vcsRevision[:12]
}
}
}
if vcsRevision != "" {
version = module.PseudoVersion("", "", vcsTime, vcsRevision)
}
}

fmt.Fprintf(w, "cue version %s\n\n", version)
fmt.Fprintf(w, "go version %s\n", runtime.Version())
for _, s := range bi.Settings {
Expand All @@ -115,3 +78,66 @@ func runVersion(cmd *Command, args []string) error {
}
return nil
}

// 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
}
v := version
if v != "" {
// The global version variable has been
// configured via ldflags.
return v
}
if bi == nil {
return ""
}
if bi.Main.Version != "" && bi.Main.Version != defaultVersion {
v = bi.Main.Version
}
if v != "" {
return v
}
// a specific version was not provided by ldflags or buildInfo
// attempt to make our own
var vcsTime time.Time
var vcsRevision string
for _, s := range bi.Settings {
switch s.Key {
case "vcs.time":
// If the format is invalid, we'll print a zero timestamp.
vcsTime, _ = time.Parse(time.RFC3339Nano, s.Value)
case "vcs.revision":
vcsRevision = s.Value
// module.PseudoVersion recommends the revision to be a 12-byte
// commit hash prefix, which is what cmd/go uses as well.
if len(vcsRevision) > 12 {
vcsRevision = vcsRevision[:12]
}
}
}
if vcsRevision != "" {
return module.PseudoVersion("", "", vcsTime, vcsRevision)
}
return ""
}

func readBuildInfo() (*debug.BuildInfo, bool) {
bi, ok := debug.ReadBuildInfo()
if !ok || !inTest {
return bi, ok
}
// test-based overrides
if v := os.Getenv("CUE_VERSION_TEST_CFG"); v != "" {
var extra []debug.BuildSetting
if err := json.Unmarshal([]byte(v), &extra); err != nil {
// It's only for tests, so panic is OK.
panic(err)
}
bi.Settings = append(bi.Settings, extra...)
}
return bi, true
}

0 comments on commit 9ceec10

Please sign in to comment.