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

Support GitHub Actions #621

Merged
merged 8 commits into from
Mar 19, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/plugin/GitBase.js
Expand Up @@ -12,6 +12,10 @@ class GitBase extends Plugin {
if (!this.remoteUrl) {
throw new GitRemoteUrlError();
}
// Ref: https://bit.ly/2vsyRzu
this.isGitHubAction = _.isBoolean(this.options.isGitHubAction)
? this.options.isGitHubAction
: Boolean(process.env.GITHUB_ACTION);
await this.fetch();
const repo = parseGitUrl(this.remoteUrl);
const latestTagName = await this.getLatestTagName();
Expand Down
8 changes: 8 additions & 0 deletions lib/plugin/github/GitHub.js
Expand Up @@ -32,6 +32,14 @@ class GitHub extends Release {

async init() {
await super.init();

// If we're running on GitHub Actions, we can skip the authentication and
// collaborator checks.
if (this.isGitHubAction) {
vinsidious marked this conversation as resolved.
Show resolved Hide resolved
this.setContext({ github: { username: process.env.GITHUB_ACTOR } });
return;
}

if (!(await this.isAuthenticated())) {
throw new GitHubClientError(
`Could not authenticate with GitHub using environment variable "${this.options.tokenRef}".`
Expand Down
35 changes: 26 additions & 9 deletions test/github.js
Expand Up @@ -18,7 +18,7 @@ const host = 'github.com';

test('should validate token', async t => {
const tokenRef = 'MY_GITHUB_TOKEN';
const options = { github: { release: true, tokenRef, remoteUrl } };
const options = { github: { release: true, tokenRef, remoteUrl, isGitHubAction: false } };
const github = factory(GitHub, { options });
delete process.env[tokenRef];

Expand All @@ -41,7 +41,8 @@ test('should release and upload assets', async t => {
release: true,
releaseName: 'Release ${tagName}',
releaseNotes: 'echo Custom notes',
assets: `test/resources/${asset}`
assets: `test/resources/${asset}`,
isGitHubAction: false
}
};
const github = factory(GitHub, { options });
Expand All @@ -64,7 +65,7 @@ test('should release and upload assets', async t => {
});

test('should release to enterprise host', async t => {
const github = factory(GitHub, { options: { github: { tokenRef } } });
const github = factory(GitHub, { options: { github: { tokenRef, isGitHubAction: false } } });
const exec = sinon.stub(github.shell, 'exec').callThrough();
exec.withArgs('git config --get remote.origin.url').resolves(`https://github.example.org/user/repo`);
exec.withArgs('git describe --tags --abbrev=0').resolves(`1.0.0`);
Expand Down Expand Up @@ -93,7 +94,8 @@ test('should release to alternative host and proxy', async t => {
tokenRef,
remoteUrl: `git://my-custom-host.org:user/repo`,
host: 'my-custom-host.org',
proxy: 'http://proxy:8080'
proxy: 'http://proxy:8080',
isGitHubAction: false
}
};
const github = factory(GitHub, { options });
Expand All @@ -108,7 +110,7 @@ test('should release to alternative host and proxy', async t => {
});

test('should throw for unauthenticated user', async t => {
const options = { github: { tokenRef, remoteUrl, host } };
const options = { github: { tokenRef, remoteUrl, host, isGitHubAction: false } };
const github = factory(GitHub, { options });
const stub = sinon.stub(github.client.users, 'getAuthenticated');
stub.throws(new RequestError('Bad credentials', 401, { request: { url: '', headers: {} } }));
Expand All @@ -124,7 +126,7 @@ test('should throw for unauthenticated user', async t => {

test('should throw for non-collaborator', async t => {
interceptAuthentication({ username: 'john' });
const options = { github: { tokenRef, remoteUrl, host } };
const options = { github: { tokenRef, remoteUrl, host, isGitHubAction: false } };
const github = factory(GitHub, { options });
const stub = sinon.stub(github.client.repos, 'checkCollaborator');
stub.throws(new RequestError('HttpError', 401, { request: { url: '', headers: {} } }));
Expand All @@ -137,8 +139,23 @@ test('should throw for non-collaborator', async t => {
stub.restore();
});

test('should skip authentication and collaborator checks when running on GitHub Actions', async t => {
const options = { github: { tokenRef, remoteUrl, host, isGitHubAction: true } };
const github = factory(GitHub, { options });
const authStub = sinon.stub(github, 'isAuthenticated');
const collaboratorStub = sinon.stub(github, 'isCollaborator');

await t.notThrowsAsync(github.init());

t.is(authStub.callCount, 0);
t.is(collaboratorStub.callCount, 0);

authStub.restore();
collaboratorStub.restore();
});

test('should handle octokit client error (without retries)', async t => {
const github = factory(GitHub, { options: { github: { tokenRef, remoteUrl, host } } });
const github = factory(GitHub, { options: { github: { tokenRef, remoteUrl, host, isGitHubAction: false } } });
const stub = sinon.stub(github.client.repos, 'createRelease');
stub.throws(new RequestError('Not found', 404, { request: { url: '', headers: {} } }));
interceptAuthentication();
Expand All @@ -151,7 +168,7 @@ test('should handle octokit client error (without retries)', async t => {
});

test('should handle octokit client error (with retries)', async t => {
const options = { github: { tokenRef, remoteUrl, host, retryMinTimeout: 0 } };
const options = { github: { tokenRef, remoteUrl, host, retryMinTimeout: 0, isGitHubAction: false } };
const github = factory(GitHub, { options });
const stub = sinon.stub(github.client.repos, 'createRelease');
stub.throws(new RequestError('Request failed', 500, { request: { url: '', headers: {} } }));
Expand All @@ -166,7 +183,7 @@ test('should handle octokit client error (with retries)', async t => {

test('should not call octokit client in dry run', async t => {
const options = {
github: { tokenRef, remoteUrl, releaseName: 'R ${version}', assets: ['*'] }
github: { tokenRef, remoteUrl, releaseName: 'R ${version}', assets: ['*'], isGitHubAction: false }
};
const github = factory(GitHub, { options, global: { isDryRun: true } });
const spy = sinon.spy(github, 'client', ['get']);
Expand Down