From fc952ad45a57908b519079484e03b071ac61772f Mon Sep 17 00:00:00 2001 From: plukevdh Date: Fri, 19 May 2023 15:17:09 -0400 Subject: [PATCH] add rudimentary .tool-versions parser --- .github/workflows/versions.yml | 16 ++++ __tests__/data/.tool-versions | 4 + __tests__/setup-go.test.ts | 153 +++++++++++++++++++++------------ src/installer.ts | 7 ++ src/main.ts | 27 ++++-- 5 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 __tests__/data/.tool-versions diff --git a/.github/workflows/versions.yml b/.github/workflows/versions.yml index 89fc7b8..b25d1d7 100644 --- a/.github/workflows/versions.yml +++ b/.github/workflows/versions.yml @@ -134,6 +134,22 @@ jobs: run: __tests__/verify-go.sh 1.19 shell: bash + tool-versions-file: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, windows-latest, macos-latest ] + steps: + - uses: actions/checkout@v3 + - name: Setup Go and check latest + uses: ./ + with: + tool-versions-file: __tests__/data/.tool-versions + - name: verify go + run: __tests__/verify-go.sh 1.16.3 + shell: bash + setup-versions-from-manifest: name: Setup ${{ matrix.go }} ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/__tests__/data/.tool-versions b/__tests__/data/.tool-versions new file mode 100644 index 0000000..cce7a1b --- /dev/null +++ b/__tests__/data/.tool-versions @@ -0,0 +1,4 @@ +terraform 1.3.7 +golang 1.16.3 +python 3.10.11 +ruby 3.2.1 diff --git a/__tests__/setup-go.test.ts b/__tests__/setup-go.test.ts index cea14f4..7a028ce 100644 --- a/__tests__/setup-go.test.ts +++ b/__tests__/setup-go.test.ts @@ -138,7 +138,7 @@ describe('setup-go', () => { expect(match!.resolvedVersion).toBe('1.9.7'); expect(match!.type).toBe('manifest'); expect(match!.downloadUrl).toBe( - 'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-darwin-x64.tar.gz' + 'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-darwin-x64.tar.gz' ); }); @@ -151,7 +151,7 @@ describe('setup-go', () => { expect(match!.resolvedVersion).toBe('1.9.7'); expect(match!.type).toBe('manifest'); expect(match!.downloadUrl).toBe( - 'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-linux-x64.tar.gz' + 'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-linux-x64.tar.gz' ); }); @@ -164,7 +164,7 @@ describe('setup-go', () => { expect(match!.resolvedVersion).toBe('1.9.7'); expect(match!.type).toBe('manifest'); expect(match!.downloadUrl).toBe( - 'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-win32-x64.zip' + 'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-win32-x64.zip' ); }); @@ -263,7 +263,7 @@ describe('setup-go', () => { const toolPath = path.normalize('/cache/go/1.13.0/x64'); findSpy.mockImplementation(() => toolPath); - const vars: {[key: string]: string} = {}; + const vars: { [key: string]: string } = {}; exportVarSpy.mockImplementation((name: string, val: string) => { vars[name] = val; }); @@ -279,7 +279,7 @@ describe('setup-go', () => { const toolPath = path.normalize('/cache/go/1.8.0/x64'); findSpy.mockImplementation(() => toolPath); - const vars: {[key: string]: string} = {}; + const vars: { [key: string]: string } = {}; exportVarSpy.mockImplementation((name: string, val: string) => { vars[name] = val; }); @@ -359,9 +359,9 @@ describe('setup-go', () => { const expPath = path.win32.join(toolPath, 'bin'); expect(dlSpy).toHaveBeenCalledWith( - 'https://storage.googleapis.com/golang/go1.13.1.windows-amd64.zip', - 'C:\\temp\\go1.13.1.windows-amd64.zip', - undefined + 'https://storage.googleapis.com/golang/go1.13.1.windows-amd64.zip', + 'C:\\temp\\go1.13.1.windows-amd64.zip', + undefined ); expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`); }); @@ -376,7 +376,7 @@ describe('setup-go', () => { await main.run(); expect(cnSpy).toHaveBeenCalledWith( - `::error::Unable to find Go version '9.99.9' for platform linux and architecture x64.${osm.EOL}` + `::error::Unable to find Go version '9.99.9' for platform linux and architecture x64.${osm.EOL}` ); }); @@ -390,7 +390,7 @@ describe('setup-go', () => { inputs['token'] = 'faketoken'; const expectedUrl = - 'https://github.com/actions/go-versions/releases/download/1.12.16-20200616.20/go-1.12.16-linux-x64.tar.gz'; + 'https://github.com/actions/go-versions/releases/download/1.12.16-20200616.20/go-1.12.16-linux-x64.tar.gz'; // ... but not in the local cache findSpy.mockImplementation(() => ''); @@ -407,10 +407,10 @@ describe('setup-go', () => { expect(dlSpy).toHaveBeenCalled(); expect(extractTarSpy).toHaveBeenCalled(); expect(logSpy).not.toHaveBeenCalledWith( - 'Not found in manifest. Falling back to download directly from Go' + 'Not found in manifest. Falling back to download directly from Go' ); expect(logSpy).toHaveBeenCalledWith( - `Acquiring 1.12.16 from ${expectedUrl}` + `Acquiring 1.12.16 from ${expectedUrl}` ); expect(logSpy).toHaveBeenCalledWith(`Added go to the path`); @@ -427,7 +427,7 @@ describe('setup-go', () => { inputs['token'] = 'faketoken'; const expectedUrl = - 'https://github.com/actions/go-versions/releases/download/1.12.17-20200616.21/go-1.12.17-linux-x64.tar.gz'; + 'https://github.com/actions/go-versions/releases/download/1.12.17-20200616.21/go-1.12.17-linux-x64.tar.gz'; // ... but not in the local cache findSpy.mockImplementation(() => ''); @@ -444,10 +444,10 @@ describe('setup-go', () => { expect(dlSpy).toHaveBeenCalled(); expect(extractTarSpy).toHaveBeenCalled(); expect(logSpy).not.toHaveBeenCalledWith( - 'Not found in manifest. Falling back to download directly from Go' + 'Not found in manifest. Falling back to download directly from Go' ); expect(logSpy).toHaveBeenCalledWith( - `Acquiring 1.12.17 from ${expectedUrl}` + `Acquiring 1.12.17 from ${expectedUrl}` ); expect(logSpy).toHaveBeenCalledWith(`Added go to the path`); @@ -481,7 +481,7 @@ describe('setup-go', () => { expect(logSpy).toHaveBeenCalledWith('matching 1.12.14...'); expect(extractTarSpy).toHaveBeenCalled(); expect(logSpy).toHaveBeenCalledWith( - 'Not found in manifest. Falling back to download directly from Go' + 'Not found in manifest. Falling back to download directly from Go' ); expect(logSpy).toHaveBeenCalledWith(`Install from dist`); expect(logSpy).toHaveBeenCalledWith(`Added go to the path`); @@ -502,7 +502,7 @@ describe('setup-go', () => { await main.run(); expect(cnSpy).toHaveBeenCalledWith( - `::error::Failed to download version 1.13.1: Error: ${errMsg}${osm.EOL}` + `::error::Failed to download version 1.13.1: Error: ${errMsg}${osm.EOL}` ); }); @@ -523,7 +523,8 @@ describe('setup-go', () => { return '/Users/testuser/go'; }); - mkdirpSpy.mockImplementation(async () => {}); + mkdirpSpy.mockImplementation(async () => { + }); existsSpy.mockImplementation(() => { return false; }); @@ -667,7 +668,7 @@ describe('setup-go', () => { expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`); expect(logSpy).not.toHaveBeenCalledWith( - 'Attempting to resolve the latest version from the manifest...' + 'Attempting to resolve the latest version from the manifest...' ); }); @@ -709,20 +710,20 @@ describe('setup-go', () => { await main.run(); expect(logSpy).toHaveBeenCalledWith( - `Setup go version spec ${versionSpec}` + `Setup go version spec ${versionSpec}` ); expect(logSpy).toHaveBeenCalledWith( - 'Attempting to resolve the latest version from the manifest...' + 'Attempting to resolve the latest version from the manifest...' ); expect(logSpy).toHaveBeenCalledWith(`Resolved as '${patchVersion}'`); expect(logSpy).toHaveBeenCalledWith( - `Attempting to download ${patchVersion}...` + `Attempting to download ${patchVersion}...` ); expect(logSpy).toHaveBeenCalledWith('Extracting Go...'); expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...'); expect(logSpy).toHaveBeenCalledWith('Added go to the path'); expect(logSpy).toHaveBeenCalledWith( - `Successfully set up Go version ${versionSpec}` + `Successfully set up Go version ${versionSpec}` ); }); @@ -752,13 +753,13 @@ describe('setup-go', () => { expect(dlSpy).toHaveBeenCalled(); expect(extractTarSpy).toHaveBeenCalled(); expect(logSpy).toHaveBeenCalledWith( - 'Attempting to resolve the latest version from the manifest...' + 'Attempting to resolve the latest version from the manifest...' ); expect(logSpy).toHaveBeenCalledWith( - `Failed to resolve version ${versionSpec} from manifest` + `Failed to resolve version ${versionSpec} from manifest` ); expect(logSpy).toHaveBeenCalledWith( - `Attempting to download ${versionSpec}...` + `Attempting to download ${versionSpec}...` ); expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`); }); @@ -793,21 +794,21 @@ describe('setup-go', () => { const expPath = path.join(toolPath, 'bin'); expect(logSpy).toHaveBeenCalledWith( - `Failed to resolve version ${versionSpec} from manifest` + `Failed to resolve version ${versionSpec} from manifest` ); expect(dlSpy).toHaveBeenCalled(); expect(extractTarSpy).toHaveBeenCalled(); expect(logSpy).toHaveBeenCalledWith( - 'Attempting to resolve the latest version from the manifest...' + 'Attempting to resolve the latest version from the manifest...' ); expect(logSpy).toHaveBeenCalledWith( - 'Unable to resolve a version from the manifest...' + 'Unable to resolve a version from the manifest...' ); expect(logSpy).toHaveBeenCalledWith( - `Failed to resolve version ${versionSpec} from manifest` + `Failed to resolve version ${versionSpec} from manifest` ); expect(logSpy).toHaveBeenCalledWith( - `Attempting to download ${versionSpec}...` + `Attempting to download ${versionSpec}...` ); expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`); @@ -891,7 +892,7 @@ use . await main.run(); expect(cnSpy).toHaveBeenCalledWith( - `::error::The specified go version file at: go.mod does not exist${osm.EOL}` + `::error::The specified go version file at: go.mod does not exist${osm.EOL}` ); }); @@ -911,9 +912,9 @@ use . inputs['architecture'] = arch; const expectedUrl = - platform === 'win32' - ? `https://github.com/actions/go-versions/releases/download/${version}/go-${version}-${platform}-${arch}.${fileExtension}` - : `https://storage.googleapis.com/golang/go${version}.${osSpec}-${arch}.${fileExtension}`; + platform === 'win32' + ? `https://github.com/actions/go-versions/releases/download/${version}/go-${version}-${platform}-${arch}.${fileExtension}` + : `https://storage.googleapis.com/golang/go${version}.${osSpec}-${arch}.${fileExtension}`; // ... but not in the local cache findSpy.mockImplementation(() => ''); @@ -925,36 +926,80 @@ use . await main.run(); expect(logSpy).toHaveBeenCalledWith( - `Acquiring go${version} from ${expectedUrl}` + `Acquiring go${version} from ${expectedUrl}` ); } }, 100000); it.each(['stable', 'oldstable'])( - 'acquires latest go version with %s go-version input', - async (alias: string) => { - const arch = 'x64'; - os.platform = 'darwin'; - os.arch = arch; + 'acquires latest go version with %s go-version input', + async (alias: string) => { + const arch = 'x64'; + os.platform = 'darwin'; + os.arch = arch; - inputs['go-version'] = alias; - inputs['architecture'] = os.arch; + inputs['go-version'] = alias; + inputs['architecture'] = os.arch; - // ... but not in the local cache - findSpy.mockImplementation(() => ''); + // ... but not in the local cache + findSpy.mockImplementation(() => ''); - dlSpy.mockImplementation(async () => '/some/temp/path'); - const toolPath = path.normalize(`/cache/go/${alias}/${arch}`); - cacheSpy.mockImplementation(async () => toolPath); + dlSpy.mockImplementation(async () => '/some/temp/path'); + const toolPath = path.normalize(`/cache/go/${alias}/${arch}`); + cacheSpy.mockImplementation(async () => toolPath); - await main.run(); + await main.run(); - const releaseIndex = alias === 'stable' ? 0 : 1; + const releaseIndex = alias === 'stable' ? 0 : 1; - expect(logSpy).toHaveBeenCalledWith( - `${alias} version resolved as ${goTestManifest[releaseIndex].version}` - ); - } + expect(logSpy).toHaveBeenCalledWith( + `${alias} version resolved as ${goTestManifest[releaseIndex].version}` + ); + } ); }); + + describe('tool-versions-file', () => { + const toolVersionsFile = `terraform 1.3.7 +golang 1.16.3 +python 3.10.11 +ruby 3.2.1 +`; + + it('reads version from .tool-versions when specified', async () => { + inputs['tool-versions-file'] = '.special-tool-versions' + existsSpy.mockImplementation(() => true); + readFileSpy.mockImplementation(() => Buffer.from(toolVersionsFile)) + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.16.3'); + expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.16.3...'); + expect(logSpy).toHaveBeenCalledWith('matching 1.16.3...'); + }); + + it('is overwritten by go-version param', async () => { + inputs['go-version'] = '1.18.2' + inputs['tool-versions-file'] = '.special-tool-versions' + existsSpy.mockImplementation(() => true); + readFileSpy.mockImplementation(() => Buffer.from(toolVersionsFile)) + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.18.2'); + expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.18.2...'); + expect(logSpy).toHaveBeenCalledWith('matching 1.18.2...'); + }); + + it('uses .tool-versions as a default when no version specified', async () => { + existsSpy.mockImplementation(() => true); + readFileSpy.mockImplementation(() => Buffer.from(toolVersionsFile)) + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.16.3'); + expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.16.3...'); + expect(logSpy).toHaveBeenCalledWith('matching 1.16.3...'); + }); + }); }); diff --git a/src/installer.ts b/src/installer.ts index 013fb64..2894d3e 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -366,6 +366,13 @@ export function parseGoVersionFile(versionFilePath: string): string { return contents.trim(); } +export function parseToolVersionsFile(toolVersionsPath: string): string { + const contents = fs.readFileSync(toolVersionsPath).toString(); + + const match = contents.match(/^golang (\d+(\.\d+)*)/m); + return match ? match[1] : ''; +} + async function resolveStableVersionDist(versionSpec: string, arch: string) { const archFilter = sys.getArch(arch); const platFilter = sys.getPlatform(); diff --git a/src/main.ts b/src/main.ts index d3fb857..5e4b260 100644 --- a/src/main.ts +++ b/src/main.ts @@ -138,24 +138,41 @@ export function parseGoVersion(versionString: string): string { function resolveVersionInput(): string { let version = core.getInput('go-version'); const versionFilePath = core.getInput('go-version-file'); + let toolVersionsPath = core.getInput('tool-versions-file'); - if (version && versionFilePath) { + if (version && (versionFilePath || toolVersionsPath)) { core.warning( - 'Both go-version and go-version-file inputs are specified, only go-version will be used' + 'Multiple version inputs are specified, only go-version will be used' ); } if (version) { return version; - } - - if (versionFilePath) { + } else if (versionFilePath) { if (!fs.existsSync(versionFilePath)) { throw new Error( `The specified go version file at: ${versionFilePath} does not exist` ); } version = installer.parseGoVersionFile(versionFilePath); + } else { + // in the case of no version specification, reach for .tool-versions + if(toolVersionsPath && !fs.existsSync(toolVersionsPath)) { + throw new Error( + `The specified .tool-versions file at ${toolVersionsPath} does not exist` + ) + } + + if (!toolVersionsPath) { + toolVersionsPath = '.tool-versions' + if(!fs.existsSync(toolVersionsPath)) { + throw new Error( + `No .tool-versions file was found in the project path. Please specify using tool-versions-file` + ) + } + } + + version = installer.parseToolVersionsFile(toolVersionsPath) } return version;