mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 08:11:32 +01:00 
			
		
		
		
	sync.OnceValue and slices.Compact were both added in Go 1.21. cmp.Or was added in Go 1.22. Updates #8632 Updates #11058 Change-Id: I89ba4c404f40188e1f8a9566c8aaa049be377754 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			213 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| // Package version provides the version that the binary was built at.
 | |
| package version
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"runtime/debug"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 
 | |
| 	tailscaleroot "tailscale.com"
 | |
| 	"tailscale.com/types/lazy"
 | |
| )
 | |
| 
 | |
| // Stamp vars can have their value set at build time by linker flags (see
 | |
| // build_dist.sh for an example). When set, these stamps serve as additional
 | |
| // inputs to computing the binary's version as returned by the functions in this
 | |
| // package.
 | |
| //
 | |
| // All stamps are optional.
 | |
| var (
 | |
| 	// longStamp is the full version identifier of the build. If set, it is
 | |
| 	// returned verbatim by Long() and other functions that return Long()'s
 | |
| 	// output.
 | |
| 	longStamp string
 | |
| 
 | |
| 	// shortStamp is the short version identifier of the build. If set, it
 | |
| 	// is returned verbatim by Short() and other functions that return Short()'s
 | |
| 	// output.
 | |
| 	shortStamp string
 | |
| 
 | |
| 	// gitCommitStamp is the git commit of the github.com/tailscale/tailscale
 | |
| 	// repository at which Tailscale was built. Its format is the one returned
 | |
| 	// by `git rev-parse <commit>`. If set, it is used instead of any git commit
 | |
| 	// information embedded by the Go tool.
 | |
| 	gitCommitStamp string
 | |
| 
 | |
| 	// gitDirtyStamp is whether the git checkout from which the code was built
 | |
| 	// was dirty. Its value is ORed with the dirty bit embedded by the Go tool.
 | |
| 	//
 | |
| 	// We need this because when we build binaries from another repo that
 | |
| 	// imports tailscale.com, the Go tool doesn't stamp any dirtiness info into
 | |
| 	// the binary. Instead, we have to inject the dirty bit ourselves here.
 | |
| 	gitDirtyStamp bool
 | |
| 
 | |
| 	// extraGitCommit, is the git commit of a "supplemental" repository at which
 | |
| 	// Tailscale was built. Its format is the same as gitCommit.
 | |
| 	//
 | |
| 	// extraGitCommit is used to track the source revision when the main
 | |
| 	// Tailscale repository is integrated into and built from another repository
 | |
| 	// (for example, Tailscale's proprietary code, or the Android OSS
 | |
| 	// repository). Together, gitCommit and extraGitCommit exactly describe what
 | |
| 	// repositories and commits were used in a build.
 | |
| 	extraGitCommitStamp string
 | |
| )
 | |
| 
 | |
| var long lazy.SyncValue[string]
 | |
| 
 | |
| // Long returns a full version number for this build, of one of the forms:
 | |
| //
 | |
| //   - "x.y.z-commithash-otherhash" for release builds distributed by Tailscale
 | |
| //   - "x.y.z-commithash" for release builds built with build_dist.sh
 | |
| //   - "x.y.z-changecount-commithash-otherhash" for untagged release branch
 | |
| //     builds by Tailscale (these are not distributed).
 | |
| //   - "x.y.z-changecount-commithash" for untagged release branch builds
 | |
| //     built with build_dist.sh
 | |
| //   - "x.y.z-devYYYYMMDD-commithash{,-dirty}" for builds made with plain "go
 | |
| //     build" or "go install"
 | |
| //   - "x.y.z-ERR-BuildInfo" for builds made by plain "go run"
 | |
| func Long() string {
 | |
| 	return long.Get(func() string {
 | |
| 		if longStamp != "" {
 | |
| 			return longStamp
 | |
| 		}
 | |
| 		bi := getEmbeddedInfo()
 | |
| 		if !bi.valid {
 | |
| 			return strings.TrimSpace(tailscaleroot.VersionDotTxt) + "-ERR-BuildInfo"
 | |
| 		}
 | |
| 		return fmt.Sprintf("%s-dev%s-t%s%s", strings.TrimSpace(tailscaleroot.VersionDotTxt), bi.commitDate, bi.commitAbbrev(), dirtyString())
 | |
| 	})
 | |
| }
 | |
| 
 | |
| var short lazy.SyncValue[string]
 | |
| 
 | |
| // Short returns a short version number for this build, of the forms:
 | |
| //
 | |
| //   - "x.y.z" for builds distributed by Tailscale or built with build_dist.sh
 | |
| //   - "x.y.z-devYYYYMMDD" for builds made with plain "go build" or "go install"
 | |
| //   - "x.y.z-ERR-BuildInfo" for builds made by plain "go run"
 | |
| func Short() string {
 | |
| 	return short.Get(func() string {
 | |
| 		if shortStamp != "" {
 | |
| 			return shortStamp
 | |
| 		}
 | |
| 		bi := getEmbeddedInfo()
 | |
| 		if !bi.valid {
 | |
| 			return strings.TrimSpace(tailscaleroot.VersionDotTxt) + "-ERR-BuildInfo"
 | |
| 		}
 | |
| 		return strings.TrimSpace(tailscaleroot.VersionDotTxt) + "-dev" + bi.commitDate
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type embeddedInfo struct {
 | |
| 	valid      bool
 | |
| 	commit     string
 | |
| 	commitDate string
 | |
| 	commitTime string
 | |
| 	dirty      bool
 | |
| }
 | |
| 
 | |
| func (i embeddedInfo) commitAbbrev() string {
 | |
| 	if len(i.commit) >= 9 {
 | |
| 		return i.commit[:9]
 | |
| 	}
 | |
| 	return i.commit
 | |
| }
 | |
| 
 | |
| var getEmbeddedInfo = sync.OnceValue(func() embeddedInfo {
 | |
| 	bi, ok := debug.ReadBuildInfo()
 | |
| 	if !ok {
 | |
| 		return embeddedInfo{}
 | |
| 	}
 | |
| 	ret := embeddedInfo{valid: true}
 | |
| 	for _, s := range bi.Settings {
 | |
| 		switch s.Key {
 | |
| 		case "vcs.revision":
 | |
| 			ret.commit = s.Value
 | |
| 		case "vcs.time":
 | |
| 			ret.commitTime = s.Value
 | |
| 			if len(s.Value) >= len("yyyy-mm-dd") {
 | |
| 				ret.commitDate = s.Value[:len("yyyy-mm-dd")]
 | |
| 				ret.commitDate = strings.ReplaceAll(ret.commitDate, "-", "")
 | |
| 			}
 | |
| 		case "vcs.modified":
 | |
| 			ret.dirty = s.Value == "true"
 | |
| 		}
 | |
| 	}
 | |
| 	if ret.commit == "" || ret.commitDate == "" {
 | |
| 		// Build info is present in the binary, but has no useful data. Act as
 | |
| 		// if it's missing.
 | |
| 		return embeddedInfo{}
 | |
| 	}
 | |
| 	return ret
 | |
| })
 | |
| 
 | |
| func gitCommit() string {
 | |
| 	if gitCommitStamp != "" {
 | |
| 		return gitCommitStamp
 | |
| 	}
 | |
| 	return getEmbeddedInfo().commit
 | |
| }
 | |
| 
 | |
| func gitDirty() bool {
 | |
| 	if gitDirtyStamp {
 | |
| 		return true
 | |
| 	}
 | |
| 	return getEmbeddedInfo().dirty
 | |
| }
 | |
| 
 | |
| func dirtyString() string {
 | |
| 	if gitDirty() {
 | |
| 		return "-dirty"
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| func majorMinorPatch() string {
 | |
| 	ret, _, _ := strings.Cut(Short(), "-")
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func isValidLongWithTwoRepos(v string) bool {
 | |
| 	s := strings.Split(v, "-")
 | |
| 	if len(s) != 3 {
 | |
| 		return false
 | |
| 	}
 | |
| 	hexChunk := func(s string) bool {
 | |
| 		if len(s) < 6 {
 | |
| 			return false
 | |
| 		}
 | |
| 		for i := range len(s) {
 | |
| 			b := s[i]
 | |
| 			if (b < '0' || b > '9') && (b < 'a' || b > 'f') {
 | |
| 				return false
 | |
| 			}
 | |
| 		}
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	v, t, g := s[0], s[1], s[2]
 | |
| 	if !strings.HasPrefix(t, "t") || !strings.HasPrefix(g, "g") ||
 | |
| 		!hexChunk(t[1:]) || !hexChunk(g[1:]) {
 | |
| 		return false
 | |
| 	}
 | |
| 	nums := strings.Split(v, ".")
 | |
| 	if len(nums) != 3 {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i, n := range nums {
 | |
| 		bits := 8
 | |
| 		if i == 2 {
 | |
| 			bits = 16
 | |
| 		}
 | |
| 		if _, err := strconv.ParseUint(n, 10, bits); err != nil {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 |