253 lines
7.9 KiB
Plaintext
253 lines
7.9 KiB
Plaintext
import test from 'ava';
|
|
import sinon from 'sinon';
|
|
import nock from 'nock';
|
|
import GitLab from '../lib/plugin/gitlab/GitLab.js';
|
|
import { factory, runTasks } from './util/index.js';
|
|
import {
|
|
interceptUser,
|
|
interceptCollaborator,
|
|
interceptPublish,
|
|
interceptAsset,
|
|
interceptMilestones
|
|
} from './stub/gitlab.js';
|
|
|
|
const tokenHeader = 'Private-Token';
|
|
const tokenRef = 'GITLAB_TOKEN';
|
|
|
|
test.serial('should validate token', async t => {
|
|
const tokenRef = 'MY_GITLAB_TOKEN';
|
|
const pushRepo = 'https://gitlab.com/user/repo';
|
|
const options = { gitlab: { release: true, tokenRef, tokenHeader, pushRepo } };
|
|
const gitlab = factory(GitLab, { options });
|
|
delete process.env[tokenRef];
|
|
|
|
await t.throwsAsync(gitlab.init(), {
|
|
message: /^Environment variable "MY_GITLAB_TOKEN" is required for GitLab releases/
|
|
});
|
|
process.env[tokenRef] = '123'; // eslint-disable-line require-atomic-updates
|
|
|
|
interceptUser(undefined, { reqheaders: { 'private-token': '123' } });
|
|
interceptCollaborator(undefined, { reqheaders: { 'private-token': '123' } });
|
|
await t.notThrowsAsync(gitlab.init());
|
|
});
|
|
|
|
test.serial('should support CI Job token header', async t => {
|
|
const tokenRef = 'CI_JOB_TOKEN';
|
|
const tokenHeader = 'Job-Token';
|
|
process.env[tokenRef] = 'j0b-t0k3n';
|
|
const pushRepo = 'https://gitlab.com/user/repo';
|
|
const options = { git: { pushRepo }, gitlab: { release: true, tokenRef, tokenHeader } };
|
|
const gitlab = factory(GitLab, { options });
|
|
|
|
interceptPublish(undefined, { reqheaders: { 'job-token': '1' } });
|
|
|
|
await t.notThrowsAsync(gitlab.init());
|
|
|
|
delete process.env[tokenRef];
|
|
});
|
|
|
|
test.serial('should upload assets and release', async t => {
|
|
const pushRepo = 'https://gitlab.com/user/repo';
|
|
const options = {
|
|
git: { pushRepo },
|
|
gitlab: {
|
|
tokenRef,
|
|
release: true,
|
|
releaseName: 'Release ${version}',
|
|
releaseNotes: 'echo Custom notes',
|
|
assets: 'test/resources/file-v${version}.txt',
|
|
milestones: ['${version}', '${latestVersion} UAT']
|
|
}
|
|
};
|
|
const gitlab = factory(GitLab, { options });
|
|
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
|
|
interceptUser();
|
|
interceptCollaborator();
|
|
interceptMilestones({
|
|
query: { title: '2.0.1' },
|
|
milestones: [
|
|
{
|
|
id: 17,
|
|
iid: 3,
|
|
title: '2.0.1'
|
|
}
|
|
]
|
|
});
|
|
interceptMilestones({
|
|
query: { title: '2.0.0 UAT' },
|
|
milestones: [
|
|
{
|
|
id: 42,
|
|
iid: 4,
|
|
title: '2.0.0 UAT'
|
|
}
|
|
]
|
|
});
|
|
interceptAsset();
|
|
interceptPublish({
|
|
body: {
|
|
name: 'Release 2.0.1',
|
|
tag_name: '2.0.1',
|
|
description: 'Custom notes',
|
|
assets: {
|
|
links: [
|
|
{
|
|
name: 'file-v2.0.1.txt',
|
|
url: `${pushRepo}/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt`
|
|
}
|
|
]
|
|
},
|
|
milestones: ['2.0.1', '2.0.0 UAT']
|
|
}
|
|
});
|
|
|
|
await runTasks(gitlab);
|
|
|
|
t.is(gitlab.assets[0].url, `${pushRepo}/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt`);
|
|
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
t.true(isReleased);
|
|
t.is(releaseUrl, `${pushRepo}/-/releases`);
|
|
});
|
|
|
|
test.serial('should throw when release milestone is missing', async t => {
|
|
const pushRepo = 'https://gitlab.com/user/repo';
|
|
const options = {
|
|
git: { pushRepo },
|
|
gitlab: {
|
|
tokenRef,
|
|
release: true,
|
|
milestones: ['${version}', '${latestVersion} UAT']
|
|
}
|
|
};
|
|
const gitlab = factory(GitLab, { options });
|
|
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
|
|
interceptUser();
|
|
interceptCollaborator();
|
|
interceptMilestones({
|
|
query: { title: '2.0.1' },
|
|
milestones: [
|
|
{
|
|
id: 17,
|
|
iid: 3,
|
|
title: '2.0.1'
|
|
}
|
|
]
|
|
});
|
|
interceptMilestones({
|
|
query: { title: '2.0.0 UAT' },
|
|
milestones: []
|
|
});
|
|
|
|
await t.throwsAsync(runTasks(gitlab), {
|
|
message: /^Missing one or more milestones in GitLab. Creating a GitLab release will fail./
|
|
});
|
|
});
|
|
|
|
test.serial('should release to self-managed host', async t => {
|
|
const host = 'https://gitlab.example.org';
|
|
const scope = nock(host);
|
|
scope.post('/api/v4/projects/user%2Frepo/releases').reply(200, {});
|
|
const options = {
|
|
git: { pushRepo: `${host}/user/repo` },
|
|
gitlab: { releaseName: 'Release ${version}', releaseNotes: 'echo readme', tokenRef }
|
|
};
|
|
const gitlab = factory(GitLab, { options });
|
|
sinon.stub(gitlab, 'getLatestVersion').resolves('1.0.0');
|
|
|
|
interceptUser({ host });
|
|
interceptCollaborator({ host });
|
|
|
|
await runTasks(gitlab);
|
|
|
|
const { origin, baseUrl } = gitlab.getContext();
|
|
t.is(origin, host);
|
|
t.is(baseUrl, `${host}/api/v4`);
|
|
});
|
|
|
|
test.serial('should release to sub-grouped repo', async t => {
|
|
const scope = nock('https://gitlab.com');
|
|
scope.post('/api/v4/projects/group%2Fsub-group%2Frepo/releases').reply(200, {});
|
|
const options = { gitlab: { tokenRef }, git: { pushRepo: 'git@gitlab.com:group/sub-group/repo.git' } };
|
|
const gitlab = factory(GitLab, { options });
|
|
|
|
interceptUser({ owner: 'sub-group' });
|
|
interceptCollaborator({ owner: 'sub-group', group: 'group' });
|
|
|
|
await runTasks(gitlab);
|
|
|
|
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
t.true(isReleased);
|
|
t.is(releaseUrl, 'https://gitlab.com/group/sub-group/repo/-/releases');
|
|
});
|
|
|
|
test.serial('should throw for unauthenticated user', async t => {
|
|
const host = 'https://gitlab.com';
|
|
const pushRepo = `${host}/user/repo`;
|
|
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
const gitlab = factory(GitLab, { options });
|
|
const scope = nock(host);
|
|
scope.get(`/api/v4/user`).reply(401);
|
|
|
|
await t.throwsAsync(runTasks(gitlab), {
|
|
message: /^Could not authenticate with GitLab using environment variable "GITLAB_TOKEN"/
|
|
});
|
|
});
|
|
|
|
test.serial('should throw for non-collaborator', async t => {
|
|
const host = 'https://gitlab.com';
|
|
const pushRepo = `${host}/john/repo`;
|
|
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
const gitlab = factory(GitLab, { options });
|
|
const scope = nock(host);
|
|
scope.get(`/api/v4/projects/john%2Frepo/members/all/1`).reply(200, { username: 'emma' });
|
|
interceptUser({ owner: 'john' });
|
|
|
|
await t.throwsAsync(runTasks(gitlab), { message: /^User john is not a collaborator for john\/repo/ });
|
|
});
|
|
|
|
test.serial('should throw for insufficient access level', async t => {
|
|
const host = 'https://gitlab.com';
|
|
const pushRepo = `${host}/john/repo`;
|
|
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
const gitlab = factory(GitLab, { options });
|
|
const scope = nock(host);
|
|
scope.get(`/api/v4/projects/john%2Frepo/members/all/1`).reply(200, { username: 'john', access_level: 10 });
|
|
interceptUser({ owner: 'john' });
|
|
|
|
await t.throwsAsync(runTasks(gitlab), { message: /^User john is not a collaborator for john\/repo/ });
|
|
});
|
|
|
|
test('should not make requests in dry run', async t => {
|
|
const [host, owner, repo] = ['https://gitlab.example.org', 'user', 'repo'];
|
|
const pushRepo = `${host}/${owner}/${repo}`;
|
|
const options = { 'dry-run': true, git: { pushRepo }, gitlab: { releaseName: 'R', tokenRef } };
|
|
const gitlab = factory(GitLab, { options });
|
|
sinon.stub(gitlab, 'getLatestVersion').resolves('1.0.0');
|
|
const spy = sinon.spy(gitlab, 'client', ['get']);
|
|
|
|
await runTasks(gitlab);
|
|
|
|
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
t.is(spy.get.callCount, 0);
|
|
t.is(gitlab.log.exec.args[2][0], 'gitlab releases#uploadAssets');
|
|
t.is(gitlab.log.exec.args[3][0], 'gitlab releases#createRelease "R" (1.0.1)');
|
|
t.true(isReleased);
|
|
t.is(releaseUrl, `${pushRepo}/-/releases`);
|
|
spy.get.restore();
|
|
});
|
|
|
|
test('should skip checks', async t => {
|
|
const options = { gitlab: { tokenRef, skipChecks: true, release: true, milestones: ['v1.0.0'] } };
|
|
const gitlab = factory(GitLab, { options });
|
|
const spy = sinon.spy(gitlab, 'client', ['get']);
|
|
|
|
await t.notThrowsAsync(gitlab.init());
|
|
await t.notThrowsAsync(gitlab.beforeRelease());
|
|
|
|
t.is(spy.get.callCount, 0);
|
|
|
|
t.is(gitlab.log.exec.args.filter(entry => /checkReleaseMilestones/.test(entry[0])).length, 0);
|
|
});
|