mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
142 lines
4.9 KiB
Go
142 lines
4.9 KiB
Go
package util
|
|
|
|
import (
|
|
"log"
|
|
"strings"
|
|
|
|
"golang.org/x/mod/semver"
|
|
)
|
|
|
|
// A type used to represent values known to be valid semantic versions.
|
|
type SemVer interface {
|
|
String() string
|
|
// Compares this semantic version against the `other`. Returns the following values:
|
|
//
|
|
// 0 if both versions are equal.
|
|
//
|
|
// -1 if this version is older than the `other`.
|
|
//
|
|
// 1 if this version is newer than the `other`.
|
|
Compare(other SemVer) int
|
|
// Returns true if this version is newer than the `other`, or false otherwise.
|
|
IsNewerThan(other SemVer) bool
|
|
// Returns true if this version is equal to `other` or newer, or false otherwise.
|
|
IsAtLeast(other SemVer) bool
|
|
// Returns true if this version is older than the `other`, or false otherwise.
|
|
IsOlderThan(other SemVer) bool
|
|
// Returns true if this version is equal to `other` or older, or false otherwise.
|
|
IsAtMost(other SemVer) bool
|
|
// Returns the `major.minor` version prefix of the semantic version. For example, "v1.2.3" becomes "v1.2".
|
|
MajorMinor() SemVer
|
|
// Renders the semantic version as a standard version string, i.e. without a leading "v".
|
|
StandardSemVer() string
|
|
}
|
|
|
|
// The internal representation used for values known to be valid semantic versions.
|
|
//
|
|
// NOTE: Not exported to prevent invalid values from being constructed.
|
|
type semVer string
|
|
|
|
// Converts the semantic version to a string representation.
|
|
func (ver semVer) String() string {
|
|
return string(ver)
|
|
}
|
|
|
|
// Represents `v0.0.0`.
|
|
func Zero() SemVer {
|
|
return semVer("v0.0.0")
|
|
}
|
|
|
|
// Constructs a [SemVer] from the given `version` string. The input can be any valid version string
|
|
// that we commonly deal with. This includes ordinary version strings such as "1.2.3", ones with
|
|
// the "go" prefix, and ones with the "v" prefix. Go's non-semver-compliant release candidate
|
|
// versions are also automatically corrected from e.g. "go1.20rc1" to "v1.20-rc1". If given
|
|
// the empty string, this function return `nil`. Otherwise, for invalid version strings, the function
|
|
// prints a message to the log and exits the process.
|
|
//
|
|
// Note that we deliberately do not format the resulting [SemVer] to be in a `Canonical` representation.
|
|
// This is because we want to maintain the input version specificity for as long as possible. This is useful
|
|
// for e.g. `IdentifyEnvironment` where we want to output "1.22" if the project specifies "1.22" as the
|
|
// required Go version, rather than outputting "1.22.0", which implies a specific patch-level version
|
|
// when the intention is that any patch-level version of "1.22" is acceptable.
|
|
func NewSemVer(version string) SemVer {
|
|
// If the input is the empty string, return `nil` since we use `nil` to represent "no version".
|
|
if version == "" {
|
|
return nil
|
|
}
|
|
|
|
// Drop a "go" prefix, if there is one.
|
|
version = strings.TrimPrefix(version, "go")
|
|
|
|
// Go versions don't follow the SemVer format, but the only exception we normally care about
|
|
// is release candidates; so this is a horrible hack to convert e.g. `1.22rc1` into `1.22-rc1`
|
|
// which is compatible with the SemVer specification.
|
|
rcIndex := strings.Index(version, "rc")
|
|
if rcIndex != -1 {
|
|
var numeric string
|
|
prerelease := version[rcIndex:]
|
|
|
|
// the version string may already contain a "-";
|
|
// if it does, drop the "-" since we add it back later
|
|
if version[rcIndex-1] != '-' {
|
|
numeric = version[:rcIndex]
|
|
} else {
|
|
numeric = version[:rcIndex-1]
|
|
}
|
|
|
|
// add a "v" to the numeric part of the version, if it's not already there
|
|
if !strings.HasPrefix(numeric, "v") {
|
|
numeric = "v" + numeric
|
|
}
|
|
|
|
// for the semver library to accept a version containing a prerelease,
|
|
// the numeric part must be canonical; e.g.. "v0-rc1" is not valid and
|
|
// must be "v0.0.0-rc1" instead.
|
|
version = semver.Canonical(numeric) + "-" + prerelease
|
|
} else if !strings.HasPrefix(version, "v") {
|
|
// Add the "v" prefix that is required by the `semver` package, if
|
|
// it's not already there.
|
|
version = "v" + version
|
|
}
|
|
|
|
// Check that the remaining version string is valid.
|
|
if !semver.IsValid(version) {
|
|
log.Fatalf("%s is not a valid version string\n", version)
|
|
}
|
|
|
|
return semVer(version)
|
|
}
|
|
|
|
func (ver semVer) Compare(other SemVer) int {
|
|
return semver.Compare(string(ver), string(other.String()))
|
|
}
|
|
|
|
func (ver semVer) IsNewerThan(other SemVer) bool {
|
|
return ver.Compare(other) > 0
|
|
}
|
|
|
|
func (ver semVer) IsAtLeast(other SemVer) bool {
|
|
return ver.Compare(other) >= 0
|
|
}
|
|
|
|
func (ver semVer) IsOlderThan(other SemVer) bool {
|
|
return ver.Compare(other) < 0
|
|
}
|
|
|
|
func (ver semVer) IsAtMost(other SemVer) bool {
|
|
return ver.Compare(other) <= 0
|
|
}
|
|
|
|
func (ver semVer) MajorMinor() SemVer {
|
|
return semVer(semver.MajorMinor(string(ver)))
|
|
}
|
|
|
|
func (ver semVer) StandardSemVer() string {
|
|
// Drop the 'v' prefix from the version string.
|
|
result := string(ver)[1:]
|
|
|
|
// Correct the pre-release identifier for use with `setup-go`, if one is present.
|
|
// This still remains a standard semantic version.
|
|
return strings.Replace(result, "-rc", "-rc.", 1)
|
|
}
|