diff --git a/__tests__/setup-go.test.ts b/__tests__/setup-go.test.ts index 5015e91..9f45b0c 100644 --- a/__tests__/setup-go.test.ts +++ b/__tests__/setup-go.test.ts @@ -901,6 +901,18 @@ use . expect(logSpy).toHaveBeenCalledWith('matching 1.19...'); }); + it('reads version from go.mod-like file name', async () => { + inputs['go-version-file'] = 'go.tool.mod'; + existsSpy.mockImplementation(() => true); + readFileSpy.mockImplementation(() => Buffer.from(goModContents)); + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.14'); + expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.14...'); + expect(logSpy).toHaveBeenCalledWith('matching 1.14...'); + }); + it('reads version from .tool-versions', async () => { inputs['go-version-file'] = '.tool-versions'; existsSpy.mockImplementation(() => true); @@ -1066,6 +1078,19 @@ use . expected_version: placeholderVersion, desc: 'from go directive when GOTOOLCHAIN is local' }, + { + goVersionfile: 'go.tool.mod', + fileContents: Buffer.from(buildGoMod(placeholderVersion, version)), + expected_version: version, + desc: 'from toolchain directive' + }, + { + goVersionfile: 'go.tool.mod', + fileContents: Buffer.from(buildGoMod(placeholderVersion, version)), + gotoolchain_env: 'local', + expected_version: placeholderVersion, + desc: 'from go directive when GOTOOLCHAIN is local' + }, { goVersionfile: 'go.work', fileContents: Buffer.from(buildGoMod(placeholderVersion, version)), diff --git a/dist/setup/index.js b/dist/setup/index.js index 796803e..4f3730d 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -77493,26 +77493,33 @@ function makeSemver(version) { return fullVersion; } function parseGoVersionFile(versionFilePath) { + const moduleOrWorkspaceDirective = /^\s*go\s+(\d+(?:\.\d+)*)/m; + const toolchainDirective = /^\s*toolchain\s+go(1\.\d+(?:\.\d+|rc\d+)?)/m; + const moduleDeclaration = /^\s*module\s+\S+/m; + const workspaceUseDirective = /^\s*use(?:\s+\S+|\s*\()/m; const contents = fs_1.default.readFileSync(versionFilePath).toString(); - if (path.basename(versionFilePath) === 'go.mod' || - path.basename(versionFilePath) === 'go.work') { + const fileName = path.basename(versionFilePath); + const isGoModOrWorkFileName = fileName === 'go.mod' || fileName === 'go.work'; + const isGoModuleOrWorkspaceLike = moduleOrWorkspaceDirective.test(contents) && + (moduleDeclaration.test(contents) || workspaceUseDirective.test(contents)); + if (fileName === '.tool-versions') { + const match = contents.match(/^golang\s+([^\n#]+)/m); + return match ? match[1].trim() : ''; + } + if (isGoModOrWorkFileName || isGoModuleOrWorkspaceLike) { // for backwards compatibility: use version from go directive if // 'GOTOOLCHAIN' has been explicitly set if (process.env[exports.GOTOOLCHAIN_ENV_VAR] !== exports.GOTOOLCHAIN_LOCAL_VAL) { // toolchain directive: https://go.dev/ref/mod#go-mod-file-toolchain - const matchToolchain = contents.match(/^toolchain go(1\.\d+(?:\.\d+|rc\d+)?)/m); + const matchToolchain = contents.match(toolchainDirective); if (matchToolchain) { return matchToolchain[1]; } } // go directive: https://go.dev/ref/mod#go-mod-file-go - const matchGo = contents.match(/^go (\d+(\.\d+)*)/m); + const matchGo = contents.match(moduleOrWorkspaceDirective); return matchGo ? matchGo[1] : ''; } - else if (path.basename(versionFilePath) === '.tool-versions') { - const match = contents.match(/^golang\s+([^\n#]+)/m); - return match ? match[1].trim() : ''; - } return contents.trim(); } function resolveStableVersionDist(versionSpec, arch) { diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 91e8ffa..01a929f 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -164,7 +164,7 @@ jobs: ## Using the `go-version-file` input -`setup-go` action can read the Go version from a version file. `go-version-file` input is used for specifying the path to the version file. If the file supplied to the `go-version-file` input doesn't exist, the action will fail with an error. This input supports go.mod, go.work, .go-version, and .tool-versions files. +`setup-go` action can read the Go version from a version file. `go-version-file` input is used for specifying the path to the version file. If the file supplied to the `go-version-file` input doesn't exist, the action will fail with an error. This input supports standard `go.mod` and `go.work` files, custom files that use the same `go.mod`/`go.work` format (for example, `go.tool.mod`), and `.go-version` and `.tool-versions` files. If both the `go-version` and the `go-version-file` inputs are provided then the `go-version` input is used. The `.tool-versions` file supports version specifications in accordance with asdf standards, adhering to Semantic Versioning ([semver](https://semver.org)). diff --git a/src/installer.ts b/src/installer.ts index e845e31..c6de1b9 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -651,30 +651,38 @@ export function makeSemver(version: string): string { } export function parseGoVersionFile(versionFilePath: string): string { - const contents = fs.readFileSync(versionFilePath).toString(); + const moduleOrWorkspaceDirective = /^\s*go\s+(\d+(?:\.\d+)*)/m; + const toolchainDirective = /^\s*toolchain\s+go(1\.\d+(?:\.\d+|rc\d+)?)/m; + const moduleDeclaration = /^\s*module\s+\S+/m; + const workspaceUseDirective = /^\s*use(?:\s+\S+|\s*\()/m; - if ( - path.basename(versionFilePath) === 'go.mod' || - path.basename(versionFilePath) === 'go.work' - ) { + const contents = fs.readFileSync(versionFilePath).toString(); + const fileName = path.basename(versionFilePath); + + const isGoModOrWorkFileName = fileName === 'go.mod' || fileName === 'go.work'; + const isGoModuleOrWorkspaceLike = + moduleOrWorkspaceDirective.test(contents) && + (moduleDeclaration.test(contents) || workspaceUseDirective.test(contents)); + + if (fileName === '.tool-versions') { + const match = contents.match(/^golang\s+([^\n#]+)/m); + return match ? match[1].trim() : ''; + } + + if (isGoModOrWorkFileName || isGoModuleOrWorkspaceLike) { // for backwards compatibility: use version from go directive if // 'GOTOOLCHAIN' has been explicitly set if (process.env[GOTOOLCHAIN_ENV_VAR] !== GOTOOLCHAIN_LOCAL_VAL) { // toolchain directive: https://go.dev/ref/mod#go-mod-file-toolchain - const matchToolchain = contents.match( - /^toolchain go(1\.\d+(?:\.\d+|rc\d+)?)/m - ); + const matchToolchain = contents.match(toolchainDirective); if (matchToolchain) { return matchToolchain[1]; } } // go directive: https://go.dev/ref/mod#go-mod-file-go - const matchGo = contents.match(/^go (\d+(\.\d+)*)/m); + const matchGo = contents.match(moduleOrWorkspaceDirective); return matchGo ? matchGo[1] : ''; - } else if (path.basename(versionFilePath) === '.tool-versions') { - const match = contents.match(/^golang\s+([^\n#]+)/m); - return match ? match[1].trim() : ''; } return contents.trim();