diff --git a/version/prop.go b/version/prop.go index 710c6aabe..4bb77717a 100644 --- a/version/prop.go +++ b/version/prop.go @@ -4,107 +4,35 @@ package version import ( - "os" - "path/filepath" - "runtime" - "strconv" "strings" - "sync" tailscaleroot "tailscale.com" - "tailscale.com/syncs" "tailscale.com/tailcfg" ) // IsMobile reports whether this is a mobile client build. -func IsMobile() bool { - return runtime.GOOS == "android" || runtime.GOOS == "ios" -} +func IsMobile() bool { return isMobile } // OS returns runtime.GOOS, except instead of returning "darwin" it // returns "iOS" or "macOS". -func OS() string { - if runtime.GOOS == "ios" { - return "iOS" - } - if runtime.GOOS == "darwin" { - return "macOS" - } - return runtime.GOOS -} +func OS() string { return legacyOS } // IsSandboxedMacOS reports whether this process is a sandboxed macOS // process (either the app or the extension). It is true for the Mac App Store // and macsys (System Extension) version on macOS, and false for // tailscaled-on-macOS. -func IsSandboxedMacOS() bool { - if runtime.GOOS != "darwin" { - return false - } - if IsMacSysExt() { - return true - } - exe, _ := os.Executable() - return strings.HasSuffix(exe, "/Contents/MacOS/Tailscale") || strings.HasSuffix(exe, "/Contents/MacOS/IPNExtension") -} - -var isMacSysExt syncs.AtomicValue[bool] +func IsSandboxedMacOS() bool { return isSandboxedMacOS } // IsMacSysExt whether this binary is from the standalone "System // Extension" (a.k.a. "macsys") version of Tailscale for macOS. -func IsMacSysExt() bool { - if runtime.GOOS != "darwin" { - return false - } - if b, ok := isMacSysExt.LoadOk(); ok { - return b - } - exe, err := os.Executable() - if err != nil { - return false - } - v := filepath.Base(exe) == "io.tailscale.ipn.macsys.network-extension" - isMacSysExt.Store(v) - return v -} +func IsMacSysExt() bool { return isMacSysExt } // IsWindowsGUI reports whether the current process is the Windows GUI. -func IsWindowsGUI() bool { - if runtime.GOOS != "windows" { - return false - } - exe, _ := os.Executable() - exe = filepath.Base(exe) - return strings.EqualFold(exe, "tailscale-ipn.exe") || strings.EqualFold(exe, "tailscale-ipn") -} - -var ( - isUnstableOnce sync.Once - isUnstableBuild bool -) +func IsWindowsGUI() bool { return isWindowsGUI } // IsUnstableBuild reports whether this is an unstable build. // That is, whether its minor version number is odd. -func IsUnstableBuild() bool { - isUnstableOnce.Do(initUnstable) - return isUnstableBuild -} - -func initUnstable() { - _, rest, ok := strings.Cut(Short, ".") - if !ok { - return - } - minorStr, _, ok := strings.Cut(rest, ".") - if !ok { - return - } - minor, err := strconv.Atoi(minorStr) - if err != nil { - return - } - isUnstableBuild = minor%2 == 1 -} +func IsUnstableBuild() bool { return isUnstable } // Meta is a JSON-serializable type that contains all the version // information. diff --git a/version/version.go b/version/version.go index 2028488de..ac0a274c5 100644 --- a/version/version.go +++ b/version/version.go @@ -5,34 +5,97 @@ package version import ( + "os" + "path/filepath" + "runtime" "runtime/debug" + "strconv" "strings" tailscaleroot "tailscale.com" ) -// Long is a full version number for this build, of the form -// "x.y.z-commithash" for builds stamped in the usual way (see -// build_dist.sh in the root) or, for binaries built by hand with the -// go tool, it's of the form "1.23.0-dev20220316-t29837428937{,-dirty}" -// where "1.23.0" comes from ../VERSION.txt and the part after dev -// is YYYYMMDD of the commit time, and the part after -t is the commit -// hash. The dirty suffix is whether there are uncommitted changes. -var Long = "" - -// Short is a short version number for this build, of the form -// "x.y.z" for builds stamped in the usual way (see -// build_dist.sh in the root) or, for binaries built by hand with the -// go tool, it's like Long's dev form, but ending at the date part, -// of the form "1.23.0-dev20220316". -var Short = "" +var ( + // Long is a full version number for this build, of the form + // "x.y.z-commithash" for builds stamped in the usual way (see build_dist.sh + // in the root) or, for binaries built by hand with the go tool, it's of the + // form "1.23.0-dev20220316-t29837428937{,-dirty}" where "1.23.0" comes from + // ../VERSION.txt and the part after dev is YYYYMMDD of the commit time, and + // the part after -t is the commit hash. The dirty suffix is whether there + // are uncommitted changes. + Long string + + // Short is a short version number for this build, of the form + // "x.y.z" for builds stamped in the usual way (see + // build_dist.sh in the root) or, for binaries built by hand with the + // go tool, it's like Long's dev form, but ending at the date part, + // of the form "1.23.0-dev20220316". + Short string + + // GitCommit, if non-empty, is the git commit of the + // github.com/tailscale/tailscale repository at which Tailscale was + // built. Its format is the one returned by `git describe --always + // --exclude "*" --dirty --abbrev=200`. + GitCommit string + + // GitDirty is whether Go stamped the binary as having dirty version + // control changes in the working directory (debug.ReadBuildInfo + // setting "vcs.modified" was true). + GitDirty bool + + // ExtraGitCommit, if non-empty, 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. + ExtraGitCommit = "" + + // isUnstable is whether the current build appears to be an unstable, i.e. with + // an odd minor version number. + isUnstable bool + + // legacyOS is runtime.GOOS, except on apple devices where it's either "iOS" or + // "macOS" (with that exact case). + // + // This used to be a thing because Go reported both macOS and iOS as "darwin" + // and we needed to tell them apart. But then Go learned GOOS=ios and + // GOOS=darwin as separate things, but we're still stuck with this function + // because of the odd casing we picked, which has ossified into databases. + legacyOS string + + // isMobile is whether the current build is for a mobile device. + isMobile bool + + // isSandboxedMacOS is whether the current binary is any binary in the mac store + // or standalone sysext mac apps. + isSandboxedMacOS bool + + // isMacSysExt is whether the current binary is the mac system extension binary. + isMacSysExt bool + + // isWindowsGUI is whether the current binary is the Windows GUI binary. + isWindowsGUI bool +) func init() { + initVersion() + initUnstable() + initMiscTraits() +} + +func initVersion() { if Long != "" && Short != "" { // Built in the recommended way, using build_dist.sh. return } + // Otherwise, make approximate version info using Go 1.18's built-in git + // stamping. bi, ok := debug.ReadBuildInfo() if !ok { Long = strings.TrimSpace(tailscaleroot.Version) + "-ERR-BuildInfo" @@ -67,25 +130,38 @@ func init() { Long = Short + "-t" + commitHashAbbrev + dirty } -// GitCommit, if non-empty, is the git commit of the -// github.com/tailscale/tailscale repository at which Tailscale was -// built. Its format is the one returned by `git describe --always -// --exclude "*" --dirty --abbrev=200`. -var GitCommit = "" - -// GitDirty is whether Go stamped the binary as having dirty version -// control changes in the working directory (debug.ReadBuildInfo -// setting "vcs.modified" was true). -var GitDirty bool - -// ExtraGitCommit, if non-empty, 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. -var ExtraGitCommit = "" +func initUnstable() { + _, rest, ok := strings.Cut(Short, ".") + if !ok { + return + } + minorStr, _, ok := strings.Cut(rest, ".") + if !ok { + return + } + minor, err := strconv.Atoi(minorStr) + if err != nil { + return + } + isUnstable = minor%2 == 1 +} + +func initMiscTraits() { + exe, _ := os.Executable() + base := filepath.Base(exe) + + legacyOS = runtime.GOOS + switch runtime.GOOS { + case "darwin": + legacyOS = "macOS" + isMacSysExt = strings.HasPrefix(base, "io.tailscale.ipn.macsys.network-extension") + isSandboxedMacOS = isMacSysExt || strings.HasSuffix(exe, "/Contents/MacOS/Tailscale") || strings.HasSuffix(exe, "/Contents/MacOS/IPNExtension") + case "ios": + legacyOS = "iOS" + isMobile = true + case "android": + isMobile = true + case "windows": + isWindowsGUI = strings.EqualFold(base, "tailscale-ipn.exe") || strings.EqualFold(base, "tailscale-ipn") + } +}