From 7d123087d5b2e8979afabc9fdd8dc484a6db5737 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sun, 3 Mar 2024 09:06:34 +0000 Subject: [PATCH] Configure environment to avoid toolchain installs Force `go` to always use the local toolchain (i.e. the one the one that shipped with the go command being run) via setting the `GOTOOLCHAIN` environment variable to `local`[1]: > When GOTOOLCHAIN is set to local, the go command always runs the bundled Go toolchain. This is how things are setup in the official Docker images (e.g.[2], see also the discussion around that change[3]). The motivation behind this is to: * Reduce duplicate work: if the `toolchain` version in `go.mod` was greated than the `go` version, the version from the `go` directive would be installed, then Go would detect the `toolchain` version and additionally install that * Avoid Unexpected behaviour: if you specify this action runs with some Go version (e.g. `1.21.0`) but your go.mod contains a `toolchain` or `go` directive for a newer version (e.g. `1.22.0`) then, without any other configuration/environment setup, any go commands will be run using go `1.22.0` This will be a **breaking change** for some workflows. Given a `go.mod` like: module proj go 1.22.0 Then running any `go` command, e.g. `go mod tidy`, in an environment where only go versions before `1.22.0` were installed would previously trigger a toolchain download of Go `1.22.0` and that version being used to execute the command. With this change the above would error out with something like: > go: go.mod requires go >= 1.22.0 (running go 1.21.7; GOTOOLCHAIN=local) [1] https://go.dev/doc/toolchain#select [2] https://github.com/docker-library/golang/blob/dae3405a325073e8ad7c8c378ebdf2540d8565c4/Dockerfile-linux.template#L163 [3] https://github.com/docker-library/golang/issues/472 --- __tests__/setup-go.test.ts | 22 +++++++++++++++++----- dist/setup/index.js | 16 ++++++++++++++++ src/main.ts | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/__tests__/setup-go.test.ts b/__tests__/setup-go.test.ts index f94f9ee..1596d7b 100644 --- a/__tests__/setup-go.test.ts +++ b/__tests__/setup-go.test.ts @@ -285,7 +285,7 @@ describe('setup-go', () => { expect(logSpy).toHaveBeenCalledWith(`Setup go version spec 1.13.0`); }); - it('does not export any variables for Go versions >=1.9', async () => { + it('does not export GOROOT for Go versions >=1.9', async () => { inputs['go-version'] = '1.13.0'; inSpy.mockImplementation(name => inputs[name]); @@ -298,7 +298,7 @@ describe('setup-go', () => { }); await main.run(); - expect(vars).toStrictEqual({}); + expect(vars).not.toHaveProperty('GOROOT'); }); it('exports GOROOT for Go versions <1.9', async () => { @@ -314,9 +314,7 @@ describe('setup-go', () => { }); await main.run(); - expect(vars).toStrictEqual({ - GOROOT: toolPath - }); + expect(vars).toHaveProperty('GOROOT', toolPath); }); it('finds a version of go already in the cache', async () => { @@ -989,4 +987,18 @@ use . } ); }); + + it('exports GOTOOLCHAIN and sets it in current process env', async () => { + inputs['go-version'] = '1.21.0'; + inSpy.mockImplementation(name => inputs[name]); + + const vars: {[key: string]: string} = {}; + exportVarSpy.mockImplementation((name: string, val: string) => { + vars[name] = val; + }); + + await main.run(); + expect(vars).toStrictEqual({GOTOOLCHAIN: 'local'}); + expect(process.env).toHaveProperty('GOTOOLCHAIN', 'local'); + }); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index e7278d2..fd2401f 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -93707,6 +93707,7 @@ const os_1 = __importDefault(__nccwpck_require__(2037)); function run() { return __awaiter(this, void 0, void 0, function* () { try { + setToolchain(); // // versionSpec is optional. If supplied, install / use from the tool cache // If not supplied then problem matchers will still be setup. Useful for self-hosted. @@ -93820,6 +93821,21 @@ function resolveVersionInput() { } return version; } +function setToolchain() { + // docs: https://go.dev/doc/toolchain + // "local indicates the bundled Go toolchain (the one that shipped with the go command being run)" + // this is so any 'go' command is run with the selected Go version + // and doesn't trigger a toolchain download and run commands with that + // see e.g. issue #424 + // and a similar discussion: https://github.com/docker-library/golang/issues/472 + const toolchain = 'local'; + const toolchainVar = 'GOTOOLCHAIN'; + // set the value in process env so any `go` commands run as child-process + // don't cause toolchain downloads + process.env[toolchainVar] = toolchain; + // and in the runner env so e.g. a user running `go mod tidy` won't cause it + core.exportVariable(toolchainVar, toolchain); +} /***/ }), diff --git a/src/main.ts b/src/main.ts index 690d277..a52951c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,7 @@ import os from 'os'; export async function run() { try { + setToolchain(); // // versionSpec is optional. If supplied, install / use from the tool cache // If not supplied then problem matchers will still be setup. Useful for self-hosted. @@ -160,3 +161,20 @@ function resolveVersionInput(): string { return version; } + +function setToolchain() { + // docs: https://go.dev/doc/toolchain + // "local indicates the bundled Go toolchain (the one that shipped with the go command being run)" + // this is so any 'go' command is run with the selected Go version + // and doesn't trigger a toolchain download and run commands with that + // see e.g. issue #424 + // and a similar discussion: https://github.com/docker-library/golang/issues/472 + const toolchain = 'local'; + const toolchainVar = 'GOTOOLCHAIN'; + + // set the value in process env so any `go` commands run as child-process + // don't cause toolchain downloads + process.env[toolchainVar] = toolchain; + // and in the runner env so e.g. a user running `go mod tidy` won't cause it + core.exportVariable(toolchainVar, toolchain); +}