1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
// Package cueversion provides access to the version of the
// cuelang.org/go module.
package cueversion
import (
"fmt"
"runtime"
"runtime/debug"
"strings"
"sync"
"time"
"golang.org/x/mod/module"
)
// LanguageVersion returns the CUE language version.
// This determines the latest version of CUE that
// is accepted by the module.
func LanguageVersion() string {
return "v0.12.0"
}
// ModuleVersion returns the version of the cuelang.org/go module as best as can
// reasonably be determined. This is provided for informational
// and debugging purposes and should not be used to predicate
// version-specific behavior.
func ModuleVersion() string {
return moduleVersionOnce()
}
const cueModule = "cuelang.org/go"
var moduleVersionOnce = sync.OnceValue(func() string {
bi, ok := debug.ReadBuildInfo()
if !ok {
// This might happen if the binary was not built with module support
// or with an alternative toolchain.
return "(no-build-info)"
}
cueMod := findCUEModule(bi)
if cueMod == nil {
// Could happen if someone has forked CUE under a different
// module name; it also happens when running the cue tests.
return "(no-cue-module)"
}
version := cueMod.Version
if version != "(devel)" {
return version
}
// A specific version was not provided by the buildInfo
// so 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)
}
return version
})
func findCUEModule(bi *debug.BuildInfo) *debug.Module {
if bi.Main.Path == cueModule {
return &bi.Main
}
for _, m := range bi.Deps {
if m.Replace != nil && m.Replace.Path == cueModule {
return m.Replace
}
if m.Path == cueModule {
return m
}
}
return nil
}
// UserAgent returns a string suitable for adding as the User-Agent
// header in an HTTP agent. The clientType argument specifies
// how CUE is being used: if this is empty it defaults to "cuelang.org/go".
//
// Example:
//
// Cue/v0.8.0 (cuelang.org/go; vxXXX) Go/go1.22.0 (linux/amd64)
func UserAgent(clientType string) string {
if clientType == "" {
clientType = "cuelang.org/go"
}
// The Go version can contain spaces, but we don't want spaces inside
// Component/Version pair, so replace them with underscores.
// As the runtime version won't contain underscores itself, this
// is reversible.
goVersion := strings.ReplaceAll(runtime.Version(), " ", "_")
return fmt.Sprintf("Cue/%s (%s; lang %s) Go/%s (%s/%s)", ModuleVersion(), clientType, LanguageVersion(), goVersion, runtime.GOOS, runtime.GOARCH)
}
|