package main import ( "fmt" "regexp" "strconv" "strings" ) // version holds a parsed semver version. type version struct { Major int Minor int Patch int } var semverRe = regexp.MustCompile(`^v(\d+)\.(\d+)\.(\d+)$`) func parseVersion(s string) (version, bool) { m := semverRe.FindStringSubmatch(s) if m == nil { return version{}, false } maj, _ := strconv.Atoi(m[1]) mnr, _ := strconv.Atoi(m[2]) pat, _ := strconv.Atoi(m[3]) return version{Major: maj, Minor: mnr, Patch: pat}, true } func (v version) String() string { return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Patch) } func (v version) GreaterThan(b version) bool { if v.Major != b.Major { return v.Major > b.Major } if v.Minor != b.Minor { return v.Minor > b.Minor } return v.Patch > b.Patch } func (v version) Equal(b version) bool { return v.Major == b.Major && v.Minor == b.Minor && v.Patch == b.Patch } // allSemverTags returns all semver tags sorted descending. func allSemverTags() ([]version, error) { out, err := gitOutput("tag", "--sort=-v:refname") if err != nil { return nil, err } if out == "" { return nil, nil } var tags []version for _, line := range strings.Split(out, "\n") { if v, ok := parseVersion(strings.TrimSpace(line)); ok { tags = append(tags, v) } } return tags, nil } // mergedSemverTags returns semver tags reachable from HEAD, sorted // descending. func mergedSemverTags() ([]version, error) { out, err := gitOutput("tag", "--merged", "HEAD", "--sort=-v:refname") if err != nil { return nil, err } if out == "" { return nil, nil } var tags []version for _, line := range strings.Split(out, "\n") { if v, ok := parseVersion(strings.TrimSpace(line)); ok { tags = append(tags, v) } } return tags, nil }