blob: c4decb54f77107caeb39c141ed1b1ec4b6b505e2 [file] [edit]
// Package versions contains functions to parse and sort Bazel version identifier.
package versions
import (
"fmt"
"log"
"regexp"
"sort"
"strconv"
"github.com/hashicorp/go-version"
)
const (
// BazelUpstream contains the name of the official Bazel GitHub organization.
BazelUpstream = "bazelbuild"
)
var (
releasePattern = regexp.MustCompile(`^(\d+)\.\d+\.\d+$`)
trackPattern = regexp.MustCompile(`^(\d+)\.(x|\*)$`)
patchPattern = regexp.MustCompile(`^(\d+\.\d+\.\d+)-([\w\d]+)$`)
candidatePattern = regexp.MustCompile(`^(\d+\.\d+\.\d+)rc(\d+)$`)
rollingPattern = regexp.MustCompile(`^\d+\.0\.0-pre\.\d{8}(\.\d+){1,2}$`)
latestReleasePattern = regexp.MustCompile(`^latest(?:-(?P<offset>\d+))?$`)
commitPattern = regexp.MustCompile(`^[a-z0-9]{40}$`)
)
// Info represents a structured Bazel version identifier.
type Info struct {
MustBeRelease, MustBeCandidate bool
IsLTS, IsRolling bool
IsCommit, IsFork, IsRelative bool
Fork, Value string
LatestOffset, TrackRestriction int
}
// Parse extracts and returns structured information about the given Bazel version label.
func Parse(fork, version string) (*Info, error) {
vi := &Info{Fork: fork, Value: version, IsFork: isFork(fork)}
if m := releasePattern.FindStringSubmatch(version); m != nil {
vi.IsLTS = true
vi.MustBeRelease = true
} else if m := trackPattern.FindStringSubmatch(version); m != nil {
track, err := strconv.Atoi(m[1])
if err != nil {
return nil, fmt.Errorf("invalid version %q, expected something like '5.x' or '5.*'", version)
}
vi.IsLTS = true
vi.MustBeRelease = (m[2] == "x")
vi.IsRelative = true
vi.TrackRestriction = track
} else if patchPattern.MatchString(version) {
vi.IsLTS = true
vi.MustBeRelease = true
} else if m := latestReleasePattern.FindStringSubmatch(version); m != nil {
vi.IsLTS = true
vi.MustBeRelease = true
vi.IsRelative = true
if m[1] != "" {
offset, err := strconv.Atoi(m[1])
if err != nil {
return nil, fmt.Errorf("invalid version %q, could not parse offset: %v", version, err)
}
vi.LatestOffset = offset
}
} else if candidatePattern.MatchString(version) {
vi.IsLTS = true
vi.MustBeCandidate = true
} else if version == "last_rc" {
vi.IsLTS = true
vi.MustBeCandidate = true
vi.IsRelative = true
} else if commitPattern.MatchString(version) {
vi.IsCommit = true
} else if version == "last_green" {
vi.IsCommit = true
vi.IsRelative = true
} else if rollingPattern.MatchString(version) {
vi.IsRolling = true
} else if version == "rolling" {
vi.IsRolling = true
vi.IsRelative = true
} else {
return nil, fmt.Errorf("invalid version '%s'", version)
}
return vi, nil
}
func isFork(value string) bool {
return value != "" && value != BazelUpstream
}
// GetInAscendingOrder returns the given versions sorted in ascending order.
func GetInAscendingOrder(versions []string) []string {
wrappers := make([]*version.Version, len(versions))
for i, v := range versions {
wrapper, err := version.NewVersion(v)
if err != nil {
log.Printf("WARN: Could not parse version: %s", v)
}
wrappers[i] = wrapper
}
sort.Sort(version.Collection(wrappers))
sorted := make([]string, len(versions))
for i, w := range wrappers {
sorted[i] = w.Original()
}
return sorted
}
func MatchCommitPattern(version string) bool {
return commitPattern.MatchString(version)
}
// IsCommit returns whether the given version refers to a commit.
func IsCommit(version string) bool {
return version == "last_green" || commitPattern.MatchString(version)
}