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  }