Go: Use SemVer type in toolchain package

This commit is contained in:
Michael B. Gale
2024-05-08 20:57:14 +01:00
parent d171750678
commit 010df54657
4 changed files with 26 additions and 31 deletions

View File

@@ -156,7 +156,8 @@ func getNeedGopath(workspace project.GoWorkspace, importpath string) bool {
// Try to update `go.mod` and `go.sum` if the go version is >= 1.16.
func tryUpdateGoModAndGoSum(workspace project.GoWorkspace) {
// Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
if workspace.ModMode != project.ModVendor && workspace.DepMode == project.GoGetWithModules && semver.Compare(toolchain.GetEnvGoSemVer(), "v1.16") >= 0 {
v1_16 := util.NewSemVer("v1.16")
if workspace.ModMode != project.ModVendor && workspace.DepMode == project.GoGetWithModules && toolchain.GetEnvGoSemVer().IsAtLeast(v1_16) {
for _, goMod := range workspace.Modules {
// stat go.mod and go.sum
goModPath := goMod.Path
@@ -338,7 +339,7 @@ func buildWithoutCustomCommands(modMode project.ModMode) bool {
}
shouldInstallDependencies = true
} else if toolchain.DepErrors("./...", modMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...) {
} else if toolchain.DepErrors("./...", modMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer().String())...) {
log.Printf("Dependencies are still not resolving after executing %d build script(s), continuing to install dependencies in the normal way.\n", scriptCount)
shouldInstallDependencies = true
@@ -452,7 +453,7 @@ func extract(workspace project.GoWorkspace) bool {
extractorArgs := []string{}
if workspace.DepMode == project.GoGetWithModules {
extractorArgs = append(extractorArgs, workspace.ModMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...)
extractorArgs = append(extractorArgs, workspace.ModMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer().String())...)
}
if len(workspace.Modules) == 0 {
@@ -542,8 +543,9 @@ func installDependenciesAndBuild() {
// This diagnostic is not required if the system Go version is 1.21 or greater, since the
// Go tooling should install required Go versions as needed.
if semver.Compare(toolchain.GetEnvGoSemVer(), "v1.21.0") < 0 && greatestGoVersion.Found && semver.Compare("v"+greatestGoVersion.Version, toolchain.GetEnvGoSemVer()) > 0 {
diagnostics.EmitNewerGoVersionNeeded(toolchain.GetEnvGoSemVer(), "v"+greatestGoVersion.Version)
v1_21 := util.NewSemVer("v1.21.0")
if toolchain.GetEnvGoSemVer().IsOlderThan(v1_21) && greatestGoVersion.Found && semver.Compare("v"+greatestGoVersion.Version, toolchain.GetEnvGoSemVer().String()) > 0 {
diagnostics.EmitNewerGoVersionNeeded(toolchain.GetEnvGoSemVer().String(), "v"+greatestGoVersion.Version)
if val, _ := os.LookupEnv("GITHUB_ACTIONS"); val == "true" {
log.Printf(
"A go.mod file requires version %s of Go, but version %s is installed. Consider adding an actions/setup-go step to your workflow.\n",

View File

@@ -7,14 +7,12 @@ go_library(
srcs = ["toolchain.go"],
importpath = "github.com/github/codeql-go/extractor/toolchain",
visibility = ["//visibility:public"],
deps = [
"//go/extractor/util",
"//go/extractor/vendor/golang.org/x/mod/semver",
],
deps = ["//go/extractor/util"],
)
go_test(
name = "toolchain_test",
srcs = ["toolchain_test.go"],
embed = [":toolchain"],
deps = ["//go/extractor/util"],
)

View File

@@ -11,7 +11,6 @@ import (
"strings"
"github.com/github/codeql-go/extractor/util"
"golang.org/x/mod/semver"
)
// Check if Go is installed in the environment.
@@ -23,11 +22,11 @@ func IsInstalled() bool {
// The default Go version that is available on a system and a set of all versions
// that we know are installed on the system.
var goVersion = ""
var goVersions = map[string]struct{}{}
var goVersions = map[util.SemVer]struct{}{}
// Adds an entry to the set of installed Go versions for the normalised `version` number.
func addGoVersion(version string) {
goVersions[semver.Canonical("v"+version)] = struct{}{}
func addGoVersion(version util.SemVer) {
goVersions[version] = struct{}{}
}
// Returns the current Go version as returned by 'go version', e.g. go1.14.4
@@ -53,19 +52,19 @@ func GetEnvGoVersion() string {
}
goVersion = parseGoVersion(string(out))
addGoVersion(goVersion[2:])
addGoVersion(util.NewSemVer(goVersion))
}
return goVersion
}
// Determines whether, to our knowledge, `version` is available on the current system.
func HasGoVersion(version string) bool {
_, found := goVersions[semver.Canonical("v"+version)]
func HasGoVersion(version util.SemVer) bool {
_, found := goVersions[version]
return found
}
// Attempts to install the Go toolchain `version`.
func InstallVersion(workingDir string, version string) bool {
func InstallVersion(workingDir string, version util.SemVer) bool {
// No need to install it if we know that it is already installed.
if HasGoVersion(version) {
return true
@@ -74,7 +73,7 @@ func InstallVersion(workingDir string, version string) bool {
// Construct a command to invoke `go version` with `GOTOOLCHAIN=go1.N.0` to give
// Go a valid toolchain version to download the toolchain we need; subsequent commands
// should then work even with an invalid version that's still in `go.mod`
toolchainArg := "GOTOOLCHAIN=go" + semver.Canonical("v" + version)[1:]
toolchainArg := "GOTOOLCHAIN=go" + version.String()[1:]
versionCmd := Version()
versionCmd.Dir = workingDir
versionCmd.Env = append(os.Environ(), toolchainArg)
@@ -107,20 +106,12 @@ func InstallVersion(workingDir string, version string) bool {
}
// Returns the current Go version in semver format, e.g. v1.14.4
func GetEnvGoSemVer() string {
func GetEnvGoSemVer() util.SemVer {
goVersion := GetEnvGoVersion()
if !strings.HasPrefix(goVersion, "go") {
log.Fatalf("Expected 'go version' output of the form 'go1.2.3'; got '%s'", goVersion)
}
// 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. `go1.22rc1` into `go1.22-rc1`
// which is compatible with the SemVer specification
rcIndex := strings.Index(goVersion, "rc")
if rcIndex != -1 {
return semver.Canonical("v"+goVersion[2:rcIndex]) + "-" + goVersion[rcIndex:]
} else {
return semver.Canonical("v" + goVersion[2:])
}
return util.NewSemVer(goVersion)
}
// The 'go version' command may output warnings on separate lines before
@@ -137,7 +128,7 @@ func parseGoVersion(data string) string {
// Returns a value indicating whether the system Go toolchain supports workspaces.
func SupportsWorkspaces() bool {
return semver.Compare(GetEnvGoSemVer(), "v1.18.0") >= 0
return GetEnvGoSemVer().IsAtLeast(util.NewSemVer("v1.18.0"))
}
// Run `go mod tidy -e` in the directory given by `path`.

View File

@@ -1,6 +1,10 @@
package toolchain
import "testing"
import (
"testing"
"github.com/github/codeql-go/extractor/util"
)
func TestParseGoVersion(t *testing.T) {
tests := map[string]string{
@@ -16,7 +20,7 @@ func TestParseGoVersion(t *testing.T) {
}
func TestHasGoVersion(t *testing.T) {
if HasGoVersion("1.21") {
if HasGoVersion(util.NewSemVer("1.21")) {
t.Error("Expected HasGoVersion(\"1.21\") to be false, but got true")
}
}