@@ -16,7 +16,15 @@ func IsInstalled() bool {
1616 return err == nil
1717}
1818
19+ // The default Go version that is available on a system and a set of all versions
20+ // that we know are installed on the system.
1921var goVersion = ""
22+ var goVersions = map [string ]struct {}{}
23+
24+ // Adds an entry to the set of installed Go versions for the normalised `version` number.
25+ func addGoVersion (version string ) {
26+ goVersions [semver .Canonical (version )] = struct {}{}
27+ }
2028
2129// Returns the current Go version as returned by 'go version', e.g. go1.14.4
2230func GetEnvGoVersion () string {
@@ -34,10 +42,57 @@ func GetEnvGoVersion() string {
3442 }
3543
3644 goVersion = parseGoVersion (string (out ))
45+ addGoVersion (goVersion )
3746 }
3847 return goVersion
3948}
4049
50+ // Determines whether, to our knowledge, `version` is available on the current system.
51+ func HasGoVersion (version string ) bool {
52+ _ , found := goVersions [semver .Canonical (version )]
53+ return found
54+ }
55+
56+ // Attempts to install the Go toolchain `version`.
57+ func InstallVersion (workingDir string , version string ) bool {
58+ // No need to install it if we know that it is already installed.
59+ if HasGoVersion (version ) {
60+ return true
61+ }
62+
63+ // Construct a command to invoke `go version` with `GOTOOLCHAIN=go1.N.0` to give
64+ // Go a valid toolchain version to download the toolchain we need; subsequent commands
65+ // should then work even with an invalid version that's still in `go.mod`
66+ toolchainArg := "GOTOOLCHAIN=go" + semver .Canonical (version )
67+ versionCmd := Version ()
68+ versionCmd .Dir = workingDir
69+ versionCmd .Env = append (os .Environ (), toolchainArg )
70+
71+ log .Printf (
72+ "Trying to install Go %s using its canonical representation in `%s`." ,
73+ version ,
74+ workingDir ,
75+ )
76+
77+ // Run the command. If something goes wrong, report it to the log and signal failure
78+ // to the caller.
79+ if versionErr := versionCmd .Run (); versionErr != nil {
80+ log .Printf (
81+ "Failed to invoke `%s go version` in %s: %s\n " ,
82+ toolchainArg ,
83+ versionCmd .Dir ,
84+ versionErr .Error (),
85+ )
86+
87+ return false
88+ }
89+
90+ // Add the version to the set of versions that we know are installed and signal
91+ // success to the caller.
92+ addGoVersion (version )
93+ return true
94+ }
95+
4196// Returns the current Go version in semver format, e.g. v1.14.4
4297func GetEnvGoSemVer () string {
4398 goVersion := GetEnvGoVersion ()
0 commit comments