Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for gotip and latest #34

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ jobs:
name: Run
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest]
steps:
Expand All @@ -14,13 +15,14 @@ jobs:
- name: Set Node.js 10.x
uses: actions/setup-node@master
with:
version: 10.x
node-version: 10.x

- name: npm install
run: npm install

- name: Lint
run: npm run format-check

- name: npm test
run: npm test

- name: Lint
run: npm run format-check
continue-on-error: true
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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 building and caching Go tip (master branch) from source
- registering problem matchers for error output

# Usage
Expand All @@ -30,7 +31,7 @@ jobs:
runs-on: ubuntu-16.04
strategy:
matrix:
go: [ '1.8', '1.9.3', '1.10.x' ]
go: [ '1.8', '1.9.3', '1.10.x', 'latest', 'tip' ]
name: Go ${{ matrix.go }} sample
steps:
- uses: actions/checkout@master
Expand Down
195 changes: 102 additions & 93 deletions __tests__/installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,116 +11,125 @@ const dataDir = path.join(__dirname, 'data');
process.env['RUNNER_TOOL_CACHE'] = toolDir;
process.env['RUNNER_TEMP'] = tempDir;
import * as installer from '../src/installer';
import * as executil from '../src/executil';

const osarch = os.arch();
const IS_WINDOWS = process.platform === 'win32';

describe('installer tests', () => {
beforeAll(async () => {
await io.rmRF(toolDir);
await io.rmRF(tempDir);
}, 100000);

afterAll(async () => {
try {
await io.rmRF(toolDir);
await io.rmRF(tempDir);
} catch {
console.log('Failed to remove test directories');
}
}, 100000);

it('Acquires version of go if no matching version is installed', async () => {
await installer.getGo('1.10.8');
const goDir = path.join(toolDir, 'go', '1.10.8', os.arch());

expect(fs.existsSync(`${goDir}.complete`)).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(goDir, 'bin', 'go.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(goDir, 'bin', 'go'))).toBe(true);
}
}, 100000);

describe('the latest release of a go version', () => {
beforeEach(() => {
const goExe: string = IS_WINDOWS ? 'go.exe' : 'go';

const cleanup = async () => {
await io.rmRF(toolDir);
await io.rmRF(tempDir);
}
beforeAll(cleanup, 100000);
afterAll(cleanup, 100000);

let bootstrapCgo: string = '';
let bootstrapGo: string = '';
beforeAll(async () => {
bootstrapGo = await io.which('go', false);
if (!bootstrapGo) {
return;
}
bootstrapCgo = await executil.goEnv('CGO_ENABLED', bootstrapGo);
// Override the value for acquireGo, because running getGo tests
// exports different GOROOT each time.
const bootstrapGoroot = await executil.goEnv('GOROOT', bootstrapGo);
process.env['GOROOT_BOOTSTRAP'] = bootstrapGoroot
})

const describeTable = describe.each([
['tip', '+60f14fd', 'go1.13beta1', '0.0.0-devel.60f14fddfee107dedd76c0be6b422a3d8ccc841a'],
['tip', '+a5bfd9d', 'go1.14beta1', '0.0.0-devel.a5bfd9da1d1b24f326399b6b75558ded14514f23'],
['latest', 'go1.13', 'n/a', '1.13.0'],
['1.x', 'go1.13', 'n/a', '1.13.0'],
['1.10.x', 'go1.10.8', 'n/a', '1.10.8'],
['1.10.8', 'go1.10.8', 'n/a', '1.10.8'],
['1.10', 'go1.10', 'n/a', '1.10.0'],
]);
describeTable('Go %s (%s)', (version: string, goVersion: string, gitRef: string, normVersion: string) => {
const gotip = version == 'tip';
const cacheDir = gotip ? 'gotip' : 'go';
const goRoot = path.join(toolDir, cacheDir, normVersion, osarch);
const goTool = path.join(goRoot, 'bin', goExe);

if (!gotip) {
beforeAll(() => {
nock('https://golang.org')
.get('/dl/')
.query({mode: 'json', include: 'all'})
.replyWithFile(200, path.join(dataDir, 'golang-dl.json'));
});

afterEach(() => {
afterAll(() => {
nock.cleanAll();
nock.enableNetConnect();
});
}

const timeout = gotip ? 300000 : 100000;
test('installation', async () => {
const promise = installer.getGo(version, gitRef, bootstrapGo);
await expect(promise).resolves.toBeUndefined();
}, timeout);

if (gotip) {
const gitDir = path.join(toolDir, cacheDir, '0.0.0-devel', 'noarch');
test('git cache check', async () => {
const promise = fs.promises.access(gitDir);
await expect(promise).resolves.toBeUndefined();
});
}

it('Acquires latest release version of go 1.10 if using 1.10 and no matching version is installed', async () => {
await installer.getGo('1.10');
const goDir = path.join(toolDir, 'go', '1.10.8', os.arch());

expect(fs.existsSync(`${goDir}.complete`)).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(goDir, 'bin', 'go.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(goDir, 'bin', 'go'))).toBe(true);
}
}, 100000);

it('Acquires latest release version of go 1.10 if using 1.10.x and no matching version is installed', async () => {
await installer.getGo('1.10.x');
const goDir = path.join(toolDir, 'go', '1.10.8', os.arch());

expect(fs.existsSync(`${goDir}.complete`)).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(goDir, 'bin', 'go.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(goDir, 'bin', 'go'))).toBe(true);
}
}, 100000);

it('Acquires latest release version of go if using 1.x and no matching version is installed', async () => {
await installer.getGo('1.x');
const goDir = path.join(toolDir, 'go', '1.13.0', os.arch());

expect(fs.existsSync(`${goDir}.complete`)).toBe(true);
if (IS_WINDOWS) {
expect(fs.existsSync(path.join(goDir, 'bin', 'go.exe'))).toBe(true);
} else {
expect(fs.existsSync(path.join(goDir, 'bin', 'go'))).toBe(true);
}
}, 100000);
test('tool existence check', async () => {
const promise = fs.promises.access(goTool);
await expect(promise).resolves.toBeUndefined();
});

it('Throws if no location contains correct go version', async () => {
let thrown = false;
try {
await installer.getGo('1000.0');
} catch {
thrown = true;
}
expect(thrown).toBe(true);
test('cache completeness check', async () => {
const promise = fs.promises.access(`${goRoot}.complete`);
await expect(promise).resolves.toBeUndefined();
});

it('Uses version of go installed in cache', async () => {
const goDir: string = path.join(toolDir, 'go', '250.0.0', os.arch());
await io.mkdirP(goDir);
fs.writeFileSync(`${goDir}.complete`, 'hello');
// This will throw if it doesn't find it in the cache (because no such version exists)
await installer.getGo('250.0');
return;
goVersion = ' ' + goVersion;
if (!gotip) {
goVersion += ' ';
}
test('version check', async () => {
const promise = executil.stdout(goTool, ['version']);
await expect(promise).resolves.toContain(goVersion);
});

it('Doesnt use version of go that was only partially installed in cache', async () => {
const goDir: string = path.join(toolDir, 'go', '251.0.0', os.arch());
await io.mkdirP(goDir);
let thrown = false;
try {
// This will throw if it doesn't find it in the cache (because no such version exists)
await installer.getGo('251.0');
} catch {
thrown = true;
}
expect(thrown).toBe(true);
if (!gotip) {
return;
}
test('CGO_ENABLED check', async () => {
const promise = executil.goEnv('CGO_ENABLED', goTool);
await expect(promise).resolves.toBe(bootstrapCgo);
});
});

describe('installer cache', () => {
const version = '1000.0';
const normVersion = '1000.0.0';
const cacheDir = 'go';
const goRoot = path.join(toolDir, cacheDir, normVersion, osarch);

test('throws on incorrect version', async () => {
const promise = installer.getGo(version);
await expect(promise).rejects.toThrow();
});

test('throws on partial install', async () => {
await io.mkdirP(goRoot);

const promise = installer.getGo(version);
await expect(promise).rejects.toThrow();
})

test('uses existing version', async () => {
await fs.promises.writeFile(`${goRoot}.complete`, 'hello');

const promise = installer.getGo(version);
await expect(promise).resolves.toBeUndefined();
});
});
82 changes: 82 additions & 0 deletions src/executil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as exec from '@actions/exec';

export async function stdout(cmd: string, args: string[]): Promise<string> {
let s: string = '';
// For some strange reason, exec ignores process.env
// unless we pass it explicitly.
const env: { [key: string]: string } = {};
for (const [key, value] of Object.entries(process.env)) {
if (value === undefined) {
continue;
}
env[key] = value;
}
await exec.exec(cmd, args, {
env,
silent: true,
listeners: {
stdout: (buf: Buffer) => {
s += buf.toString();
},
},
});
return s;
}

export async function git(args: string[], opts?: any) {
let rc: number = await exec.exec('git', args, opts);
if (rc != 0) {
throw new Error(`git: exit code ${rc}`);
}
}

export async function gitClone(gitDir: string, gitRepo: string, workTree: string, ref: string) {
let args: string[] = [
'clone',
`--branch=${ref}`,
`--separate-git-dir=${gitDir}`,
'--depth=1',
'--',
gitRepo,
workTree,
];
await git(args);
}

export async function gitFetch(gitDir: string, ref: string) {
let args: string[] = [
`--git-dir=${gitDir}`,
'fetch',
'--depth=1',
'origin',
ref,
];
await git(args);
}

export async function gitRevParse(gitDir: string, spec: string): Promise<string> {
let args: string[] = [
`--git-dir=${gitDir}`,
'rev-parse',
'--verify',
spec,
];
let hash = await stdout('git', args);
return hash.trim();
}

export async function gitReset(gitDir: string, workTree: string, spec: string) {
let args: string[] = [
`--git-dir=${gitDir}`,
`--work-tree=${workTree}`,
'reset',
'--hard',
spec,
];
await git(args);
}

export async function goEnv(envVar: string, goExe: string = 'go'): Promise<string> {
let envVal = await stdout(goExe, ['env', '--', envVar]);
return envVal.trim();
}
Loading