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