Compare commits

..

No commits in common. "master" and "binpath" have entirely different histories.

12 changed files with 9824 additions and 4320 deletions

View File

@ -1,4 +1,4 @@
name: Validate 'setup-go' name: go-versions
on: on:
push: push:
branches: branches:
@ -8,67 +8,44 @@ on:
pull_request: pull_request:
paths-ignore: paths-ignore:
- '**.md' - '**.md'
schedule:
- cron: 0 0 * * *
jobs: jobs:
local-cache: run:
name: Setup local-cache version name: Go
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.operating-system }}
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-latest] operating-system: [ubuntu-latest, windows-latest, macos-latest]
go: [1.12, 1.13, 1.14]
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: ${{ matrix.go }} go-version: ^1.13.6
- name: verify go - name: validate version
run: __tests__/verify-go.sh ${{ matrix.go }} run: go version | grep "go1."
shell: bash
setup-versions-from-manifest: - name: setup-go 1.13
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: ./ uses: ./
with: with:
go-version: ${{ matrix.go }} go-version: 1.13
- name: verify go - name: validate version
run: __tests__/verify-go.sh ${{ matrix.go }} run: go version | grep "go1.13."
shell: bash
setup-versions-from-dist: - name: setup-go 1.12.9
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:
- name: Checkout
uses: actions/checkout@v2
- name: setup-go ${{ matrix.go }}
uses: ./ uses: ./
with: with:
go-version: ${{ matrix.go }} go-version: 1.12.9
- name: verify go
run: __tests__/verify-go.sh ${{ matrix.go }} - 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,21 +11,18 @@ 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 # V2 Beta
The V2 offers: The V2 beta 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 - uses: actions/setup-go@v2-beta
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
@ -35,7 +32,7 @@ Matching an unstable pre-release:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2-beta
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

@ -1,77 +0,0 @@
[
{
"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,18 +1,15 @@
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import fs from 'fs'; import * as core from '@actions/core';
import cp from 'child_process'; import fs = require('fs');
import osm from 'os'; import osm = require('os');
import path from 'path'; import path = require('path');
import * as main from '../src/main'; import {run} from '../src/main';
import * as httpm from '@actions/http-client';
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;
@ -28,12 +25,6 @@ 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
@ -41,35 +32,24 @@ describe('setup-go', () => {
inSpy = jest.spyOn(core, 'getInput'); inSpy = jest.spyOn(core, 'getInput');
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
// node // node 'os'
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, 'getVersionsDist'); getSpy = jest.spyOn(im, 'getVersions');
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(core, 'info'); logSpy = jest.spyOn(console, 'log');
dbgSpy = jest.spyOn(core, 'debug'); getSpy.mockImplementation(() => <im.IGoVersion[]>goJsonData);
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');
@ -78,57 +58,36 @@ 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 find 1.9.7 from manifest on osx', async () => { it('can query versions', async () => {
os.platform = 'darwin'; let versions: im.IGoVersion[] | null = await im.getVersions(
os.arch = 'x64'; 'https://non.existant.com/path'
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('can find 1.9 from manifest on linux', async () => { it('finds stable match for exact version', 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';
let match = await im.getInfoFromManifest('1.9.7', true, 'mocktoken'); // get request is already mocked
// 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();
expect(match!.resolvedVersion).toBe('1.9.7'); let version: string = match ? match.version : '';
expect(match!.type).toBe('manifest'); expect(version).toBe('go1.13.7');
expect(match!.downloadUrl).toBe( let fileName = match ? match.files[0].filename : '';
'https://github.com/actions/go-versions/releases/download/1.9.7/go-1.9.7-win32-x64.zip' expect(fileName).toBe('go1.13.7.windows-amd64.zip');
);
}); });
it('finds stable match for exact dot zero version', async () => { it('finds stable match for exact dot zero version', async () => {
@ -205,7 +164,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 main.run(); await run();
expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`); expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`);
}); });
@ -217,7 +176,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 main.run(); await run();
expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`); expect(logSpy).toHaveBeenCalledWith(`Setup go stable version spec 1.13.0`);
}); });
@ -227,17 +186,16 @@ 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 main.run(); await 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 main.run(); await 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}`);
@ -250,7 +208,7 @@ describe('setup-go', () => {
findSpy.mockImplementation(() => { findSpy.mockImplementation(() => {
throw new Error(errMsg); throw new Error(errMsg);
}); });
await main.run(); await run();
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL); expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
}); });
@ -265,7 +223,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 main.run(); await run();
let expPath = path.join(toolPath, 'bin'); let expPath = path.join(toolPath, 'bin');
@ -281,127 +239,13 @@ describe('setup-go', () => {
inputs['go-version'] = '9.99.9'; inputs['go-version'] = '9.99.9';
findSpy.mockImplementation(() => ''); findSpy.mockImplementation(() => '');
await main.run(); await run();
expect(cnSpy).toHaveBeenCalledWith( expect(cnSpy).toHaveBeenCalledWith(
`::error::Unable to find Go version '9.99.9' for platform linux and architecture x64.${osm.EOL}` `::error::Could not find a version that satisfied version spec: 9.99.9${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';
@ -413,131 +257,27 @@ describe('setup-go', () => {
dlSpy.mockImplementation(() => { dlSpy.mockImplementation(() => {
throw new Error(errMsg); throw new Error(errMsg);
}); });
await main.run(); await 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('does not add BIN if go is not in path', async () => { it('reports empty query results', async () => {
whichSpy.mockImplementation(async () => { let errMsg = 'unhandled download message';
return ''; os.platform = 'linux';
}); os.arch = 'x64';
let added = await main.addBinToPath();
expect(added).toBeFalsy();
});
it('adds bin if dir not exists', async () => { inputs['go-version'] = '1.13.1';
whichSpy.mockImplementation(async () => {
return '/usr/local/go/bin/go';
});
execSpy.mockImplementation(() => { findSpy.mockImplementation(() => '');
return '/Users/testuser/go'; getSpy.mockImplementation(() => null);
}); await run();
mkdirpSpy.mockImplementation(async () => {}); expect(cnSpy).toHaveBeenCalledWith(
existsSpy.mockImplementation(path => { `::error::Failed to download version 1.13.1: Error: golang download url did not return results${osm.EOL}`
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

View File

@ -1,14 +0,0 @@
#!/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,9 +7,6 @@ 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'

669
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": "^\\s*(\\.{0,2}[\\/\\\\].+\\.go):(?:(\\d+):(\\d+):)? (.*)", "regexp": "^([^:]*: )?((.:)?[^:]*):(\\d+)(:(\\d+))?: (.*)$",
"file": 1, "file": 2,
"line": 2, "line": 4,
"column": 3, "column": 6,
"message": 4 "message": 7
} }
] ]
} }

13088
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.3", "@actions/core": "^1.2.2",
"@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.5.5", "@actions/tool-cache": "^1.3.3",
"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.8.3" "typescript": "^3.5.1"
} }
} }

View File

@ -1,12 +1,46 @@
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 os from 'os'; import {debug} from '@actions/core';
type InstallationType = 'dist' | 'manifest'; export async function downloadGo(
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;
@ -21,165 +55,6 @@ 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
@ -191,9 +66,7 @@ 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.getVersionsDist( let candidates: IGoVersion[] | null = await module.exports.getVersions(dlUrl);
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`);
} }
@ -210,20 +83,18 @@ export async function findMatch(
version = version + '.0'; version = version + '.0';
} }
core.debug(`check ${version} satisfies ${versionSpec}`); 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 => {
core.debug( debug(`${file.arch}===${archFilter} && ${file.os}===${platFilter}`);
`${file.arch}===${archFilter} && ${file.os}===${platFilter}`
);
return file.arch === archFilter && file.os === platFilter; return file.arch === archFilter && file.os === platFilter;
}); });
if (goFile) { if (goFile) {
core.debug(`matched ${candidate.version}`); debug(`matched ${candidate.version}`);
match = candidate; match = candidate;
break; break;
} }
@ -239,14 +110,9 @@ export async function findMatch(
return result; return result;
} }
export async function getVersionsDist( export async function getVersions(dlUrl: string): Promise<IGoVersion[] | null> {
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 path from 'path'; import * as path from 'path';
import cp from 'child_process'; import * as cp from 'child_process';
import fs from 'fs'; import * as fs from 'fs';
import {URL} from 'url';
export async function run() { export async function run() {
try { try {
@ -18,42 +18,44 @@ 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';
core.info(`Setup go ${stable ? 'stable' : ''} version spec ${versionSpec}`); console.log(
`Setup go ${stable ? 'stable' : ''} version spec ${versionSpec}`
);
if (versionSpec) { if (versionSpec) {
let token = core.getInput('token'); let installDir: string | undefined = tc.find('go', versionSpec);
let auth = !token || isGhes() ? undefined : `token ${token}`;
const installDir = await installer.getGo(versionSpec, stable, auth); if (!installDir) {
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 = await addBinToPath(); let added = addBinToPath();
core.debug(`add bin ${added}`); core.debug(`add bin ${added}`);
core.info(`Successfully setup go version ${versionSpec}`); } else {
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');
core.info(`##[add-matcher]${matchersPath}`); console.log(`##[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);
} }
} }
export async function addBinToPath(): Promise<boolean> { 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}:`);
@ -83,10 +85,3 @@ export 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';
}