release-it 19.0.0-next.1 → 19.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/cli.js +2 -5
- package/lib/config.js +34 -2
- package/lib/index.js +26 -9
- package/lib/log.js +7 -7
- package/lib/plugin/github/GitHub.js +2 -6
- package/lib/plugin/github/util.js +1 -1
- package/lib/plugin/gitlab/GitLab.js +11 -4
- package/lib/plugin/npm/npm.js +4 -0
- package/lib/plugin/version/Version.js +6 -4
- package/package.json +14 -23
- package/schema/gitlab.json +4 -0
- package/schema/release-it.json +4 -0
- package/test/cli.js +6 -5
- package/test/config.js +228 -122
- package/test/git.init.js +220 -218
- package/test/git.js +359 -364
- package/test/github.js +563 -480
- package/test/gitlab.js +374 -336
- package/test/log.js +141 -138
- package/test/npm.js +315 -373
- package/test/plugin-name.js +7 -6
- package/test/plugins.js +200 -211
- package/test/prompt.js +26 -32
- package/test/shell.js +63 -60
- package/test/spinner.js +20 -24
- package/test/stub/github.js +113 -123
- package/test/stub/gitlab.js +75 -51
- package/test/tasks.interactive.js +215 -164
- package/test/tasks.js +392 -412
- package/test/util/helpers.js +4 -3
- package/test/util/index.js +34 -7
- package/test/util/mock.js +11 -0
- package/test/utils.js +33 -32
- package/test/version.js +192 -176
- package/test/util/setup.js +0 -3
package/test/gitlab.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import test from '
|
|
3
|
-
import
|
|
4
|
-
import nock from 'nock';
|
|
2
|
+
import test, { before, after, afterEach, beforeEach, describe } from 'node:test';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
5
4
|
import { Agent } from 'undici';
|
|
6
5
|
import Git from '../lib/plugin/git/Git.js';
|
|
7
6
|
import GitLab from '../lib/plugin/gitlab/GitLab.js';
|
|
@@ -13,405 +12,444 @@ import {
|
|
|
13
12
|
interceptPublish,
|
|
14
13
|
interceptAsset,
|
|
15
14
|
interceptAssetGeneric,
|
|
16
|
-
interceptMilestones
|
|
15
|
+
interceptMilestones,
|
|
16
|
+
interceptMembers
|
|
17
17
|
} from './stub/gitlab.js';
|
|
18
|
+
import { mockFetch } from './util/mock.js';
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
const
|
|
20
|
+
describe('GitLab', () => {
|
|
21
|
+
const tokenHeader = 'Private-Token';
|
|
22
|
+
const tokenRef = 'GITLAB_TOKEN';
|
|
23
|
+
const certificateAuthorityFileRef = 'CI_SERVER_TLS_CA_FILE';
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
delete process.env[tokenRef];
|
|
25
|
+
const [mocker, api, example, local] = mockFetch([
|
|
26
|
+
'https://gitlab.com/api/v4',
|
|
27
|
+
'https://gitlab.example.org/api/v4',
|
|
28
|
+
'https://localhost:3000/api/v4'
|
|
29
|
+
]);
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
before(() => {
|
|
32
|
+
mocker.mockGlobal();
|
|
31
33
|
});
|
|
32
|
-
process.env[tokenRef] = '123';
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
35
|
+
let originalEnv;
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
originalEnv = process.env;
|
|
38
|
+
process.env = { ...originalEnv };
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const tokenHeader = 'Job-Token';
|
|
42
|
-
process.env[tokenRef] = 'j0b-t0k3n';
|
|
43
|
-
const pushRepo = 'https://gitlab.com/user/repo';
|
|
44
|
-
const options = { git: { pushRepo }, gitlab: { release: true, tokenRef, tokenHeader } };
|
|
45
|
-
const gitlab = factory(GitLab, { options });
|
|
40
|
+
process.env[tokenRef] = '123';
|
|
41
|
+
});
|
|
46
42
|
|
|
47
|
-
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
if (originalEnv !== undefined) process.env = originalEnv;
|
|
45
|
+
mocker.clearAll();
|
|
46
|
+
});
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
after(() => {
|
|
49
|
+
mocker.unmockGlobal();
|
|
50
|
+
});
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
test('should validate token', async () => {
|
|
53
|
+
const tokenRef = 'MY_GITLAB_TOKEN';
|
|
54
|
+
const pushRepo = 'https://gitlab.com/user/repo';
|
|
55
|
+
const options = { gitlab: { release: true, tokenRef, tokenHeader, pushRepo } };
|
|
56
|
+
const gitlab = factory(GitLab, { options });
|
|
57
|
+
delete process.env[tokenRef];
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
releaseName: 'Release ${version}',
|
|
62
|
-
releaseNotes: 'echo Custom notes',
|
|
63
|
-
assets: 'test/resources/file-v${version}.txt',
|
|
64
|
-
milestones: ['${version}', '${latestVersion} UAT']
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
const gitlab = factory(GitLab, { options });
|
|
68
|
-
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
69
|
-
|
|
70
|
-
const git = factory(Git);
|
|
71
|
-
const ref = (await git.getBranchName()) ?? 'HEAD';
|
|
72
|
-
|
|
73
|
-
interceptUser();
|
|
74
|
-
interceptCollaborator();
|
|
75
|
-
interceptMilestones({
|
|
76
|
-
query: { title: '2.0.1' },
|
|
77
|
-
milestones: [
|
|
78
|
-
{
|
|
79
|
-
id: 17,
|
|
80
|
-
iid: 3,
|
|
81
|
-
title: '2.0.1'
|
|
82
|
-
}
|
|
83
|
-
]
|
|
59
|
+
await assert.rejects(gitlab.init(), /Environment variable "MY_GITLAB_TOKEN" is required for GitLab releases/);
|
|
60
|
+
|
|
61
|
+
process.env[tokenRef] = '123';
|
|
62
|
+
|
|
63
|
+
interceptUser(api, { headers: { 'private-token': '123' } });
|
|
64
|
+
interceptCollaborator(api, { headers: { 'private-token': '123' } });
|
|
65
|
+
await assert.doesNotReject(gitlab.init());
|
|
84
66
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
67
|
+
|
|
68
|
+
test('should support CI Job token header', async () => {
|
|
69
|
+
const tokenRef = 'CI_JOB_TOKEN';
|
|
70
|
+
const tokenHeader = 'Job-Token';
|
|
71
|
+
process.env[tokenRef] = 'j0b-t0k3n';
|
|
72
|
+
const pushRepo = 'https://gitlab.com/user/repo';
|
|
73
|
+
const options = { git: { pushRepo }, gitlab: { release: true, tokenRef, tokenHeader } };
|
|
74
|
+
const gitlab = factory(GitLab, { options });
|
|
75
|
+
|
|
76
|
+
interceptPublish(api, { headers: { 'job-token': '1' } });
|
|
77
|
+
|
|
78
|
+
await assert.doesNotReject(gitlab.init());
|
|
79
|
+
|
|
80
|
+
delete process.env[tokenRef];
|
|
94
81
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
82
|
+
|
|
83
|
+
test('should upload assets and release', async t => {
|
|
84
|
+
const pushRepo = 'https://gitlab.com/user/repo';
|
|
85
|
+
const options = {
|
|
86
|
+
git: { pushRepo },
|
|
87
|
+
gitlab: {
|
|
88
|
+
tokenRef,
|
|
89
|
+
release: true,
|
|
90
|
+
releaseName: 'Release ${version}',
|
|
91
|
+
releaseNotes: 'echo Custom notes',
|
|
92
|
+
assets: 'test/resources/file-v${version}.txt',
|
|
93
|
+
milestones: ['${version}', '${latestVersion} UAT']
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const gitlab = factory(GitLab, { options });
|
|
97
|
+
t.mock.method(gitlab, 'getLatestVersion', () => Promise.resolve('2.0.0'));
|
|
98
|
+
|
|
99
|
+
const git = factory(Git);
|
|
100
|
+
const ref = (await git.getBranchName()) ?? 'HEAD';
|
|
101
|
+
|
|
102
|
+
interceptUser(api);
|
|
103
|
+
interceptCollaborator(api);
|
|
104
|
+
interceptMilestones(api, { query: { title: '2.0.1' }, milestones: [{ id: 17, iid: 3, title: '2.0.1' }] });
|
|
105
|
+
interceptMilestones(api, { query: { title: '2.0.0 UAT' }, milestones: [{ id: 42, iid: 4, title: '2.0.0 UAT' }] });
|
|
106
|
+
interceptAsset(api);
|
|
107
|
+
interceptPublish(api, {
|
|
108
|
+
body: {
|
|
109
|
+
name: 'Release 2.0.1',
|
|
110
|
+
ref,
|
|
111
|
+
tag_name: '2.0.1',
|
|
112
|
+
tag_message: 'Release 2.0.1',
|
|
113
|
+
description: 'Custom notes',
|
|
114
|
+
assets: {
|
|
115
|
+
links: [
|
|
116
|
+
{ name: 'file-v2.0.1.txt', url: `${pushRepo}/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt` }
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
milestones: ['2.0.1', '2.0.0 UAT']
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await runTasks(gitlab);
|
|
124
|
+
|
|
125
|
+
assert.equal(gitlab.assets[0].url, `${pushRepo}/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt`);
|
|
126
|
+
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
127
|
+
assert(isReleased);
|
|
128
|
+
assert.equal(releaseUrl, `${pushRepo}/-/releases/2.0.1`);
|
|
113
129
|
});
|
|
114
130
|
|
|
115
|
-
|
|
131
|
+
test('should upload assets with ID-based URLs', async t => {
|
|
132
|
+
const host = 'https://gitlab.com';
|
|
133
|
+
const pushRepo = `${host}/user/repo`;
|
|
134
|
+
const options = {
|
|
135
|
+
git: { pushRepo },
|
|
136
|
+
gitlab: {
|
|
137
|
+
tokenRef,
|
|
138
|
+
release: true,
|
|
139
|
+
assets: 'test/resources/file-v${version}.txt',
|
|
140
|
+
useIdsForUrls: true
|
|
141
|
+
}
|
|
142
|
+
};
|
|
116
143
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
t.true(isReleased);
|
|
120
|
-
t.is(releaseUrl, `${pushRepo}/-/releases/2.0.1`);
|
|
121
|
-
});
|
|
144
|
+
const gitlab = factory(GitLab, { options });
|
|
145
|
+
t.mock.method(gitlab, 'getLatestVersion', () => Promise.resolve('2.0.0'));
|
|
122
146
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
git: { pushRepo },
|
|
128
|
-
gitlab: {
|
|
129
|
-
tokenRef,
|
|
130
|
-
release: true,
|
|
131
|
-
assets: 'test/resources/file-v${version}.txt',
|
|
132
|
-
useIdsForUrls: true
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
const gitlab = factory(GitLab, { options });
|
|
136
|
-
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
137
|
-
|
|
138
|
-
interceptUser();
|
|
139
|
-
interceptCollaborator();
|
|
140
|
-
interceptAsset();
|
|
141
|
-
interceptPublish();
|
|
142
|
-
|
|
143
|
-
await runTasks(gitlab);
|
|
144
|
-
|
|
145
|
-
t.is(gitlab.assets[0].url, `${host}/-/project/1234/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt`);
|
|
146
|
-
});
|
|
147
|
+
interceptUser(api);
|
|
148
|
+
interceptCollaborator(api);
|
|
149
|
+
interceptAsset(api);
|
|
150
|
+
interceptPublish(api);
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
release: true,
|
|
156
|
-
assets: 'test/resources/file-v${version}.txt',
|
|
157
|
-
useGenericPackageRepositoryForAssets: true,
|
|
158
|
-
genericPackageRepositoryName: 'release-it'
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
const gitlab = factory(GitLab, { options });
|
|
162
|
-
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
163
|
-
|
|
164
|
-
interceptUser();
|
|
165
|
-
interceptCollaborator();
|
|
166
|
-
interceptAssetGeneric();
|
|
167
|
-
interceptPublish();
|
|
168
|
-
|
|
169
|
-
await runTasks(gitlab);
|
|
170
|
-
|
|
171
|
-
t.is(gitlab.assets[0].url, `${host}/api/v4/projects/user%2Frepo/packages/generic/release-it/2.0.1/file-v2.0.1.txt`);
|
|
172
|
-
});
|
|
152
|
+
await runTasks(gitlab);
|
|
153
|
+
|
|
154
|
+
assert.equal(
|
|
155
|
+
gitlab.assets[0].url,
|
|
156
|
+
`${host}/-/project/1234/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt`
|
|
157
|
+
);
|
|
158
|
+
});
|
|
173
159
|
|
|
174
|
-
test
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
186
|
-
|
|
187
|
-
interceptUser();
|
|
188
|
-
interceptCollaborator();
|
|
189
|
-
interceptMilestones({
|
|
190
|
-
query: { title: '2.0.1' },
|
|
191
|
-
milestones: [
|
|
192
|
-
{
|
|
193
|
-
id: 17,
|
|
194
|
-
iid: 3,
|
|
195
|
-
title: '2.0.1'
|
|
160
|
+
test('should upload assets to generic repo', async t => {
|
|
161
|
+
const host = 'https://gitlab.com';
|
|
162
|
+
const pushRepo = `${host}/user/repo`;
|
|
163
|
+
const options = {
|
|
164
|
+
git: { pushRepo },
|
|
165
|
+
gitlab: {
|
|
166
|
+
tokenRef,
|
|
167
|
+
release: true,
|
|
168
|
+
assets: 'test/resources/file-v${version}.txt',
|
|
169
|
+
useGenericPackageRepositoryForAssets: true,
|
|
170
|
+
genericPackageRepositoryName: 'release-it'
|
|
196
171
|
}
|
|
197
|
-
|
|
172
|
+
};
|
|
173
|
+
const gitlab = factory(GitLab, { options });
|
|
174
|
+
t.mock.method(gitlab, 'getLatestVersion', () => Promise.resolve('2.0.0'));
|
|
175
|
+
|
|
176
|
+
interceptUser(api);
|
|
177
|
+
interceptCollaborator(api);
|
|
178
|
+
interceptAssetGeneric(api);
|
|
179
|
+
interceptPublish(api);
|
|
180
|
+
|
|
181
|
+
await runTasks(gitlab);
|
|
182
|
+
|
|
183
|
+
assert.equal(
|
|
184
|
+
gitlab.assets[0].url,
|
|
185
|
+
`${host}/api/v4/projects/user%2Frepo/packages/generic/release-it/2.0.1/file-v2.0.1.txt`
|
|
186
|
+
);
|
|
198
187
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
188
|
+
|
|
189
|
+
test('should throw when release milestone is missing', async t => {
|
|
190
|
+
const pushRepo = 'https://gitlab.com/user/repo';
|
|
191
|
+
const options = {
|
|
192
|
+
git: { pushRepo },
|
|
193
|
+
gitlab: {
|
|
194
|
+
tokenRef,
|
|
195
|
+
release: true,
|
|
196
|
+
milestones: ['${version}', '${latestVersion} UAT']
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const gitlab = factory(GitLab, { options });
|
|
200
|
+
t.mock.method(gitlab, 'getLatestVersion', () => Promise.resolve('2.0.0'));
|
|
201
|
+
|
|
202
|
+
interceptUser(api);
|
|
203
|
+
interceptCollaborator(api);
|
|
204
|
+
interceptMilestones(api, { query: { title: '2.0.1' }, milestones: [{ id: 17, iid: 3, title: '2.0.1' }] });
|
|
205
|
+
interceptMilestones(api, { query: { title: '2.0.0 UAT' }, milestones: [] });
|
|
206
|
+
|
|
207
|
+
await assert.rejects(
|
|
208
|
+
runTasks(gitlab),
|
|
209
|
+
/Missing one or more milestones in GitLab. Creating a GitLab release will fail./
|
|
210
|
+
);
|
|
202
211
|
});
|
|
203
212
|
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
test('should release to self-managed host', async t => {
|
|
214
|
+
const host = 'https://gitlab.example.org';
|
|
215
|
+
const options = {
|
|
216
|
+
git: { pushRepo: `${host}/user/repo` },
|
|
217
|
+
gitlab: { releaseName: 'Release ${version}', releaseNotes: 'echo readme', tokenRef }
|
|
218
|
+
};
|
|
219
|
+
const gitlab = factory(GitLab, { options });
|
|
220
|
+
t.mock.method(gitlab, 'getLatestVersion', () => Promise.resolve('1.0.0'));
|
|
221
|
+
|
|
222
|
+
interceptUser(example);
|
|
223
|
+
interceptCollaborator(example);
|
|
224
|
+
interceptPublish(example);
|
|
225
|
+
|
|
226
|
+
await runTasks(gitlab);
|
|
227
|
+
|
|
228
|
+
const { origin, baseUrl } = gitlab.getContext();
|
|
229
|
+
assert.equal(origin, host);
|
|
230
|
+
assert.equal(baseUrl, `${host}/api/v4`);
|
|
206
231
|
});
|
|
207
|
-
});
|
|
208
232
|
|
|
209
|
-
test
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
scope.post('/api/v4/projects/user%2Frepo/releases').reply(200, {});
|
|
213
|
-
const options = {
|
|
214
|
-
git: { pushRepo: `${host}/user/repo` },
|
|
215
|
-
gitlab: { releaseName: 'Release ${version}', releaseNotes: 'echo readme', tokenRef }
|
|
216
|
-
};
|
|
217
|
-
const gitlab = factory(GitLab, { options });
|
|
218
|
-
sinon.stub(gitlab, 'getLatestVersion').resolves('1.0.0');
|
|
219
|
-
|
|
220
|
-
interceptUser({ host });
|
|
221
|
-
interceptCollaborator({ host });
|
|
222
|
-
|
|
223
|
-
await runTasks(gitlab);
|
|
224
|
-
|
|
225
|
-
const { origin, baseUrl } = gitlab.getContext();
|
|
226
|
-
t.is(origin, host);
|
|
227
|
-
t.is(baseUrl, `${host}/api/v4`);
|
|
228
|
-
});
|
|
233
|
+
test('should release to sub-grouped repo', async () => {
|
|
234
|
+
const options = { gitlab: { tokenRef }, git: { pushRepo: 'git@gitlab.com:group/sub-group/repo.git' } };
|
|
235
|
+
const gitlab = factory(GitLab, { options });
|
|
229
236
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const options = { gitlab: { tokenRef }, git: { pushRepo: 'git@gitlab.com:group/sub-group/repo.git' } };
|
|
234
|
-
const gitlab = factory(GitLab, { options });
|
|
237
|
+
interceptUser(api, { owner: 'sub-group' });
|
|
238
|
+
interceptCollaborator(api, { owner: 'sub-group', group: 'group' });
|
|
239
|
+
interceptPublish(api, { owner: 'group', project: 'sub-group%2Frepo' });
|
|
235
240
|
|
|
236
|
-
|
|
237
|
-
interceptCollaborator({ owner: 'sub-group', group: 'group' });
|
|
241
|
+
await runTasks(gitlab);
|
|
238
242
|
|
|
239
|
-
|
|
243
|
+
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
244
|
+
assert(isReleased);
|
|
245
|
+
assert.match(releaseUrl, /https:\/\/gitlab.com\/group\/sub-group(\/|%2F)repo\/-\/releases\//);
|
|
246
|
+
});
|
|
240
247
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
248
|
+
test('should throw for unauthenticated user', async () => {
|
|
249
|
+
const host = 'https://gitlab.com';
|
|
250
|
+
const pushRepo = `${host}/user/repo`;
|
|
251
|
+
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
252
|
+
const gitlab = factory(GitLab, { options });
|
|
245
253
|
|
|
246
|
-
|
|
247
|
-
const host = 'https://gitlab.com';
|
|
248
|
-
const pushRepo = `${host}/user/repo`;
|
|
249
|
-
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
250
|
-
const gitlab = factory(GitLab, { options });
|
|
251
|
-
const scope = nock(host);
|
|
252
|
-
scope.get(`/api/v4/user`).reply(401);
|
|
254
|
+
api.get('/user', { status: 401 });
|
|
253
255
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
+
await assert.rejects(
|
|
257
|
+
runTasks(gitlab),
|
|
258
|
+
/Could not authenticate with GitLab using environment variable "GITLAB_TOKEN"/
|
|
259
|
+
);
|
|
256
260
|
});
|
|
257
|
-
});
|
|
258
261
|
|
|
259
|
-
test
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const scope = nock(host);
|
|
265
|
-
scope.get(`/api/v4/projects/john%2Frepo/members/all/1`).reply(200, { username: 'emma' });
|
|
266
|
-
interceptUser({ owner: 'john' });
|
|
262
|
+
test('should throw for non-collaborator', async () => {
|
|
263
|
+
const host = 'https://gitlab.com';
|
|
264
|
+
const pushRepo = `${host}/john/repo`;
|
|
265
|
+
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
266
|
+
const gitlab = factory(GitLab, { options });
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
});
|
|
268
|
+
interceptMembers(api, { owner: 'emma' });
|
|
269
|
+
interceptUser(api, { owner: 'john' });
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const pushRepo = `${host}/john/repo`;
|
|
274
|
-
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
275
|
-
const gitlab = factory(GitLab, { options });
|
|
276
|
-
const scope = nock(host);
|
|
277
|
-
scope.get(`/api/v4/projects/john%2Frepo/members/all/1`).reply(200, { username: 'john', access_level: 10 });
|
|
278
|
-
interceptUser({ owner: 'john' });
|
|
271
|
+
await assert.rejects(runTasks(gitlab), /User john is not a collaborator for john\/repo/);
|
|
272
|
+
});
|
|
279
273
|
|
|
280
|
-
|
|
281
|
-
|
|
274
|
+
test('should throw for insufficient access level', async () => {
|
|
275
|
+
const host = 'https://gitlab.com';
|
|
276
|
+
const pushRepo = `${host}/john/repo`;
|
|
277
|
+
const options = { gitlab: { tokenRef, pushRepo, host } };
|
|
278
|
+
const gitlab = factory(GitLab, { options });
|
|
282
279
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const pushRepo = `${host}/${owner}/${repo}`;
|
|
286
|
-
const options = { 'dry-run': true, git: { pushRepo }, gitlab: { releaseName: 'R', tokenRef } };
|
|
287
|
-
const gitlab = factory(GitLab, { options });
|
|
288
|
-
sinon.stub(gitlab, 'getLatestVersion').resolves('1.0.0');
|
|
280
|
+
interceptMembers(api, { owner: 'john', access_level: 10 });
|
|
281
|
+
interceptUser(api, { owner: 'john' });
|
|
289
282
|
|
|
290
|
-
|
|
283
|
+
await assert.rejects(runTasks(gitlab), /User john is not a collaborator for john\/repo/);
|
|
284
|
+
});
|
|
291
285
|
|
|
292
|
-
|
|
286
|
+
test('should not make requests in dry run', async t => {
|
|
287
|
+
const [host, owner, repo] = ['https://gitlab.example.org', 'user', 'repo'];
|
|
288
|
+
const pushRepo = `${host}/${owner}/${repo}`;
|
|
289
|
+
const options = { 'dry-run': true, git: { pushRepo }, gitlab: { releaseName: 'R', tokenRef } };
|
|
290
|
+
const gitlab = factory(GitLab, { options });
|
|
291
|
+
t.mock.method(gitlab, 'getLatestVersion', () => Promise.resolve('1.0.0'));
|
|
293
292
|
|
|
294
|
-
|
|
295
|
-
t.is(gitlab.log.exec.args[3][0], 'gitlab releases#createRelease "R" (1.0.1)');
|
|
296
|
-
t.true(isReleased);
|
|
297
|
-
t.is(releaseUrl, `${pushRepo}/-/releases/1.0.1`);
|
|
298
|
-
});
|
|
293
|
+
await runTasks(gitlab);
|
|
299
294
|
|
|
300
|
-
|
|
301
|
-
const options = { gitlab: { tokenRef, skipChecks: true, release: true, milestones: ['v1.0.0'] } };
|
|
302
|
-
const gitlab = factory(GitLab, { options });
|
|
295
|
+
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
303
296
|
|
|
304
|
-
|
|
305
|
-
|
|
297
|
+
assert.equal(gitlab.log.exec.mock.calls[2].arguments[0], 'gitlab releases#uploadAssets');
|
|
298
|
+
assert.equal(gitlab.log.exec.mock.calls[3].arguments[0], 'gitlab releases#createRelease "R" (1.0.1)');
|
|
299
|
+
assert(isReleased);
|
|
300
|
+
assert.equal(releaseUrl, `${pushRepo}/-/releases/1.0.1`);
|
|
301
|
+
});
|
|
306
302
|
|
|
307
|
-
|
|
308
|
-
}
|
|
303
|
+
test('should skip checks', async () => {
|
|
304
|
+
const options = { gitlab: { tokenRef, skipChecks: true, release: true, milestones: ['v1.0.0'] } };
|
|
305
|
+
const gitlab = factory(GitLab, { options });
|
|
309
306
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const gitlab = factory(GitLab, { options });
|
|
307
|
+
await assert.doesNotReject(gitlab.init());
|
|
308
|
+
await assert.doesNotReject(gitlab.beforeRelease());
|
|
313
309
|
|
|
314
|
-
|
|
315
|
-
|
|
310
|
+
assert.equal(
|
|
311
|
+
gitlab.log.exec.mock.calls
|
|
312
|
+
.flatMap(call => call.arguments)
|
|
313
|
+
.filter(entry => /checkReleaseMilestones/.test(entry[0])).length,
|
|
314
|
+
0
|
|
315
|
+
);
|
|
316
|
+
});
|
|
316
317
|
|
|
317
|
-
test
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const { dispatcher } = gitlab.certificateAuthorityOption;
|
|
318
|
+
test('should not create fetch agent', () => {
|
|
319
|
+
const options = { gitlab: {} };
|
|
320
|
+
const gitlab = factory(GitLab, { options });
|
|
321
321
|
|
|
322
|
-
|
|
322
|
+
assert.deepEqual(gitlab.certificateAuthorityOption, {});
|
|
323
|
+
});
|
|
323
324
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
});
|
|
325
|
+
test('should create fetch agent if secure == false', () => {
|
|
326
|
+
const options = { gitlab: { secure: false } };
|
|
327
|
+
const gitlab = factory(GitLab, { options });
|
|
328
|
+
const { dispatcher } = gitlab.certificateAuthorityOption;
|
|
327
329
|
|
|
328
|
-
|
|
329
|
-
const sandbox = sinon.createSandbox();
|
|
330
|
-
sandbox.stub(fs, 'readFileSync').returns('test certificate');
|
|
330
|
+
assert(dispatcher instanceof Agent, "Fetch dispatcher should be an instance of undici's Agent class");
|
|
331
331
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
332
|
+
const kOptions = Object.getOwnPropertySymbols(dispatcher).find(symbol => symbol.description === 'options');
|
|
333
|
+
assert.deepEqual(dispatcher[kOptions].connect, { rejectUnauthorized: false, ca: undefined });
|
|
334
|
+
});
|
|
335
335
|
|
|
336
|
-
|
|
336
|
+
test('should create fetch agent if certificateAuthorityFile', t => {
|
|
337
|
+
const readFileSync = t.mock.method(fs, 'readFileSync', () => 'test certificate');
|
|
337
338
|
|
|
338
|
-
|
|
339
|
-
|
|
339
|
+
const options = { gitlab: { certificateAuthorityFile: 'cert.crt' } };
|
|
340
|
+
const gitlab = factory(GitLab, { options });
|
|
341
|
+
const { dispatcher } = gitlab.certificateAuthorityOption;
|
|
340
342
|
|
|
341
|
-
|
|
342
|
-
});
|
|
343
|
+
assert(dispatcher instanceof Agent, "Fetch dispatcher should be an instance of undici's Agent class");
|
|
343
344
|
|
|
344
|
-
|
|
345
|
-
|
|
345
|
+
const kOptions = Object.getOwnPropertySymbols(dispatcher).find(symbol => symbol.description === 'options');
|
|
346
|
+
assert.deepEqual(dispatcher[kOptions].connect, { rejectUnauthorized: undefined, ca: 'test certificate' });
|
|
347
|
+
|
|
348
|
+
readFileSync.mock.restore();
|
|
349
|
+
});
|
|
346
350
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
};
|
|
351
|
-
const gitlab = factory(GitLab, { options });
|
|
352
|
-
const server = new GitlabTestServer();
|
|
351
|
+
test('should create fetch agent if CI_SERVER_TLS_CA_FILE env is set', t => {
|
|
352
|
+
const readFileSync = t.mock.method(fs, 'readFileSync', () => 'test certificate');
|
|
353
|
+
process.env[certificateAuthorityFileRef] = 'ca.crt';
|
|
353
354
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
const options = { gitlab: {} };
|
|
356
|
+
const gitlab = factory(GitLab, { options });
|
|
357
|
+
const { dispatcher } = gitlab.certificateAuthorityOption;
|
|
358
|
+
|
|
359
|
+
assert(dispatcher instanceof Agent, "Fetch dispatcher should be an instance of undici's Agent class");
|
|
360
|
+
|
|
361
|
+
const kOptions = Object.getOwnPropertySymbols(dispatcher).find(symbol => symbol.description === 'options');
|
|
362
|
+
assert.deepEqual(dispatcher[kOptions].connect, { rejectUnauthorized: undefined, ca: 'test certificate' });
|
|
363
|
+
|
|
364
|
+
readFileSync.mock.restore();
|
|
357
365
|
});
|
|
358
366
|
|
|
359
|
-
|
|
360
|
-
|
|
367
|
+
test('should create fetch agent if certificateAuthorityFileRef env is set', t => {
|
|
368
|
+
const readFileSync = t.mock.method(fs, 'readFileSync', () => 'test certificate');
|
|
369
|
+
process.env['GITLAB_CA_FILE'] = 'custom-ca.crt';
|
|
370
|
+
|
|
371
|
+
const options = { gitlab: { certificateAuthorityFileRef: 'GITLAB_CA_FILE' } };
|
|
372
|
+
const gitlab = factory(GitLab, { options });
|
|
373
|
+
const { dispatcher } = gitlab.certificateAuthorityOption;
|
|
361
374
|
|
|
362
|
-
|
|
363
|
-
|
|
375
|
+
assert(dispatcher instanceof Agent, "Fetch dispatcher should be an instance of undici's Agent class");
|
|
376
|
+
|
|
377
|
+
const kOptions = Object.getOwnPropertySymbols(dispatcher).find(symbol => symbol.description === 'options');
|
|
378
|
+
assert.deepEqual(dispatcher[kOptions].connect, { rejectUnauthorized: undefined, ca: 'test certificate' });
|
|
379
|
+
|
|
380
|
+
readFileSync.mock.restore();
|
|
364
381
|
});
|
|
365
|
-
});
|
|
366
382
|
|
|
367
|
-
test
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
await server.stop();
|
|
383
|
+
test('should throw for insecure connections to self-hosted instances', async t => {
|
|
384
|
+
const host = 'https://localhost:3000';
|
|
385
|
+
|
|
386
|
+
const options = {
|
|
387
|
+
git: { pushRepo: `${host}/user/repo` },
|
|
388
|
+
gitlab: { host, tokenRef, origin: host }
|
|
389
|
+
};
|
|
390
|
+
const gitlab = factory(GitLab, { options });
|
|
391
|
+
const server = new GitlabTestServer();
|
|
392
|
+
|
|
393
|
+
t.after(async () => {
|
|
394
|
+
await server.stop();
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
await server.run();
|
|
398
|
+
|
|
399
|
+
await assert.rejects(gitlab.init(), /Could not authenticate with GitLab using environment variable "GITLAB_TOKEN"/);
|
|
385
400
|
});
|
|
386
401
|
|
|
387
|
-
|
|
388
|
-
|
|
402
|
+
test('should succesfully connect to self-hosted instance if insecure connection allowed', async t => {
|
|
403
|
+
const host = 'https://localhost:3000';
|
|
389
404
|
|
|
390
|
-
|
|
391
|
-
}
|
|
405
|
+
const options = {
|
|
406
|
+
git: { pushRepo: `${host}/user/repo` },
|
|
407
|
+
gitlab: {
|
|
408
|
+
host,
|
|
409
|
+
tokenRef,
|
|
410
|
+
origin: host,
|
|
411
|
+
secure: false
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
const gitlab = factory(GitLab, { options });
|
|
415
|
+
const server = new GitlabTestServer();
|
|
392
416
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
const gitlab = factory(GitLab, { options });
|
|
406
|
-
const server = new GitlabTestServer();
|
|
407
|
-
|
|
408
|
-
t.teardown(async () => {
|
|
409
|
-
nock.disableNetConnect();
|
|
410
|
-
await server.stop();
|
|
417
|
+
t.after(async () => {
|
|
418
|
+
await server.stop();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
await server.run();
|
|
422
|
+
|
|
423
|
+
interceptUser(local);
|
|
424
|
+
interceptCollaborator(local);
|
|
425
|
+
|
|
426
|
+
await assert.doesNotReject(gitlab.init());
|
|
411
427
|
});
|
|
412
428
|
|
|
413
|
-
|
|
414
|
-
|
|
429
|
+
test('should succesfully connect to self-hosted instance with valid CA file', async t => {
|
|
430
|
+
const host = 'https://localhost:3000';
|
|
431
|
+
|
|
432
|
+
const options = {
|
|
433
|
+
git: { pushRepo: `${host}/user/repo` },
|
|
434
|
+
gitlab: {
|
|
435
|
+
host,
|
|
436
|
+
tokenRef,
|
|
437
|
+
origin: host,
|
|
438
|
+
certificateAuthorityFile: 'test/util/https-server/client/my-private-root-ca.cert.pem'
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
const gitlab = factory(GitLab, { options });
|
|
442
|
+
const server = new GitlabTestServer();
|
|
415
443
|
|
|
416
|
-
|
|
444
|
+
t.after(async () => {
|
|
445
|
+
await server.stop();
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
await server.run();
|
|
449
|
+
|
|
450
|
+
interceptUser(local);
|
|
451
|
+
interceptCollaborator(local);
|
|
452
|
+
|
|
453
|
+
await assert.doesNotReject(gitlab.init());
|
|
454
|
+
});
|
|
417
455
|
});
|