Compare commits

..

20 Commits

Author SHA1 Message Date
Maxim Lobanov d0c5defdf3
Switch manifest installation from "master" to "main" branch (#65)
* switch from master to main branch

* Update README.md
2020-07-20 12:50:40 -04:00
Dmitry Shibanov 1616116e1b
Use GitHub releases to download Go versions. (#58) 2020-06-29 11:41:13 -04:00
Bryan MacFarlane 0f551ac199
Update v2 tags in readme 2020-04-16 18:52:49 -04:00
Bryan MacFarlane e1c0a1665b
Update readme with v2 release 2020-04-16 18:52:17 -04:00
Bryan MacFarlane 78bd24e01a
Merge pull request #50 from actions/goenv
go env block
2020-04-06 09:29:27 -04:00
Bryan MacFarlane 6613fc89fc go env block 2020-04-06 08:42:55 -04:00
Bryan MacFarlane 202a594963
Merge pull request #48 from actions/matcher
More focused problem matcher regex
2020-04-03 11:51:07 -04:00
Bryan MacFarlane 0dbc7e4965 toString 2020-03-31 10:40:32 -04:00
Bryan MacFarlane 2091469f9f lint 2020-03-31 10:38:13 -04:00
Bryan MacFarlane f32657ccaf output version of go it resolved to 2020-03-31 10:37:33 -04:00
Bryan MacFarlane cec6ecefb4 output version of go it resolved to 2020-03-31 10:32:03 -04:00
Bryan MacFarlane e36ce1d6cf tabs or spaces 2020-03-30 10:46:01 -04:00
Bryan MacFarlane 89c89c5036 cleaner regex 2020-03-30 10:39:29 -04:00
Bryan MacFarlane 0a62a734da another test case 2020-03-30 09:12:21 -04:00
Bryan MacFarlane 5156bc5dd3 lint 2020-03-27 20:21:13 -04:00
Bryan MacFarlane 7837b03976 more tests 2020-03-27 20:14:29 -04:00
Bryan MacFarlane 74c8095946 lint 2020-03-27 19:58:10 -04:00
Bryan MacFarlane 34223181a5 starting 2020-03-27 19:56:10 -04:00
Bryan MacFarlane 1c06f0e82e couple tests 2020-03-27 00:55:12 -04:00
Bryan MacFarlane a030287975
Merge pull request #47 from actions/binpath
add bin to path
2020-03-26 14:08:44 -04:00
12 changed files with 4345 additions and 9849 deletions

View File

@ -1,51 +1,74 @@
name: go-versions name: Validate 'setup-go'
on: on:
push: push:
branches: branches:
- master - master
paths-ignore: paths-ignore:
- '**.md' - '**.md'
pull_request: pull_request:
paths-ignore: paths-ignore:
- '**.md' - '**.md'
schedule:
- cron: 0 0 * * *
jobs: jobs:
run: local-cache:
name: Go name: Setup local-cache version
runs-on: ${{ matrix.operating-system }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest] os: [macos-latest, windows-latest, ubuntu-latest]
go: [1.12, 1.13, 1.14]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: setup-go ${{ matrix.go }}
uses: ./
with:
go-version: ${{ matrix.go }}
- name: verify go
run: __tests__/verify-go.sh ${{ matrix.go }}
shell: bash
setup-versions-from-manifest:
name: Setup ${{ matrix.go }} ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
go: [1.12.16, 1.13.11, 1.14.3]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: setup-go ${{ matrix.go }}
uses: ./
with:
go-version: ${{ matrix.go }}
- name: verify go
run: __tests__/verify-go.sh ${{ matrix.go }}
shell: bash
setup-versions-from-dist:
name: Setup ${{ matrix.go }} ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
go: [1.7, 1.8.6]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: setup-go ${{ matrix.go }}
- name: setup-go ^1.13.6
uses: ./ uses: ./
with: with:
go-version: ^1.13.6 go-version: ${{ matrix.go }}
- name: verify go
- name: validate version run: __tests__/verify-go.sh ${{ matrix.go }}
run: go version | grep "go1."
- name: setup-go 1.13
uses: ./
with:
go-version: 1.13
- name: validate version
run: go version | grep "go1.13."
- name: setup-go 1.12.9
uses: ./
with:
go-version: 1.12.9
- name: validate version
run: go version | grep "go1.12.9"
- name: dump env
shell: bash shell: bash
run: |
echo $PATH
echo go versions in tool cache:
echo $(ls $RUNNER_TOOL_CACHE/go)

View File

@ -11,18 +11,21 @@ This action sets up a go environment for use in actions by:
- optionally downloading and caching a version of Go by version and adding to PATH - optionally downloading and caching a version of Go by version and adding to PATH
- registering problem matchers for error output - registering problem matchers for error output
# V2 Beta # V2
The V2 beta offers: The V2 offers:
- Adds GOBIN to the PATH
- Proxy Support - Proxy Support
- stable input - stable input
- Bug Fixes (including issues around version matching and semver) - Bug Fixes (including issues around version matching and semver)
It will first check the local cache for a version match. If version is not found locally, It will pull it from `main` branch of [go-versions](https://github.com/actions/go-versions/blob/main/versions-manifest.json) repository and on miss or failure, it will fall back to the previous behavior of download directly from [go dist](https://storage.googleapis.com/golang).
Matching by semver spec: Matching by semver spec:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-go@v2-beta - uses: actions/setup-go@v2
with: with:
go-version: '^1.13.1' # The Go version to download (if necessary) and use. go-version: '^1.13.1' # The Go version to download (if necessary) and use.
- run: go version - run: go version
@ -32,7 +35,7 @@ Matching an unstable pre-release:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-go@v2-beta - uses: actions/setup-go@v2
with: with:
stable: 'false' stable: 'false'
go-version: '1.14.0-rc1' # The Go version to download (if necessary) and use. go-version: '1.14.0-rc1' # The Go version to download (if necessary) and use.

View File

@ -0,0 +1,77 @@
[
{
"version": "1.12.17",
"stable": true,
"release_url": "https://github.com/actions/go-versions/releases/tag/1.12.17-20200616.21",
"files": [
{
"filename": "go-1.12.17-darwin-x64.tar.gz",
"arch": "x64",
"platform": "darwin",
"download_url": "https://github.com/actions/go-versions/releases/download/1.12.17-20200616.21/go-1.12.17-darwin-x64.tar.gz"
},
{
"filename": "go-1.12.17-linux-x64.tar.gz",
"arch": "x64",
"platform": "linux",
"download_url": "https://github.com/actions/go-versions/releases/download/1.12.17-20200616.21/go-1.12.17-linux-x64.tar.gz"
},
{
"filename": "go-1.12.17-win32-x64.zip",
"arch": "x64",
"platform": "win32",
"download_url": "https://github.com/actions/go-versions/releases/download/1.12.17-20200616.21/go-1.12.17-win32-x64.zip"
}
]
},
{
"version": "1.12.16",
"stable": true,
"release_url": "https://github.com/actions/go-versions/releases/tag/1.12.16-20200616.20",
"files": [
{
"filename": "go-1.12.16-darwin-x64.tar.gz",
"arch": "x64",
"platform": "darwin",
"download_url": "https://github.com/actions/go-versions/releases/download/1.12.16-20200616.20/go-1.12.16-darwin-x64.tar.gz"
},
{
"filename": "go-1.12.16-linux-x64.tar.gz",
"arch": "x64",
"platform": "linux",
"download_url": "https://github.com/actions/go-versions/releases/download/1.12.16-20200616.20/go-1.12.16-linux-x64.tar.gz"
},
{
"filename": "go-1.12.16-win32-x64.zip",
"arch": "x64",
"platform": "win32",
"download_url": "https://github.com/actions/go-versions/releases/download/1.12.16-20200616.20/go-1.12.16-win32-x64.zip"
}
]
},
{
"version": "1.9.7",
"stable": true,
"release_url": "https://github.com/actions/go-versions/releases/tag/1.9.7-20200616.1",
"files": [
{
"filename": "go-1.9.7-darwin-x64.tar.gz",
"arch": "x64",
"platform": "darwin",
"download_url": "https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-darwin-x64.tar.gz"
},
{
"filename": "go-1.9.7-linux-x64.tar.gz",
"arch": "x64",
"platform": "linux",
"download_url": "https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-linux-x64.tar.gz"
},
{
"filename": "go-1.9.7-win32-x64.zip",
"arch": "x64",
"platform": "win32",
"download_url": "https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-win32-x64.zip"
}
]
}
]

View File

@ -1,15 +1,18 @@
import * as tc from '@actions/tool-cache';
import * as core from '@actions/core'; import * as core from '@actions/core';
import fs = require('fs'); import * as io from '@actions/io';
import osm = require('os'); import * as tc from '@actions/tool-cache';
import path = require('path'); import fs from 'fs';
import {run} from '../src/main'; import cp from 'child_process';
import * as httpm from '@actions/http-client'; import osm from 'os';
import path from 'path';
import * as main from '../src/main';
import * as im from '../src/installer'; import * as im from '../src/installer';
import * as sys from '../src/system';
import {ITypedResponse} from '@actions/http-client/interfaces';
let goJsonData = require('./data/golang-dl.json'); let goJsonData = require('./data/golang-dl.json');
let matchers = require('../matchers.json');
let goTestManifest = require('./data/versions-manifest.json');
let matcherPattern = matchers.problemMatcher[0].pattern[0];
let matcherRegExp = new RegExp(matcherPattern.regexp);
describe('setup-go', () => { describe('setup-go', () => {
let inputs = {} as any; let inputs = {} as any;
@ -25,6 +28,12 @@ describe('setup-go', () => {
let dlSpy: jest.SpyInstance; let dlSpy: jest.SpyInstance;
let exSpy: jest.SpyInstance; let exSpy: jest.SpyInstance;
let cacheSpy: jest.SpyInstance; let cacheSpy: jest.SpyInstance;
let dbgSpy: jest.SpyInstance;
let whichSpy: jest.SpyInstance;
let existsSpy: jest.SpyInstance;
let mkdirpSpy: jest.SpyInstance;
let execSpy: jest.SpyInstance;
let getManifestSpy: jest.SpyInstance;
beforeEach(() => { beforeEach(() => {
// @actions/core // @actions/core
@ -32,24 +41,35 @@ describe('setup-go', () => {
inSpy = jest.spyOn(core, 'getInput'); inSpy = jest.spyOn(core, 'getInput');
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
// node 'os' // node
os = {}; os = {};
platSpy = jest.spyOn(osm, 'platform'); platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => os['platform']); platSpy.mockImplementation(() => os['platform']);
archSpy = jest.spyOn(osm, 'arch'); archSpy = jest.spyOn(osm, 'arch');
archSpy.mockImplementation(() => os['arch']); archSpy.mockImplementation(() => os['arch']);
execSpy = jest.spyOn(cp, 'execSync');
// @actions/tool-cache // @actions/tool-cache
findSpy = jest.spyOn(tc, 'find'); findSpy = jest.spyOn(tc, 'find');
dlSpy = jest.spyOn(tc, 'downloadTool'); dlSpy = jest.spyOn(tc, 'downloadTool');
exSpy = jest.spyOn(tc, 'extractTar'); exSpy = jest.spyOn(tc, 'extractTar');
cacheSpy = jest.spyOn(tc, 'cacheDir'); cacheSpy = jest.spyOn(tc, 'cacheDir');
getSpy = jest.spyOn(im, 'getVersions'); getSpy = jest.spyOn(im, 'getVersionsDist');
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
// io
whichSpy = jest.spyOn(io, 'which');
existsSpy = jest.spyOn(fs, 'existsSync');
mkdirpSpy = jest.spyOn(io, 'mkdirP');
// gets
getManifestSpy.mockImplementation(() => <tc.IToolRelease[]>goTestManifest);
// writes // writes
cnSpy = jest.spyOn(process.stdout, 'write'); cnSpy = jest.spyOn(process.stdout, 'write');
logSpy = jest.spyOn(console, 'log'); logSpy = jest.spyOn(core, 'info');
getSpy.mockImplementation(() => <im.IGoVersion[]>goJsonData); dbgSpy = jest.spyOn(core, 'debug');
getSpy.mockImplementation(() => <im.IGoVersion[] | null>goJsonData);
cnSpy.mockImplementation(line => { cnSpy.mockImplementation(line => {
// uncomment to debug // uncomment to debug
// process.stderr.write('write:' + line + '\n'); // process.stderr.write('write:' + line + '\n');
@ -58,36 +78,57 @@ describe('setup-go', () => {
// uncomment to debug // uncomment to debug
// process.stderr.write('log:' + line + '\n'); // process.stderr.write('log:' + line + '\n');
}); });
dbgSpy.mockImplementation(msg => {
// uncomment to see debug output
// process.stderr.write(msg + '\n');
});
}); });
afterEach(() => { afterEach(() => {
jest.resetAllMocks(); jest.resetAllMocks();
jest.clearAllMocks(); jest.clearAllMocks();
//jest.restoreAllMocks();
}); });
afterAll(async () => {}, 100000); afterAll(async () => {}, 100000);
it('can query versions', async () => { it('can find 1.9.7 from manifest on osx', async () => {
let versions: im.IGoVersion[] | null = await im.getVersions( os.platform = 'darwin';
'https://non.existant.com/path' os.arch = 'x64';
let match = await im.getInfoFromManifest('1.9.7', true, 'mocktoken');
expect(match).toBeDefined();
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'
); );
expect(versions).toBeDefined();
let l: number = versions ? versions.length : 0;
expect(l).toBe(91);
}); });
it('finds stable match for exact version', async () => { it('can find 1.9 from manifest on linux', async () => {
os.platform = 'linux';
os.arch = 'x64';
let match = await im.getInfoFromManifest('1.9.7', true, 'mocktoken');
expect(match).toBeDefined();
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'
);
});
it('can find 1.9 from manifest on windows', async () => {
os.platform = 'win32'; os.platform = 'win32';
os.arch = 'x64'; os.arch = 'x64';
// get request is already mocked let match = await im.getInfoFromManifest('1.9.7', true, 'mocktoken');
// spec: 1.13.7 => 1.13.7 (exact)
let match: im.IGoVersion | undefined = await im.findMatch('1.13.7', true);
expect(match).toBeDefined(); expect(match).toBeDefined();
let version: string = match ? match.version : ''; expect(match!.resolvedVersion).toBe('1.9.7');
expect(version).toBe('go1.13.7'); expect(match!.type).toBe('manifest');
let fileName = match ? match.files[0].filename : ''; expect(match!.downloadUrl).toBe(
expect(fileName).toBe('go1.13.7.windows-amd64.zip'); 'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-win32-x64.zip'
);
}); });
it('finds stable match for exact dot zero version', async () => { it('finds stable match for exact dot zero version', async () => {
@ -164,7 +205,7 @@ describe('setup-go', () => {
let toolPath = path.normalize('/cache/go/1.13.0/x64'); let toolPath = path.normalize('/cache/go/1.13.0/x64');
findSpy.mockImplementation(() => toolPath); findSpy.mockImplementation(() => toolPath);
await run(); await main.run();
expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`); expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`);
}); });
@ -176,7 +217,7 @@ describe('setup-go', () => {
let toolPath = path.normalize('/cache/go/1.13.0/x64'); let toolPath = path.normalize('/cache/go/1.13.0/x64');
findSpy.mockImplementation(() => toolPath); findSpy.mockImplementation(() => toolPath);
await run(); await main.run();
expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`); expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`);
}); });
@ -186,16 +227,17 @@ describe('setup-go', () => {
let toolPath = path.normalize('/cache/go/1.13.0/x64'); let toolPath = path.normalize('/cache/go/1.13.0/x64');
findSpy.mockImplementation(() => toolPath); findSpy.mockImplementation(() => toolPath);
await run(); await main.run();
let expPath = path.join(toolPath, 'bin'); let expPath = path.join(toolPath, 'bin');
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
}); });
it('finds a version in the cache and adds it to the path', async () => { it('finds a version in the cache and adds it to the path', async () => {
inputs['go-version'] = '1.13.0'; inputs['go-version'] = '1.13.0';
let toolPath = path.normalize('/cache/go/1.13.0/x64'); let toolPath = path.normalize('/cache/go/1.13.0/x64');
findSpy.mockImplementation(() => toolPath); findSpy.mockImplementation(() => toolPath);
await run(); await main.run();
let expPath = path.join(toolPath, 'bin'); let expPath = path.join(toolPath, 'bin');
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`); expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
@ -208,7 +250,7 @@ describe('setup-go', () => {
findSpy.mockImplementation(() => { findSpy.mockImplementation(() => {
throw new Error(errMsg); throw new Error(errMsg);
}); });
await run(); await main.run();
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL); expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
}); });
@ -223,7 +265,7 @@ describe('setup-go', () => {
let toolPath = path.normalize('/cache/go/1.13.0/x64'); let toolPath = path.normalize('/cache/go/1.13.0/x64');
exSpy.mockImplementation(() => '/some/other/temp/path'); exSpy.mockImplementation(() => '/some/other/temp/path');
cacheSpy.mockImplementation(() => toolPath); cacheSpy.mockImplementation(() => toolPath);
await run(); await main.run();
let expPath = path.join(toolPath, 'bin'); let expPath = path.join(toolPath, 'bin');
@ -239,13 +281,127 @@ describe('setup-go', () => {
inputs['go-version'] = '9.99.9'; inputs['go-version'] = '9.99.9';
findSpy.mockImplementation(() => ''); findSpy.mockImplementation(() => '');
await run(); await main.run();
expect(cnSpy).toHaveBeenCalledWith( expect(cnSpy).toHaveBeenCalledWith(
`::error::Could not find a version that satisfied version spec: 9.99.9${osm.EOL}` `::error::Unable to find Go version '9.99.9' for platform linux and architecture x64.${osm.EOL}`
); );
}); });
it('downloads a version from a manifest match', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is in the manifest
let versionSpec = '1.12.16';
inputs['go-version'] = versionSpec;
inputs['token'] = 'faketoken';
let expectedUrl =
'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(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
let toolPath = path.normalize('/cache/go/1.12.16/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).not.toHaveBeenCalledWith(
'Not found in manifest. Falling back to download directly from Go'
);
expect(logSpy).toHaveBeenCalledWith(
`Acquiring 1.12.16 from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith(`Added go to the path`);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('downloads a major and minor from a manifest match', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is in the manifest
let versionSpec = '1.12';
inputs['go-version'] = versionSpec;
inputs['token'] = 'faketoken';
let expectedUrl =
'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(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
let toolPath = path.normalize('/cache/go/1.12.17/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).not.toHaveBeenCalledWith(
'Not found in manifest. Falling back to download directly from Go'
);
expect(logSpy).toHaveBeenCalledWith(
`Acquiring 1.12.17 from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith(`Added go to the path`);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('falls back to a version from node dist', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
let versionSpec = '1.12.14';
inputs['go-version'] = versionSpec;
inputs['token'] = 'faketoken';
let expectedUrl =
'https://github.com/actions/go-versions/releases/download/1.12.14-20200616.18/go-1.12.14-linux-x64.tar.gz';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
let toolPath = path.normalize('/cache/go/1.12.14/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
let expPath = path.join(toolPath, 'bin');
expect(logSpy).toHaveBeenCalledWith('Setup go stable version spec 1.12.14');
expect(findSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.12.14...');
expect(dlSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith('matching 1.12.14...');
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
'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`);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('reports a failed download', async () => { it('reports a failed download', async () => {
let errMsg = 'unhandled download message'; let errMsg = 'unhandled download message';
os.platform = 'linux'; os.platform = 'linux';
@ -257,27 +413,131 @@ describe('setup-go', () => {
dlSpy.mockImplementation(() => { dlSpy.mockImplementation(() => {
throw new Error(errMsg); throw new Error(errMsg);
}); });
await run(); await main.run();
expect(cnSpy).toHaveBeenCalledWith( 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}`
); );
}); });
it('reports empty query results', async () => { it('does not add BIN if go is not in path', async () => {
let errMsg = 'unhandled download message'; whichSpy.mockImplementation(async () => {
os.platform = 'linux'; return '';
os.arch = 'x64'; });
let added = await main.addBinToPath();
expect(added).toBeFalsy();
});
inputs['go-version'] = '1.13.1'; it('adds bin if dir not exists', async () => {
whichSpy.mockImplementation(async () => {
return '/usr/local/go/bin/go';
});
findSpy.mockImplementation(() => ''); execSpy.mockImplementation(() => {
getSpy.mockImplementation(() => null); return '/Users/testuser/go';
await run(); });
expect(cnSpy).toHaveBeenCalledWith( mkdirpSpy.mockImplementation(async () => {});
`::error::Failed to download version 1.13.1: Error: golang download url did not return results${osm.EOL}` existsSpy.mockImplementation(path => {
); return false;
});
let added = await main.addBinToPath();
expect(added).toBeTruthy;
});
interface Annotation {
file: string;
line: number;
column: number;
message: string;
}
//
// problem matcher regex pattern tests
function testMatch(line: string): Annotation {
let annotation = <Annotation>{};
let match = matcherRegExp.exec(line);
if (match) {
annotation.line = parseInt(match[matcherPattern.line], 10);
annotation.column = parseInt(match[matcherPattern.column], 10);
annotation.file = match[matcherPattern.file].trim();
annotation.message = match[matcherPattern.message].trim();
}
return annotation;
}
it('matches on relative unix path', async () => {
let line = './main.go:13:2: undefined: fmt.Printl';
let annotation = testMatch(line);
expect(annotation).toBeDefined();
expect(annotation.line).toBe(13);
expect(annotation.column).toBe(2);
expect(annotation.file).toBe('./main.go');
expect(annotation.message).toBe('undefined: fmt.Printl');
});
it('matches on unix path up the tree', async () => {
let line = '../main.go:13:2: undefined: fmt.Printl';
let annotation = testMatch(line);
expect(annotation).toBeDefined();
expect(annotation.line).toBe(13);
expect(annotation.column).toBe(2);
expect(annotation.file).toBe('../main.go');
expect(annotation.message).toBe('undefined: fmt.Printl');
});
it('matches on rooted unix path', async () => {
let line = '/assert.go:4:1: missing return at end of function';
let annotation = testMatch(line);
expect(annotation).toBeDefined();
expect(annotation.line).toBe(4);
expect(annotation.column).toBe(1);
expect(annotation.file).toBe('/assert.go');
expect(annotation.message).toBe('missing return at end of function');
});
it('matches on unix path with spaces', async () => {
let line = ' ./assert.go:5:2: missing return at end of function ';
let annotation = testMatch(line);
expect(annotation).toBeDefined();
expect(annotation.line).toBe(5);
expect(annotation.column).toBe(2);
expect(annotation.file).toBe('./assert.go');
expect(annotation.message).toBe('missing return at end of function');
});
it('matches on unix path with tabs', async () => {
let line = '\t./assert.go:5:2: missing return at end of function ';
let annotation = testMatch(line);
expect(annotation).toBeDefined();
expect(annotation.line).toBe(5);
expect(annotation.column).toBe(2);
expect(annotation.file).toBe('./assert.go');
expect(annotation.message).toBe('missing return at end of function');
});
it('matches on relative windows path', async () => {
let line = '.\\main.go:13:2: undefined: fmt.Printl';
let annotation = testMatch(line);
expect(annotation).toBeDefined();
expect(annotation.line).toBe(13);
expect(annotation.column).toBe(2);
expect(annotation.file).toBe('.\\main.go');
expect(annotation.message).toBe('undefined: fmt.Printl');
});
it('matches on windows path up the tree', async () => {
let line = '..\\main.go:13:2: undefined: fmt.Printl';
let annotation = testMatch(line);
expect(annotation).toBeDefined();
expect(annotation.line).toBe(13);
expect(annotation.column).toBe(2);
expect(annotation.file).toBe('..\\main.go');
expect(annotation.message).toBe('undefined: fmt.Printl');
}); });
// 1.13.1 => 1.13.1 // 1.13.1 => 1.13.1

14
__tests__/verify-go.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
if [ -z "$1" ]; then
echo "Must supply go version argument"
exit 1
fi
go_version="$(go version)"
echo "Found go version '$go_version'"
if [ -z "$(echo $go_version | grep $1)" ]; then
echo "Unexpected version"
exit 1
fi

View File

@ -7,6 +7,9 @@ inputs:
stable: stable:
description: 'Whether to download only stable versions' description: 'Whether to download only stable versions'
default: 'true' default: 'true'
token:
description: Used to pull node distributions from go-versions. Since there's a default, this is typically not supplied by the user.
default: ${{ github.token }}
runs: runs:
using: 'node12' using: 'node12'
main: 'dist/index.js' main: 'dist/index.js'

691
dist/index.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,11 @@
"owner": "go", "owner": "go",
"pattern": [ "pattern": [
{ {
"regexp": "^([^:]*: )?((.:)?[^:]*):(\\d+)(:(\\d+))?: (.*)$", "regexp": "^\\s*(\\.{0,2}[\\/\\\\].+\\.go):(?:(\\d+):(\\d+):)? (.*)",
"file": 2, "file": 1,
"line": 4, "line": 2,
"column": 6, "column": 3,
"message": 7 "message": 4
} }
] ]
} }

12644
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,10 +23,10 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.2.2", "@actions/core": "^1.2.3",
"@actions/http-client": "^1.0.6", "@actions/http-client": "^1.0.6",
"@actions/io": "^1.0.2", "@actions/io": "^1.0.2",
"@actions/tool-cache": "^1.3.3", "@actions/tool-cache": "^1.5.5",
"semver": "^6.1.1" "semver": "^6.1.1"
}, },
"devDependencies": { "devDependencies": {
@ -39,6 +39,6 @@
"nock": "^10.0.6", "nock": "^10.0.6",
"prettier": "^1.17.1", "prettier": "^1.17.1",
"ts-jest": "^24.0.2", "ts-jest": "^24.0.2",
"typescript": "^3.5.1" "typescript": "^3.8.3"
} }
} }

View File

@ -1,46 +1,12 @@
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import * as core from '@actions/core';
import * as path from 'path'; import * as path from 'path';
import * as semver from 'semver'; import * as semver from 'semver';
import * as httpm from '@actions/http-client'; import * as httpm from '@actions/http-client';
import * as sys from './system'; import * as sys from './system';
import {debug} from '@actions/core'; import os from 'os';
export async function downloadGo( type InstallationType = 'dist' | 'manifest';
versionSpec: string,
stable: boolean
): Promise<string | undefined> {
let toolPath: string | undefined;
try {
let match: IGoVersion | undefined = await findMatch(versionSpec, stable);
if (match) {
// download
debug(`match ${match.version}`);
let downloadUrl: string = `https://storage.googleapis.com/golang/${match.files[0].filename}`;
console.log(`Downloading from ${downloadUrl}`);
let downloadPath: string = await tc.downloadTool(downloadUrl);
debug(`downloaded to ${downloadPath}`);
// extract
console.log('Extracting ...');
let extPath: string =
sys.getPlatform() == 'windows'
? await tc.extractZip(downloadPath)
: await tc.extractTar(downloadPath);
debug(`extracted to ${extPath}`);
// extracts with a root folder that matches the fileName downloaded
const toolRoot = path.join(extPath, 'go');
toolPath = await tc.cacheDir(toolRoot, 'go', makeSemver(match.version));
}
} catch (error) {
throw new Error(`Failed to download version ${versionSpec}: ${error}`);
}
return toolPath;
}
export interface IGoVersionFile { export interface IGoVersionFile {
filename: string; filename: string;
@ -55,6 +21,165 @@ export interface IGoVersion {
files: IGoVersionFile[]; files: IGoVersionFile[];
} }
export interface IGoVersionInfo {
type: InstallationType;
downloadUrl: string;
resolvedVersion: string;
fileName: string;
}
export async function getGo(
versionSpec: string,
stable: boolean,
auth: string | undefined
) {
let osPlat: string = os.platform();
let osArch: string = os.arch();
// check cache
let toolPath: string;
toolPath = tc.find('go', versionSpec);
// If not found in cache, download
if (toolPath) {
core.info(`Found in cache @ ${toolPath}`);
return toolPath;
}
core.info(`Attempting to download ${versionSpec}...`);
let downloadPath = '';
let info: IGoVersionInfo | null = null;
//
// Try download from internal distribution (popular versions only)
//
try {
info = await getInfoFromManifest(versionSpec, stable, auth);
if (info) {
downloadPath = await installGoVersion(info, auth);
} else {
core.info(
'Not found in manifest. Falling back to download directly from Go'
);
}
} catch (err) {
if (
err instanceof tc.HTTPError &&
(err.httpStatusCode === 403 || err.httpStatusCode === 429)
) {
core.info(
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
);
} else {
core.info(err.message);
}
core.debug(err.stack);
core.info('Falling back to download directly from Go');
}
//
// Download from storage.googleapis.com
//
if (!downloadPath) {
info = await getInfoFromDist(versionSpec, stable);
if (!info) {
throw new Error(
`Unable to find Go version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
);
}
try {
core.info('Install from dist');
downloadPath = await installGoVersion(info, undefined);
} catch (err) {
throw new Error(`Failed to download version ${versionSpec}: ${err}`);
}
}
return downloadPath;
}
async function installGoVersion(
info: IGoVersionInfo,
auth: string | undefined
): Promise<string> {
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
const downloadPath = await tc.downloadTool(info.downloadUrl, undefined, auth);
core.info('Extracting Go...');
let extPath = await extractGoArchive(downloadPath);
core.info(`Successfully extracted go to ${extPath}`);
if (info.type === 'dist') {
extPath = path.join(extPath, 'go');
}
core.info('Adding to the cache ...');
const cachedDir = await tc.cacheDir(
extPath,
'go',
makeSemver(info.resolvedVersion)
);
core.info(`Successfully cached go to ${cachedDir}`);
return cachedDir;
}
export async function extractGoArchive(archivePath: string): Promise<string> {
const arch = os.arch();
let extPath: string;
if (arch === 'win32') {
extPath = await tc.extractZip(archivePath);
} else {
extPath = await tc.extractTar(archivePath);
}
return extPath;
}
export async function getInfoFromManifest(
versionSpec: string,
stable: boolean,
auth: string | undefined
): Promise<IGoVersionInfo | null> {
let info: IGoVersionInfo | null = null;
const releases = await tc.getManifestFromRepo(
'actions',
'go-versions',
auth,
'main'
);
core.info(`matching ${versionSpec}...`);
const rel = await tc.findFromManifest(versionSpec, stable, releases);
if (rel && rel.files.length > 0) {
info = <IGoVersionInfo>{};
info.type = 'manifest';
info.resolvedVersion = rel.version;
info.downloadUrl = rel.files[0].download_url;
info.fileName = rel.files[0].filename;
}
return info;
}
async function getInfoFromDist(
versionSpec: string,
stable: boolean
): Promise<IGoVersionInfo | null> {
let version: IGoVersion | undefined;
version = await findMatch(versionSpec, stable);
if (!version) {
return null;
}
let downloadUrl: string = `https://storage.googleapis.com/golang/${version.files[0].filename}`;
return <IGoVersionInfo>{
type: 'dist',
downloadUrl: downloadUrl,
resolvedVersion: version.version,
fileName: version.files[0].filename
};
}
export async function findMatch( export async function findMatch(
versionSpec: string, versionSpec: string,
stable: boolean stable: boolean
@ -66,7 +191,9 @@ export async function findMatch(
let match: IGoVersion | undefined; let match: IGoVersion | undefined;
const dlUrl: string = 'https://golang.org/dl/?mode=json&include=all'; const dlUrl: string = 'https://golang.org/dl/?mode=json&include=all';
let candidates: IGoVersion[] | null = await module.exports.getVersions(dlUrl); let candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
dlUrl
);
if (!candidates) { if (!candidates) {
throw new Error(`golang download url did not return results`); throw new Error(`golang download url did not return results`);
} }
@ -83,18 +210,20 @@ export async function findMatch(
version = version + '.0'; version = version + '.0';
} }
debug(`check ${version} satisfies ${versionSpec}`); core.debug(`check ${version} satisfies ${versionSpec}`);
if ( if (
semver.satisfies(version, versionSpec) && semver.satisfies(version, versionSpec) &&
(!stable || candidate.stable === stable) (!stable || candidate.stable === stable)
) { ) {
goFile = candidate.files.find(file => { goFile = candidate.files.find(file => {
debug(`${file.arch}===${archFilter} && ${file.os}===${platFilter}`); core.debug(
`${file.arch}===${archFilter} && ${file.os}===${platFilter}`
);
return file.arch === archFilter && file.os === platFilter; return file.arch === archFilter && file.os === platFilter;
}); });
if (goFile) { if (goFile) {
debug(`matched ${candidate.version}`); core.debug(`matched ${candidate.version}`);
match = candidate; match = candidate;
break; break;
} }
@ -110,9 +239,14 @@ export async function findMatch(
return result; return result;
} }
export async function getVersions(dlUrl: string): Promise<IGoVersion[] | null> { export async function getVersionsDist(
dlUrl: string
): Promise<IGoVersion[] | null> {
// this returns versions descending so latest is first // this returns versions descending so latest is first
let http: httpm.HttpClient = new httpm.HttpClient('setup-go'); let http: httpm.HttpClient = new httpm.HttpClient('setup-go', [], {
allowRedirects: true,
maxRedirects: 3
});
return (await http.getJson<IGoVersion[]>(dlUrl)).result; return (await http.getJson<IGoVersion[]>(dlUrl)).result;
} }

View File

@ -1,10 +1,10 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as io from '@actions/io'; import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as installer from './installer'; import * as installer from './installer';
import * as path from 'path'; import path from 'path';
import * as cp from 'child_process'; import cp from 'child_process';
import * as fs from 'fs'; import fs from 'fs';
import {URL} from 'url';
export async function run() { export async function run() {
try { try {
@ -18,44 +18,42 @@ export async function run() {
// since getting unstable versions should be explicit // since getting unstable versions should be explicit
let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE'; let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
console.log( core.info(`Setup go ${stable ? 'stable' : ''} version spec ${versionSpec}`);
`Setup go ${stable ? 'stable' : ''} version spec ${versionSpec}`
);
if (versionSpec) { if (versionSpec) {
let installDir: string | undefined = tc.find('go', versionSpec); let token = core.getInput('token');
let auth = !token || isGhes() ? undefined : `token ${token}`;
if (!installDir) { const installDir = await installer.getGo(versionSpec, stable, auth);
console.log(
`A version satisfying ${versionSpec} not found locally, attempting to download ...`
);
installDir = await installer.downloadGo(versionSpec, stable);
console.log('Installed');
}
if (installDir) { core.exportVariable('GOROOT', installDir);
core.exportVariable('GOROOT', installDir); core.addPath(path.join(installDir, 'bin'));
core.addPath(path.join(installDir, 'bin')); core.info('Added go to the path');
console.log('Added go to the path');
let added = addBinToPath(); let added = await addBinToPath();
core.debug(`add bin ${added}`); core.debug(`add bin ${added}`);
} else { core.info(`Successfully setup go version ${versionSpec}`);
throw new Error(
`Could not find a version that satisfied version spec: ${versionSpec}`
);
}
} }
// add problem matchers // add problem matchers
const matchersPath = path.join(__dirname, '..', 'matchers.json'); const matchersPath = path.join(__dirname, '..', 'matchers.json');
console.log(`##[add-matcher]${matchersPath}`); core.info(`##[add-matcher]${matchersPath}`);
// output the version actually being used
let goPath = await io.which('go');
let goVersion = (cp.execSync(`${goPath} version`) || '').toString();
core.info(goVersion);
core.startGroup('go env');
let goEnv = (cp.execSync(`${goPath} env`) || '').toString();
core.info(goEnv);
core.endGroup();
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
} }
} }
async function addBinToPath(): Promise<boolean> { export async function addBinToPath(): Promise<boolean> {
let added = false; let added = false;
let g = await io.which('go'); let g = await io.which('go');
core.debug(`which go :${g}:`); core.debug(`which go :${g}:`);
@ -85,3 +83,10 @@ async function addBinToPath(): Promise<boolean> {
} }
return added; return added;
} }
function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
);
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}