// Package clientver parses and compares the leading MAJOR.MINOR.PATCH of a client // version string so the edge can turn away a build too old to speak the current wire // contract. It is deliberately dependency-free and tolerant: the version rides an HTTP // header (X-Client-Version) that a build stamps from `git describe --tags`, so any // `-N-gSHA` or `+meta` suffix is ignored, and anything unparseable is reported as such // (the caller fails open — an absent or garbled header is never treated as too old). package clientver import ( "strconv" "strings" ) // Version is a parsed semantic version triple. Only MAJOR.MINOR.PATCH participate in the // ordering; any pre-release or build suffix is dropped at parse time. type Version struct { Major, Minor, Patch int } // Parse extracts the leading MAJOR.MINOR.PATCH from s, tolerating an optional leading // "v" and any `-N-gSHA` or `+meta` suffix (as produced by `git describe --tags`). It // reports ok=false when s has fewer than three numeric components or any component is not // an integer, so the caller can distinguish a real version from a dev/empty string. func Parse(s string) (Version, bool) { s = strings.TrimPrefix(strings.TrimSpace(s), "v") if i := strings.IndexAny(s, "-+"); i >= 0 { s = s[:i] } p := strings.SplitN(s, ".", 4) if len(p) < 3 { return Version{}, false } var v Version var err error if v.Major, err = strconv.Atoi(p[0]); err != nil { return Version{}, false } if v.Minor, err = strconv.Atoi(p[1]); err != nil { return Version{}, false } if v.Patch, err = strconv.Atoi(p[2]); err != nil { return Version{}, false } return v, true } // Less reports whether a orders before b by MAJOR, then MINOR, then PATCH. Equal versions // are not Less than each other, so a client exactly at the minimum passes the gate. func Less(a, b Version) bool { if a.Major != b.Major { return a.Major < b.Major } if a.Minor != b.Minor { return a.Minor < b.Minor } return a.Patch < b.Patch }