package capver //go:generate go run ../../tools/capver/main.go import ( "maps" "slices" "strings" "tailscale.com/tailcfg" "tailscale.com/util/cmpver" "tailscale.com/util/set" ) // minVersionParts is the minimum number of version parts needed for major.minor. const minVersionParts = 2 // CanOldCodeBeCleanedUp is called at server startup to panic when // [MinSupportedCapabilityVersion] has crossed a threshold at which a // backwards-compat emit path can be deleted. Each entry pairs a // [tailcfg.CapabilityVersion] threshold with the message identifying // the code to remove; today there are none. // // All capability-version-gated cleanups should be registered here. func CanOldCodeBeCleanedUp() { } func tailscaleVersSorted() []string { return slices.Sorted(maps.Keys(tailscaleToCapVer)) } // TailscaleVersion returns the Tailscale version for the given [tailcfg.CapabilityVersion]. func TailscaleVersion(ver tailcfg.CapabilityVersion) string { return capVerToTailscaleVer[ver] } // CapabilityVersion returns the [tailcfg.CapabilityVersion] for the given Tailscale version. // It accepts both full versions (v1.90.1) and minor versions (v1.90). func CapabilityVersion(ver string) tailcfg.CapabilityVersion { if !strings.HasPrefix(ver, "v") { ver = "v" + ver } // Try direct lookup first (works for minor versions like v1.90) if cv, ok := tailscaleToCapVer[ver]; ok { return cv } // Try extracting minor version from full version (v1.90.1 -> v1.90) parts := strings.Split(strings.TrimPrefix(ver, "v"), ".") if len(parts) >= minVersionParts { minor := "v" + parts[0] + "." + parts[1] return tailscaleToCapVer[minor] } return 0 } // TailscaleLatestMajorMinor returns the n latest Tailscale versions (e.g. 1.80). func TailscaleLatestMajorMinor(n int, stripV bool) []string { if n <= 0 { return nil } majors := set.Set[string]{} for _, vers := range tailscaleVersSorted() { if stripV { vers = strings.TrimPrefix(vers, "v") } v := strings.Split(vers, ".") majors.Add(v[0] + "." + v[1]) } majorSl := majors.Slice() // cmpver orders versions numerically, so v1.100 sorts after v1.98 rather than // lexically before it. slices.SortFunc(majorSl, cmpver.Compare) if n > len(majorSl) { return majorSl } return majorSl[len(majorSl)-n:] }