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