diff --git a/README.md b/README.md index 2fed43f..00531a1 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,25 @@ steps: - uses: actions/checkout@master - uses: actions/setup-go@v2 with: - go-version: '1.9.3' # The Go version to download (if necessary) and use. + go-version: '1.16.1' # The Go version to download (if necessary) and use. - run: go run hello.go ``` + +Check latest version: +> In basic example, without `check-latest` flag, the action tries to resolve version from local cache firstly and download only if it is not found. Local cache on image is updated with a couple of weeks latency. +`check-latest` flag forces the action to check if the cached version is the latest one. It reduces latency significantly but it is much more likely to incur version downloading. +```yaml +steps: + - uses: actions/checkout@master + - uses: actions/setup-go@v2 + with: + go-version: '1.14' + check-latest: true + - run: go run hello.go +``` + + Matrix Testing: ```yaml jobs: diff --git a/__tests__/setup-go.test.ts b/__tests__/setup-go.test.ts index c113ccb..3ef9128 100644 --- a/__tests__/setup-go.test.ts +++ b/__tests__/setup-go.test.ts @@ -573,4 +573,158 @@ describe('setup-go', () => { it('does not convert exact versions', async () => { expect(im.makeSemver('1.13.1')).toBe('1.13.1'); }); + + describe('check-latest flag', () => { + it("use local version and don't check manifest if check-latest is not specified", async () => { + os.platform = 'linux'; + os.arch = 'x64'; + + inputs['go-version'] = '1.16'; + inputs['check-latest'] = 'false'; + + const toolPath = path.normalize('/cache/go/1.16.1/x64'); + findSpy.mockReturnValue(toolPath); + await main.run(); + + expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`); + expect(logSpy).not.toHaveBeenCalledWith( + 'Attempt to resolve the latest version from manifest...' + ); + }); + + it('check latest version and resolve it from local cache', async () => { + os.platform = 'linux'; + os.arch = 'x64'; + + inputs['go-version'] = '1.16.1'; + inputs['check-latest'] = 'true'; + + const toolPath = path.normalize('/cache/go/1.16.1/x64'); + findSpy.mockReturnValue(toolPath); + dlSpy.mockImplementation(async () => '/some/temp/path'); + exSpy.mockImplementation(async () => '/some/other/temp/path'); + cacheSpy.mockImplementation(async () => toolPath); + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith( + 'Attempt to resolve the latest version from manifest...' + ); + expect(logSpy).toHaveBeenCalledWith("Resolved as '1.16.1'"); + expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`); + }); + + it('check latest version and install it from manifest', async () => { + os.platform = 'linux'; + os.arch = 'x64'; + + inputs['go-version'] = '1.16.13'; + inputs['check-latest'] = 'true'; + + findSpy.mockImplementation(() => ''); + dlSpy.mockImplementation(async () => '/some/temp/path'); + const toolPath = path.normalize('/cache/go/1.16.13/x64'); + exSpy.mockImplementation(async () => '/some/other/temp/path'); + cacheSpy.mockImplementation(async () => toolPath); + const expectedUrl = + 'https://github.com/actions/go-versions/releases/download/1.16.13-1668091716/go-1.16.13-linux-x64.tar.gz'; + + await main.run(); + + expect(logSpy).toHaveBeenCalledWith( + 'Attempt to resolve the latest version from manifest...' + ); + // expect(logSpy).toHaveBeenCalledWith("Resolved as '1.16.13'"); + expect(logSpy).toHaveBeenCalledWith(); + expect(logSpy).toHaveBeenCalledWith( + `Acquiring 1.16.13 from ${expectedUrl}` + ); + expect(logSpy).toHaveBeenCalledWith('Extracting ...'); + }); + + it('fallback to dist if version if not found in manifest', async () => { + os.platform = 'linux'; + os.arch = 'x64'; + + // a version which is not in the manifest but is in node dist + let versionSpec = '1.16'; + + inputs['go-version'] = versionSpec; + inputs['check-latest'] = 'true'; + inputs['always-auth'] = false; + inputs['token'] = 'faketoken'; + + // ... but not in the local cache + findSpy.mockImplementation(() => ''); + + dlSpy.mockImplementation(async () => '/some/temp/path'); + // TODO: What should be here? + let toolPath = path.normalize('/cache/go/1.15.15/x64'); + exSpy.mockImplementation(async () => '/some/other/temp/path'); + cacheSpy.mockImplementation(async () => toolPath); + + await main.run(); + + let expPath = path.join(toolPath, 'bin'); + + expect(dlSpy).toHaveBeenCalled(); + expect(exSpy).toHaveBeenCalled(); + expect(logSpy).toHaveBeenCalledWith( + 'Attempt to resolve the latest version from manifest...' + ); + expect(logSpy).toHaveBeenCalledWith( + `Failed to resolve version ${versionSpec} from manifest` + ); + expect(logSpy).toHaveBeenCalledWith( + `Attempting to download ${versionSpec}...` + ); + // TODO: Is this exists. Should be something like `extPath = path.join(extPath, 'go');` + // expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`); + }); + + it('fallback to dist if manifest is not available', async () => { + os.platform = 'linux'; + os.arch = 'x64'; + + // a version which is not in the manifest but is in node dist + let versionSpec = '1.16'; + + inputs['go-version'] = versionSpec; + inputs['check-latest'] = 'true'; + inputs['always-auth'] = false; + inputs['token'] = 'faketoken'; + + // ... but not in the local cache + findSpy.mockImplementation(() => ''); + getManifestSpy.mockImplementation(() => { + throw new Error('Unable to download manifest'); + }); + + dlSpy.mockImplementation(async () => '/some/temp/path'); + let toolPath = path.normalize('/cache/go/1.16.1/x64'); + exSpy.mockImplementation(async () => '/some/other/temp/path'); + cacheSpy.mockImplementation(async () => toolPath); + + await main.run(); + + let expPath = path.join(toolPath, 'bin'); + + expect(dlSpy).toHaveBeenCalled(); + expect(exSpy).toHaveBeenCalled(); + expect(logSpy).toHaveBeenCalledWith( + 'Attempt to resolve the latest version from manifest...' + ); + expect(logSpy).toHaveBeenCalledWith( + 'Unable to resolve version from manifest...' + ); + expect(logSpy).toHaveBeenCalledWith( + `Failed to resolve version ${versionSpec} from manifest` + ); + expect(logSpy).toHaveBeenCalledWith( + `Attempting to download ${versionSpec}...` + ); + // expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`); + }); + }); + }); diff --git a/action.yml b/action.yml index 4a32e12..ede1e0a 100644 --- a/action.yml +++ b/action.yml @@ -4,6 +4,9 @@ author: 'GitHub' inputs: go-version: description: 'The Go version to download (if necessary) and use. Supports semver spec and ranges.' + check-latest: + description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec' + default: false stable: description: 'Whether to download only stable versions' default: 'true' diff --git a/src/installer.ts b/src/installer.ts index 6a835fc..7285215 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -31,10 +31,26 @@ export interface IGoVersionInfo { export async function getGo( versionSpec: string, stable: boolean, + checkLatest: boolean, auth: string | undefined ) { let osPlat: string = os.platform(); let osArch: string = os.arch(); + + if (checkLatest) { + core.info('Attempt to resolve the latest version from manifest...'); + const resolvedVersion = await resolveVersionFromManifest( + versionSpec, + stable, + auth + ); + if (resolvedVersion) { + versionSpec = resolvedVersion; + core.info(`Resolved as '${versionSpec}'`); + } else { + core.info(`Failed to resolve version ${versionSpec} from manifest`); + } + } // check cache let toolPath: string; @@ -97,6 +113,20 @@ export async function getGo( return downloadPath; } +async function resolveVersionFromManifest( + versionSpec: string, + stable: boolean, + auth: string | undefined +): Promise { + try { + const info = await getInfoFromManifest(versionSpec, stable, auth); + return info?.resolvedVersion; + } catch (err) { + core.info('Unable to resolve version from manifest...'); + core.debug(err.message); + } +} + async function installGoVersion( info: IGoVersionInfo, auth: string | undefined diff --git a/src/main.ts b/src/main.ts index 2d90b2f..409770d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -24,7 +24,9 @@ export async function run() { let token = core.getInput('token'); let auth = !token || isGhes() ? undefined : `token ${token}`; - const installDir = await installer.getGo(versionSpec, stable, auth); + const checkLatest = + (core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE'; + const installDir = await installer.getGo(versionSpec, stable, checkLatest, auth); core.exportVariable('GOROOT', installDir); core.addPath(path.join(installDir, 'bin'));