-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Go: Deal with incorrect toolchain versions #15979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
3649af3
be027e2
96a6dd7
0d527b2
c74d634
86bf4fb
ab255d7
6ea9982
6b1d1d4
45b41bb
977ac71
f6c22d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,15 @@ func IsInstalled() bool { | |
return err == nil | ||
} | ||
|
||
// 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{}{} | ||
|
||
// Adds an entry to the set of installed Go versions for the normalised `version` number. | ||
func addGoVersion(version string) { | ||
goVersions[semver.Canonical(version)] = struct{}{} | ||
} | ||
|
||
// Returns the current Go version as returned by 'go version', e.g. go1.14.4 | ||
func GetEnvGoVersion() string { | ||
|
@@ -25,7 +33,7 @@ func GetEnvGoVersion() string { | |
// download the version of Go specified in there. That may either fail or result in us just | ||
// being told what's already in 'go.mod'. Setting 'GOTOOLCHAIN' to 'local' will force it | ||
// to use the local Go toolchain instead. | ||
cmd := exec.Command("go", "version") | ||
cmd := Version() | ||
cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local") | ||
out, err := cmd.CombinedOutput() | ||
|
||
|
@@ -34,10 +42,57 @@ func GetEnvGoVersion() string { | |
} | ||
|
||
goVersion = parseGoVersion(string(out)) | ||
addGoVersion(goVersion) | ||
} | ||
return goVersion | ||
} | ||
|
||
// Determines whether, to our knowledge, `version` is available on the current system. | ||
func HasGoVersion(version string) bool { | ||
_, found := goVersions[semver.Canonical(version)] | ||
return found | ||
} | ||
|
||
// Attempts to install the Go toolchain `version`. | ||
func InstallVersion(workingDir string, version string) bool { | ||
// No need to install it if we know that it is already installed. | ||
if HasGoVersion(version) { | ||
return true | ||
} | ||
|
||
// 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(version) | ||
versionCmd := Version() | ||
versionCmd.Dir = workingDir | ||
versionCmd.Env = append(os.Environ(), toolchainArg) | ||
|
||
log.Printf( | ||
"Trying to install Go %s using its canonical representation in `%s`.", | ||
version, | ||
workingDir, | ||
) | ||
|
||
// Run the command. If something goes wrong, report it to the log and signal failure | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider marking this as "installed" regardless to avoid repeatedly attempting to install the same thing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about this. It seems like a rare scenario where we would repeatedly attempt to install the same version and that fails for whatever reason. If I had implemented this set specifically for the new check, then okay, but since the implementation is more general, it might lead to weird results down the line if we want to utilise the set of installed versions for something else and find that in some cases those aren't actually installed. |
||
// to the caller. | ||
if versionErr := versionCmd.Run(); versionErr != nil { | ||
log.Printf( | ||
"Failed to invoke `%s go version` in %s: %s\n", | ||
toolchainArg, | ||
versionCmd.Dir, | ||
versionErr.Error(), | ||
) | ||
|
||
return false | ||
} | ||
|
||
// Add the version to the set of versions that we know are installed and signal | ||
// success to the caller. | ||
addGoVersion(version) | ||
return true | ||
} | ||
|
||
// Returns the current Go version in semver format, e.g. v1.14.4 | ||
func GetEnvGoSemVer() string { | ||
goVersion := GetEnvGoVersion() | ||
|
@@ -92,3 +147,9 @@ func VendorModule(path string) *exec.Cmd { | |
modVendor.Dir = path | ||
return modVendor | ||
} | ||
|
||
// Constructs a command to run `go version`. | ||
func Version() *exec.Cmd { | ||
version := exec.Command("go", "version") | ||
return version | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"location": { | ||
"file": "go.mod" | ||
}, | ||
"markdownMessage": "As of Go 1.21, toolchain versions [must use the 1.N.P syntax](https://tip.golang.org/doc/toolchain#version).\n\n`1.21` in `go.mod` does not match this syntax and there is no additional `toolchain` directive, which may cause some `go` commands to fail.", | ||
mbg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"severity": "warning", | ||
"source": { | ||
"extractorName": "go", | ||
"id": "go/autobuilder/invalid-go-toolchain-version", | ||
"name": "`1.21` is not a valid Go toolchain version" | ||
}, | ||
"visibility": { | ||
"cliSummaryTable": true, | ||
"statusPage": true, | ||
"telemetry": true | ||
} | ||
} | ||
{ | ||
"markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", | ||
"severity": "note", | ||
"source": { | ||
"extractorName": "go", | ||
"id": "go/autobuilder/single-root-go-mod-found", | ||
"name": "A single `go.mod` file was found in the root" | ||
}, | ||
"visibility": { | ||
"cliSummaryTable": false, | ||
"statusPage": false, | ||
"telemetry": true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
go 1.21 | ||
|
||
module example |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package main | ||
|
||
func main() { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import os | ||
import subprocess | ||
|
||
from create_database_utils import * | ||
from diagnostics_test_utils import * | ||
|
||
# Set up a GOPATH relative to this test's root directory; | ||
# we set os.environ instead of using extra_env because we | ||
# need it to be set for the call to "go clean -modcache" later | ||
goPath = os.path.join(os.path.abspath(os.getcwd()), ".go") | ||
os.environ['GOPATH'] = goPath | ||
os.environ['LGTM_INDEX_IMPORT_PATH'] = "test" | ||
run_codeql_database_create([], lang="go", source="src") | ||
|
||
check_diagnostics() | ||
|
||
# Clean up the temporary GOPATH to prevent Bazel failures next | ||
# time the tests are run; see https://github.com/golang/go/issues/27161 | ||
subprocess.call(["go", "clean", "-modcache"]) |
Uh oh!
There was an error while loading. Please reload this page.