From cf1aab0157b3ffa0139cd58c910854f5e3dccd2c Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Tue, 23 Jan 2024 13:46:33 +0000 Subject: [PATCH] Go: Move identify environment code to separate file --- go/extractor/autobuilder/build-environment.go | 283 ++++++++++++++++++ .../autobuilder/build-environment_test.go | 48 +++ .../cli/go-autobuilder/go-autobuilder.go | 273 +---------------- .../cli/go-autobuilder/go-autobuilder_test.go | 45 --- 4 files changed, 332 insertions(+), 317 deletions(-) create mode 100644 go/extractor/autobuilder/build-environment.go create mode 100644 go/extractor/autobuilder/build-environment_test.go diff --git a/go/extractor/autobuilder/build-environment.go b/go/extractor/autobuilder/build-environment.go new file mode 100644 index 000000000000..485786ed946e --- /dev/null +++ b/go/extractor/autobuilder/build-environment.go @@ -0,0 +1,283 @@ +package autobuilder + +import ( + "fmt" + "log" + "os" + + "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" +) + +const minGoVersion = "1.11" +const maxGoVersion = "1.21" + +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. +} + +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) +} + +// 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 +} + +// 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 +} + +// 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 { + 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 { + // 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 + ")." + 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 + + ")." + 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 " + + "environment is supported. Not requesting any version of Go." + version = "" + diagnostics.EmitNoGoModAndGoEnvSupported(msg) + } + + return msg, version +} + +// 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 { + // 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 + + "). No version of Go installed. Requesting the maximum supported version of Go (" + + maxGoVersion + ")." + 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 + + "). Not requesting any version of Go." + version = "" + 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 + ")." + version = maxGoVersion + diagnostics.EmitGoModVersionTooHighAndEnvVersionTooLow(msg) + } else if semver.Compare("v"+maxGoVersion, "v"+v.goEnvVersion) > 0 { + // 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 + ")." + 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 + + "). Not requesting any version of Go." + version = "" + diagnostics.EmitGoModVersionTooHighAndEnvVersionMax(msg) + } + + return msg, version +} + +// 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 { + // 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 + + "). No version of Go installed. Requesting the minimum supported version of Go (" + + minGoVersion + ")." + 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 + ")." + 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 + + ") is supported and is high enough for the version found in the `go.mod` file (" + + v.goModVersion + "). Not requesting any version of Go." + version = "" + diagnostics.EmitGoModVersionTooLowAndEnvVersionSupported(msg) + } + + return msg, version +} + +// 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 { + // 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 + ")." + 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 + "). " + + "Requesting the version of Go from the `go.mod` file (" + + v.goModVersion + ")." + version = v.goModVersion + diagnostics.EmitGoModVersionSupportedAndGoEnvUnsupported(msg) + } else if semver.Compare("v"+v.goModVersion, "v"+v.goEnvVersion) > 0 { + // 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 + ")." + 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 + + ") is supported and is high enough for the version found in the `go.mod` file (" + + v.goModVersion + "). Not requesting any version of Go." + version = "" + diagnostics.EmitGoModVersionSupportedLowerEqualGoEnv(msg) + } + + return msg, version +} + +// Check the versions of Go found in the environment and in the `go.mod` file, and return a +// version to install. If the version is the empty string then no installation is required. +// We never return a version of Go that is outside of the supported range. +// +// +-----------------------+-----------------------+-----------------------+-----------------------------------------------------+------------------------------------------------+ +// | Found in go.mod > | *None* | *Below min supported* | *In supported range* | *Above max supported | +// | Installed \/ | | | | | +// |-----------------------|-----------------------|-----------------------|-----------------------------------------------------|------------------------------------------------| +// | *None* | Install max supported | Install min supported | Install version from go.mod | Install max supported | +// | *Below min supported* | Install max supported | Install min supported | Install version from go.mod | Install max supported | +// | *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 { + return getVersionWhenGoModVersionNotFound(v) + } + + if aboveSupportedRange(v.goModVersion) { + return getVersionWhenGoModVersionTooHigh(v) + } + + if belowSupportedRange(v.goModVersion) { + return getVersionWhenGoModVersionTooLow(v) + } + + return getVersionWhenGoModVersionSupported(v) +} + +// Output some JSON to stdout specifying the version of Go to install, unless `version` is the +// empty string. +func outputEnvironmentJson(version string) { + var content string + if version == "" { + content = `{ "go": {} }` + } else { + content = `{ "go": { "version": "` + version + `" } }` + } + _, err := fmt.Fprint(os.Stdout, content) + + if err != nil { + log.Println("Failed to write environment json to stdout: ") + log.Println(err) + } +} + +// Get the version of Go to install and output it to stdout as json. +func IdentifyEnvironment() { + var v versionInfo + buildInfo := project.GetBuildInfo(false) + goVersionInfo := project.TryReadGoDirective(buildInfo) + v.goModVersion, v.goModVersionFound = goVersionInfo.Version, goVersionInfo.Found + + v.goEnvVersionFound = toolchain.IsInstalled() + if v.goEnvVersionFound { + v.goEnvVersion = toolchain.GetEnvGoVersion()[2:] + } + + msg, versionToInstall := getVersionToInstall(v) + log.Println(msg) + + outputEnvironmentJson(versionToInstall) +} diff --git a/go/extractor/autobuilder/build-environment_test.go b/go/extractor/autobuilder/build-environment_test.go new file mode 100644 index 000000000000..994381b0b4c7 --- /dev/null +++ b/go/extractor/autobuilder/build-environment_test.go @@ -0,0 +1,48 @@ +package autobuilder + +import "testing" + +func TestGetVersionToInstall(t *testing.T) { + tests := map[versionInfo]string{ + // getVersionWhenGoModVersionNotFound() + {"", false, "", false}: maxGoVersion, + {"", false, "1.2.2", true}: maxGoVersion, + {"", false, "9999.0.1", true}: maxGoVersion, + {"", false, "1.11.13", true}: "", + {"", false, "1.20.3", true}: "", + + // getVersionWhenGoModVersionTooHigh() + {"9999.0", true, "", false}: maxGoVersion, + {"9999.0", true, "9999.0.1", true}: "", + {"9999.0", true, "1.1", true}: maxGoVersion, + {"9999.0", true, minGoVersion, false}: maxGoVersion, + {"9999.0", true, maxGoVersion, true}: "", + + // getVersionWhenGoModVersionTooLow() + {"0.0", true, "", false}: minGoVersion, + {"0.0", true, "9999.0", true}: minGoVersion, + {"0.0", true, "1.2.2", true}: minGoVersion, + {"0.0", true, "1.20.3", true}: "", + + // getVersionWhenGoModVersionSupported() + {"1.20", true, "", false}: "1.20", + {"1.11", true, "", false}: "1.11", + {"1.20", true, "1.2.2", true}: "1.20", + {"1.11", true, "1.2.2", true}: "1.11", + {"1.20", true, "9999.0.1", true}: "1.20", + {"1.11", true, "9999.0.1", true}: "1.11", + // go.mod version > go installation version + {"1.20", true, "1.11.13", true}: "1.20", + {"1.20", true, "1.12", true}: "1.20", + // go.mod version <= go installation version (Note comparisons ignore the patch version) + {"1.11", true, "1.20", true}: "", + {"1.11", true, "1.20.3", true}: "", + {"1.20", true, "1.20.3", true}: "", + } + for input, expected := range tests { + _, actual := getVersionToInstall(input) + if actual != expected { + t.Errorf("Expected getVersionToInstall(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual) + } + } +} diff --git a/go/extractor/cli/go-autobuilder/go-autobuilder.go b/go/extractor/cli/go-autobuilder/go-autobuilder.go index 5523d0da7566..186b22e8fc8b 100644 --- a/go/extractor/cli/go-autobuilder/go-autobuilder.go +++ b/go/extractor/cli/go-autobuilder/go-autobuilder.go @@ -604,282 +604,11 @@ func installDependenciesAndBuild() { extract(buildInfo) } -const minGoVersion = "1.11" -const maxGoVersion = "1.21" - -// 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 -} - -// 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 -} - -// 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 { - 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 { - // 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 + ")." - 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 + - ")." - 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 " + - "environment is supported. Not requesting any version of Go." - version = "" - diagnostics.EmitNoGoModAndGoEnvSupported(msg) - } - - return msg, version -} - -// 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 { - // 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 + - "). No version of Go installed. Requesting the maximum supported version of Go (" + - maxGoVersion + ")." - 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 + - "). Not requesting any version of Go." - version = "" - 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 + ")." - version = maxGoVersion - diagnostics.EmitGoModVersionTooHighAndEnvVersionTooLow(msg) - } else if semver.Compare("v"+maxGoVersion, "v"+v.goEnvVersion) > 0 { - // 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 + ")." - 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 + - "). Not requesting any version of Go." - version = "" - diagnostics.EmitGoModVersionTooHighAndEnvVersionMax(msg) - } - - return msg, version -} - -// 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 { - // 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 + - "). No version of Go installed. Requesting the minimum supported version of Go (" + - minGoVersion + ")." - 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 + ")." - 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 + - ") is supported and is high enough for the version found in the `go.mod` file (" + - v.goModVersion + "). Not requesting any version of Go." - version = "" - diagnostics.EmitGoModVersionTooLowAndEnvVersionSupported(msg) - } - - return msg, version -} - -// 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 { - // 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 + ")." - 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 + "). " + - "Requesting the version of Go from the `go.mod` file (" + - v.goModVersion + ")." - version = v.goModVersion - diagnostics.EmitGoModVersionSupportedAndGoEnvUnsupported(msg) - } else if semver.Compare("v"+v.goModVersion, "v"+v.goEnvVersion) > 0 { - // 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 + ")." - 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 + - ") is supported and is high enough for the version found in the `go.mod` file (" + - v.goModVersion + "). Not requesting any version of Go." - version = "" - diagnostics.EmitGoModVersionSupportedLowerEqualGoEnv(msg) - } - - return msg, version -} - -// Check the versions of Go found in the environment and in the `go.mod` file, and return a -// version to install. If the version is the empty string then no installation is required. -// We never return a version of Go that is outside of the supported range. -// -// +-----------------------+-----------------------+-----------------------+-----------------------------------------------------+------------------------------------------------+ -// | Found in go.mod > | *None* | *Below min supported* | *In supported range* | *Above max supported | -// | Installed \/ | | | | | -// |-----------------------|-----------------------|-----------------------|-----------------------------------------------------|------------------------------------------------| -// | *None* | Install max supported | Install min supported | Install version from go.mod | Install max supported | -// | *Below min supported* | Install max supported | Install min supported | Install version from go.mod | Install max supported | -// | *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 { - return getVersionWhenGoModVersionNotFound(v) - } - - if aboveSupportedRange(v.goModVersion) { - return getVersionWhenGoModVersionTooHigh(v) - } - - if belowSupportedRange(v.goModVersion) { - return getVersionWhenGoModVersionTooLow(v) - } - - return getVersionWhenGoModVersionSupported(v) -} - -// Output some JSON to stdout specifying the version of Go to install, unless `version` is the -// empty string. -func outputEnvironmentJson(version string) { - var content string - if version == "" { - content = `{ "go": {} }` - } else { - content = `{ "go": { "version": "` + version + `" } }` - } - _, err := fmt.Fprint(os.Stdout, content) - - if err != nil { - log.Println("Failed to write environment json to stdout: ") - log.Println(err) - } -} - -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. -} - -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) -} - -// Get the version of Go to install and output it to stdout as json. -func identifyEnvironment() { - var v versionInfo - buildInfo := project.GetBuildInfo(false) - goVersionInfo := project.TryReadGoDirective(buildInfo) - v.goModVersion, v.goModVersionFound = goVersionInfo.Version, goVersionInfo.Found - - v.goEnvVersionFound = toolchain.IsInstalled() - if v.goEnvVersionFound { - v.goEnvVersion = toolchain.GetEnvGoVersion()[2:] - } - - msg, versionToInstall := getVersionToInstall(v) - log.Println(msg) - - outputEnvironmentJson(versionToInstall) -} - func main() { if len(os.Args) == 1 { installDependenciesAndBuild() } else if len(os.Args) == 2 && os.Args[1] == "--identify-environment" { - identifyEnvironment() + autobuilder.IdentifyEnvironment() } else { usage() os.Exit(2) diff --git a/go/extractor/cli/go-autobuilder/go-autobuilder_test.go b/go/extractor/cli/go-autobuilder/go-autobuilder_test.go index 7bb4dda0fb57..f4e8405fe365 100644 --- a/go/extractor/cli/go-autobuilder/go-autobuilder_test.go +++ b/go/extractor/cli/go-autobuilder/go-autobuilder_test.go @@ -20,48 +20,3 @@ func TestGetImportPathFromRepoURL(t *testing.T) { } } } - -func TestGetVersionToInstall(t *testing.T) { - tests := map[versionInfo]string{ - // getVersionWhenGoModVersionNotFound() - {"", false, "", false}: maxGoVersion, - {"", false, "1.2.2", true}: maxGoVersion, - {"", false, "9999.0.1", true}: maxGoVersion, - {"", false, "1.11.13", true}: "", - {"", false, "1.20.3", true}: "", - - // getVersionWhenGoModVersionTooHigh() - {"9999.0", true, "", false}: maxGoVersion, - {"9999.0", true, "9999.0.1", true}: "", - {"9999.0", true, "1.1", true}: maxGoVersion, - {"9999.0", true, minGoVersion, false}: maxGoVersion, - {"9999.0", true, maxGoVersion, true}: "", - - // getVersionWhenGoModVersionTooLow() - {"0.0", true, "", false}: minGoVersion, - {"0.0", true, "9999.0", true}: minGoVersion, - {"0.0", true, "1.2.2", true}: minGoVersion, - {"0.0", true, "1.20.3", true}: "", - - // getVersionWhenGoModVersionSupported() - {"1.20", true, "", false}: "1.20", - {"1.11", true, "", false}: "1.11", - {"1.20", true, "1.2.2", true}: "1.20", - {"1.11", true, "1.2.2", true}: "1.11", - {"1.20", true, "9999.0.1", true}: "1.20", - {"1.11", true, "9999.0.1", true}: "1.11", - // go.mod version > go installation version - {"1.20", true, "1.11.13", true}: "1.20", - {"1.20", true, "1.12", true}: "1.20", - // go.mod version <= go installation version (Note comparisons ignore the patch version) - {"1.11", true, "1.20", true}: "", - {"1.11", true, "1.20.3", true}: "", - {"1.20", true, "1.20.3", true}: "", - } - for input, expected := range tests { - _, actual := getVersionToInstall(input) - if actual != expected { - t.Errorf("Expected getVersionToInstall(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual) - } - } -}