Skip to content

Commit

Permalink
Go: Use SemVer type in toolchain package
Browse files Browse the repository at this point in the history
  • Loading branch information
mbg committed May 8, 2024
1 parent 3b40302 commit 1b82143
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 31 deletions.
12 changes: 7 additions & 5 deletions go/extractor/cli/go-autobuilder/go-autobuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -333,7 +334,7 @@ func buildWithoutCustomCommands(modMode project.ModMode) bool {
log.Println("Build failed, continuing to install dependencies.")

shouldInstallDependencies = true
} else if toolchain.DepErrors("./...", modMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...) {
} else if toolchain.DepErrors("./...", modMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer().String())...) {
log.Println("Dependencies are still not resolving after the build, continuing to install dependencies.")

shouldInstallDependencies = true
Expand Down Expand Up @@ -447,7 +448,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 {
Expand Down Expand Up @@ -537,8 +538,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",
Expand Down
6 changes: 2 additions & 4 deletions go/extractor/toolchain/BUILD.bazel

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

31 changes: 11 additions & 20 deletions go/extractor/toolchain/toolchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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`.
Expand Down
8 changes: 6 additions & 2 deletions go/extractor/toolchain/toolchain_test.go
Original file line number Diff line number Diff line change
@@ -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{
Expand All @@ -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")
}
}

0 comments on commit 1b82143

Please sign in to comment.