Skip to content

Commit

Permalink
Go: Use SemVer type in autobuilder package
Browse files Browse the repository at this point in the history
  • Loading branch information
mbg committed May 8, 2024
1 parent 0829cd0 commit 68b96d8
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 114 deletions.
2 changes: 1 addition & 1 deletion go/extractor/autobuilder/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

162 changes: 78 additions & 84 deletions go/extractor/autobuilder/build-environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,72 +8,70 @@ import (
"github.com/github/codeql-go/extractor/diagnostics"
"github.com/github/codeql-go/extractor/project"
"github.com/github/codeql-go/extractor/toolchain"
"golang.org/x/mod/semver"
"github.com/github/codeql-go/extractor/util"
)

const minGoVersion = "1.11"
const maxGoVersion = "1.22"
var minGoVersion = util.NewSemVer("1.11")
var maxGoVersion = util.NewSemVer("1.22")

type versionInfo struct {
goModVersion string // The version of Go found in the go directive in the `go.mod` file.
goModVersionFound bool // Whether a `go` directive was found in the `go.mod` file.
goEnvVersion string // The version of Go found in the environment.
goEnvVersionFound bool // Whether an installation of Go was found in the environment.
goModVersion util.SemVer // The version of Go found in the go directive in the `go.mod` file.
goEnvVersion util.SemVer // The version of Go found in the environment.
}

func (v versionInfo) String() string {
return fmt.Sprintf(
"go.mod version: %s, go.mod directive found: %t, go env version: %s, go installation found: %t",
v.goModVersion, v.goModVersionFound, v.goEnvVersion, v.goEnvVersionFound)
"go.mod version: %s, go env version: %s",
v.goModVersion, v.goEnvVersion)
}

// Check if `version` is lower than `minGoVersion`. Note that for this comparison we ignore the
// patch part of the version, so 1.20.1 and 1.20 are considered equal.
func belowSupportedRange(version string) bool {
return semver.Compare(semver.MajorMinor("v"+version), "v"+minGoVersion) < 0
func belowSupportedRange(version util.SemVer) bool {
return version.MajorMinor().IsOlderThan(minGoVersion.MajorMinor())
}

// Check if `version` is higher than `maxGoVersion`. Note that for this comparison we ignore the
// patch part of the version, so 1.20.1 and 1.20 are considered equal.
func aboveSupportedRange(version string) bool {
return semver.Compare(semver.MajorMinor("v"+version), "v"+maxGoVersion) > 0
func aboveSupportedRange(version util.SemVer) bool {
return version.MajorMinor().IsNewerThan(maxGoVersion.MajorMinor())
}

// Check if `version` is lower than `minGoVersion` or higher than `maxGoVersion`. Note that for
// this comparison we ignore the patch part of the version, so 1.20.1 and 1.20 are considered
// equal.
func outsideSupportedRange(version string) bool {
func outsideSupportedRange(version util.SemVer) bool {
return belowSupportedRange(version) || aboveSupportedRange(version)
}

// Assuming `v.goModVersionFound` is false, emit a diagnostic and return the version to install,
// or the empty string if we should not attempt to install a version of Go.
func getVersionWhenGoModVersionNotFound(v versionInfo) (msg, version string) {
if !v.goEnvVersionFound {
func getVersionWhenGoModVersionNotFound(v versionInfo) (msg string, version util.SemVer) {
if v.goEnvVersion == nil {
// There is no Go version installed in the environment. We have no indication which version
// was intended to be used to build this project. Go versions are generally backwards
// compatible, so we install the maximum supported version.
msg = "No version of Go installed and no `go.mod` file found. Requesting the maximum " +
"supported version of Go (" + maxGoVersion + ")."
"supported version of Go (" + maxGoVersion.String() + ")."
version = maxGoVersion
diagnostics.EmitNoGoModAndNoGoEnv(msg)
} else if outsideSupportedRange(v.goEnvVersion) {
// The Go version installed in the environment is not supported. We have no indication
// which version was intended to be used to build this project. Go versions are generally
// backwards compatible, so we install the maximum supported version.
msg = "No `go.mod` file found. The version of Go installed in the environment (" +
v.goEnvVersion + ") is outside of the supported range (" + minGoVersion + "-" +
maxGoVersion + "). Requesting the maximum supported version of Go (" + maxGoVersion +
v.goEnvVersion.String() + ") is outside of the supported range (" + minGoVersion.String() + "-" +
maxGoVersion.String() + "). Requesting the maximum supported version of Go (" + maxGoVersion.String() +
")."
version = maxGoVersion
diagnostics.EmitNoGoModAndGoEnvUnsupported(msg)
} else {
// The version of Go that is installed is supported. We have no indication which version
// was intended to be used to build this project. We assume that the installed version is
// suitable and do not install a version of Go.
msg = "No `go.mod` file found. Version " + v.goEnvVersion + " installed in the " +
msg = "No `go.mod` file found. Version " + v.goEnvVersion.String() + " installed in the " +
"environment is supported. Not requesting any version of Go."
version = ""
version = nil
diagnostics.EmitNoGoModAndGoEnvSupported(msg)
}

Expand All @@ -82,57 +80,57 @@ func getVersionWhenGoModVersionNotFound(v versionInfo) (msg, version string) {

// Assuming `v.goModVersion` is above the supported range, emit a diagnostic and return the
// version to install, or the empty string if we should not attempt to install a version of Go.
func getVersionWhenGoModVersionTooHigh(v versionInfo) (msg, version string) {
if !v.goEnvVersionFound {
func getVersionWhenGoModVersionTooHigh(v versionInfo) (msg string, version util.SemVer) {
if v.goEnvVersion == nil {
// The version in the `go.mod` file is above the supported range. There is no Go version
// installed. We install the maximum supported version as a best effort.
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion.String() +
") is above the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). No version of Go installed. Requesting the maximum supported version of Go (" +
maxGoVersion + ")."
maxGoVersion.String() + ")."
version = maxGoVersion
diagnostics.EmitGoModVersionTooHighAndNoGoEnv(msg)
} else if aboveSupportedRange(v.goEnvVersion) {
// The version in the `go.mod` file is above the supported range. The version of Go that
// is installed is above the supported range. We do not install a version of Go.
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
"). The version of Go installed in the environment (" + v.goEnvVersion +
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion.String() +
") is above the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is above the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). Not requesting any version of Go."
version = ""
version = nil
diagnostics.EmitGoModVersionTooHighAndEnvVersionTooHigh(msg)
} else if belowSupportedRange(v.goEnvVersion) {
// The version in the `go.mod` file is above the supported range. The version of Go that
// is installed is below the supported range. We install the maximum supported version as
// a best effort.
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
"). The version of Go installed in the environment (" + v.goEnvVersion +
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
"). Requesting the maximum supported version of Go (" + maxGoVersion + ")."
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion.String() +
") is above the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is below the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). Requesting the maximum supported version of Go (" + maxGoVersion.String() + ")."
version = maxGoVersion
diagnostics.EmitGoModVersionTooHighAndEnvVersionTooLow(msg)
} else if semver.Compare("v"+maxGoVersion, "v"+v.goEnvVersion) > 0 {
} else if maxGoVersion.IsNewerThan(v.goEnvVersion) {
// The version in the `go.mod` file is above the supported range. The version of Go that
// is installed is supported and below the maximum supported version. We install the
// maximum supported version as a best effort.
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
"). The version of Go installed in the environment (" + v.goEnvVersion +
") is below the maximum supported version (" + maxGoVersion +
"). Requesting the maximum supported version of Go (" + maxGoVersion + ")."
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion.String() +
") is above the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is below the maximum supported version (" + maxGoVersion.String() +
"). Requesting the maximum supported version of Go (" + maxGoVersion.String() + ")."
version = maxGoVersion
diagnostics.EmitGoModVersionTooHighAndEnvVersionBelowMax(msg)
} else {
// The version in the `go.mod` file is above the supported range. The version of Go that
// is installed is the maximum supported version. We do not install a version of Go.
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
"). The version of Go installed in the environment (" + v.goEnvVersion +
") is the maximum supported version (" + maxGoVersion +
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion.String() +
") is above the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is the maximum supported version (" + maxGoVersion.String() +
"). Not requesting any version of Go."
version = ""
version = nil
diagnostics.EmitGoModVersionTooHighAndEnvVersionMax(msg)
}

Expand All @@ -141,35 +139,35 @@ func getVersionWhenGoModVersionTooHigh(v versionInfo) (msg, version string) {

// Assuming `v.goModVersion` is below the supported range, emit a diagnostic and return the
// version to install, or the empty string if we should not attempt to install a version of Go.
func getVersionWhenGoModVersionTooLow(v versionInfo) (msg, version string) {
if !v.goEnvVersionFound {
func getVersionWhenGoModVersionTooLow(v versionInfo) (msg string, version util.SemVer) {
if v.goEnvVersion == nil {
// There is no Go version installed. The version in the `go.mod` file is below the
// supported range. Go versions are generally backwards compatible, so we install the
// minimum supported version.
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion.String() +
") is below the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). No version of Go installed. Requesting the minimum supported version of Go (" +
minGoVersion + ")."
minGoVersion.String() + ")."
version = minGoVersion
diagnostics.EmitGoModVersionTooLowAndNoGoEnv(msg)
} else if outsideSupportedRange(v.goEnvVersion) {
// The version of Go that is installed is outside of the supported range. The version
// in the `go.mod` file is below the supported range. Go versions are generally
// backwards compatible, so we install the minimum supported version.
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
"). The version of Go installed in the environment (" + v.goEnvVersion +
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + "). " +
"Requesting the minimum supported version of Go (" + minGoVersion + ")."
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion.String() +
") is below the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() +
"). The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is outside of the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() + "). " +
"Requesting the minimum supported version of Go (" + minGoVersion.String() + ")."
version = minGoVersion
diagnostics.EmitGoModVersionTooLowAndEnvVersionUnsupported(msg)
} else {
// The version of Go that is installed is supported. The version in the `go.mod` file is
// below the supported range. We do not install a version of Go.
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
msg = "The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is supported and is high enough for the version found in the `go.mod` file (" +
v.goModVersion + "). Not requesting any version of Go."
version = ""
v.goModVersion.String() + "). Not requesting any version of Go."
version = nil
diagnostics.EmitGoModVersionTooLowAndEnvVersionSupported(msg)
}

Expand All @@ -178,40 +176,40 @@ func getVersionWhenGoModVersionTooLow(v versionInfo) (msg, version string) {

// Assuming `v.goModVersion` is in the supported range, emit a diagnostic and return the version
// to install, or the empty string if we should not attempt to install a version of Go.
func getVersionWhenGoModVersionSupported(v versionInfo) (msg, version string) {
if !v.goEnvVersionFound {
func getVersionWhenGoModVersionSupported(v versionInfo) (msg string, version util.SemVer) {
if v.goEnvVersion == nil {
// There is no Go version installed. The version in the `go.mod` file is supported.
// We install the version from the `go.mod` file.
msg = "No version of Go installed. Requesting the version of Go found in the `go.mod` " +
"file (" + v.goModVersion + ")."
"file (" + v.goModVersion.String() + ")."
version = v.goModVersion
diagnostics.EmitGoModVersionSupportedAndNoGoEnv(msg)
} else if outsideSupportedRange(v.goEnvVersion) {
// The version of Go that is installed is outside of the supported range. The version in
// the `go.mod` file is supported. We install the version from the `go.mod` file.
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + "). " +
msg = "The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is outside of the supported range (" + minGoVersion.String() + "-" + maxGoVersion.String() + "). " +
"Requesting the version of Go from the `go.mod` file (" +
v.goModVersion + ")."
v.goModVersion.String() + ")."
version = v.goModVersion
diagnostics.EmitGoModVersionSupportedAndGoEnvUnsupported(msg)
} else if semver.Compare("v"+v.goModVersion, "v"+v.goEnvVersion) > 0 {
} else if v.goModVersion.IsNewerThan(v.goEnvVersion) {
// The version of Go that is installed is supported. The version in the `go.mod` file is
// supported and is higher than the version that is installed. We install the version from
// the `go.mod` file.
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
") is lower than the version found in the `go.mod` file (" + v.goModVersion +
"). Requesting the version of Go from the `go.mod` file (" + v.goModVersion + ")."
msg = "The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is lower than the version found in the `go.mod` file (" + v.goModVersion.String() +
"). Requesting the version of Go from the `go.mod` file (" + v.goModVersion.String() + ")."
version = v.goModVersion
diagnostics.EmitGoModVersionSupportedHigherGoEnv(msg)
} else {
// The version of Go that is installed is supported. The version in the `go.mod` file is
// supported and is lower than or equal to the version that is installed. We do not install
// a version of Go.
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
msg = "The version of Go installed in the environment (" + v.goEnvVersion.String() +
") is supported and is high enough for the version found in the `go.mod` file (" +
v.goModVersion + "). Not requesting any version of Go."
version = ""
v.goModVersion.String() + "). Not requesting any version of Go."
version = nil
diagnostics.EmitGoModVersionSupportedLowerEqualGoEnv(msg)
}

Expand All @@ -231,8 +229,8 @@ func getVersionWhenGoModVersionSupported(v versionInfo) (msg, version string) {
// | *In supported range* | No action | No action | Install version from go.mod if newer than installed | Install max supported if newer than installed |
// | *Above max supported* | Install max supported | Install min supported | Install version from go.mod | No action |
// +-----------------------+-----------------------+-----------------------+-----------------------------------------------------+------------------------------------------------+
func getVersionToInstall(v versionInfo) (msg, version string) {
if !v.goModVersionFound {
func getVersionToInstall(v versionInfo) (msg string, version util.SemVer) {
if v.goModVersion == nil {
return getVersionWhenGoModVersionNotFound(v)
}

Expand All @@ -249,12 +247,12 @@ func getVersionToInstall(v versionInfo) (msg, version string) {

// Output some JSON to stdout specifying the version of Go to install, unless `version` is the
// empty string.
func outputEnvironmentJson(version string) {
func outputEnvironmentJson(version util.SemVer) {
var content string
if version == "" {
if version == nil {
content = `{ "go": {} }`
} else {
content = `{ "go": { "version": "` + version + `" } }`
content = `{ "go": { "version": "` + version.String() + `" } }`
}
_, err := fmt.Fprint(os.Stdout, content)

Expand All @@ -273,14 +271,10 @@ func IdentifyEnvironment() {
defer project.RemoveTemporaryExtractorFiles()

// Find the greatest Go version required by any of the workspaces.
greatestGoVersion := project.RequiredGoVersion(&workspaces)
v.goModVersion, v.goModVersionFound = greatestGoVersion.String(), greatestGoVersion != nil
v.goModVersion = project.RequiredGoVersion(&workspaces)

// Find which, if any, version of Go is installed on the system already.
v.goEnvVersionFound = toolchain.IsInstalled()
if v.goEnvVersionFound {
v.goEnvVersion = toolchain.GetEnvGoVersion()[2:]
}
v.goEnvVersion = toolchain.GetEnvGoSemVer()

// Determine which version of Go we should recommend to install.
msg, versionToInstall := getVersionToInstall(v)
Expand Down

0 comments on commit 68b96d8

Please sign in to comment.