diff --git a/__tests__/setup-go.test.ts b/__tests__/setup-go.test.ts index 70f2166eb..ee2df5916 100644 --- a/__tests__/setup-go.test.ts +++ b/__tests__/setup-go.test.ts @@ -265,7 +265,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]); @@ -278,7 +278,7 @@ describe('setup-go', () => { }); await main.run(); - expect(vars).toStrictEqual({}); + expect(vars).not.toHaveProperty('GOROOT'); }); it('exports GOROOT for Go versions <1.9', async () => { @@ -294,9 +294,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 () => { @@ -966,4 +964,68 @@ use . } ); }); + + describe('go-version-file-toolchain', () => { + const goModContents = `module example.com/mymodule + +go 1.14 + +toolchain go1.21.0 + +require ( + example.com/othermodule v1.2.3 + example.com/thismodule v1.2.3 + example.com/thatmodule v1.2.3 +) + +replace example.com/thatmodule => ../thatmodule +exclude example.com/thismodule v1.3.0 +`; + + const goWorkContents = `go 1.19 + +toolchain go1.21.0 + +use . + +`; + + it('reads version from toolchain directive in go.mod', async () => { + inputs['go-version-file'] = 'go.mod'; + existsSpy.mockImplementation(() => true); + readFileSpy.mockImplementation(() => Buffer.from(goModContents)); + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.21.0'); + expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.21.0...'); + expect(logSpy).toHaveBeenCalledWith('matching 1.21.0...'); + }); + + it('reads version from toolchain directive in go.work', async () => { + inputs['go-version-file'] = 'go.work'; + existsSpy.mockImplementation(() => true); + readFileSpy.mockImplementation(() => Buffer.from(goWorkContents)); + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.21.0'); + expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.21.0...'); + expect(logSpy).toHaveBeenCalledWith('matching 1.21.0...'); + }); + }); + + 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 f0a94cfc8..bde704453 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -88318,8 +88318,14 @@ function parseGoVersionFile(versionFilePath) { const contents = fs_1.default.readFileSync(versionFilePath).toString(); if (path.basename(versionFilePath) === 'go.mod' || path.basename(versionFilePath) === 'go.work') { - const match = contents.match(/^go (\d+(\.\d+)*)/m); - return match ? match[1] : ''; + // toolchain directive: https://go.dev/ref/mod#go-mod-file-toolchain + const matchToolchain = contents.match(/^toolchain go(\d+(\.\d+)*)/m); + if (matchToolchain) { + return matchToolchain[1]; + } + // go directive: https://go.dev/ref/mod#go-mod-file-go + const matchGo = contents.match(/^go (\d+(\.\d+)*)/m); + return matchGo ? matchGo[1] : ''; } return contents.trim(); } @@ -88422,6 +88428,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. @@ -88538,6 +88545,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/installer.ts b/src/installer.ts index 817c334f6..96be79b12 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -424,8 +424,15 @@ export function parseGoVersionFile(versionFilePath: string): string { path.basename(versionFilePath) === 'go.mod' || path.basename(versionFilePath) === 'go.work' ) { - const match = contents.match(/^go (\d+(\.\d+)*)/m); - return match ? match[1] : ''; + // toolchain directive: https://go.dev/ref/mod#go-mod-file-toolchain + const matchToolchain = contents.match(/^toolchain go(\d+(\.\d+)*)/m); + if (matchToolchain) { + return matchToolchain[1]; + } + + // go directive: https://go.dev/ref/mod#go-mod-file-go + const matchGo = contents.match(/^go (\d+(\.\d+)*)/m); + return matchGo ? matchGo[1] : ''; } return contents.trim(); diff --git a/src/main.ts b/src/main.ts index 690d277f9..a52951c86 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); +}