release-it 19.0.0-next.0 → 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 +42 -7
- package/lib/index.js +30 -14
- package/lib/log.js +12 -11
- package/lib/plugin/GitBase.js +2 -1
- package/lib/plugin/GitRelease.js +5 -5
- package/lib/plugin/Plugin.js +6 -4
- package/lib/plugin/factory.js +11 -11
- package/lib/plugin/git/Git.js +5 -6
- package/lib/plugin/github/GitHub.js +13 -16
- package/lib/plugin/github/util.js +1 -1
- package/lib/plugin/gitlab/GitLab.js +6 -4
- package/lib/plugin/npm/npm.js +4 -0
- package/lib/util.js +71 -42
- package/package.json +27 -29
- 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 -165
- package/test/tasks.js +394 -413
- package/test/util/helpers.js +4 -3
- package/test/util/index.js +35 -11
- package/test/utils.js +33 -32
- package/test/version.js +192 -176
- package/test/util/setup.js +0 -3
package/test/npm.js
CHANGED
|
@@ -1,418 +1,360 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { vol } from 'memfs';
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import test, { describe } from 'node:test';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { writeFileSync } from 'node:fs';
|
|
6
5
|
import npm from '../lib/plugin/npm/npm.js';
|
|
7
6
|
import { factory, runTasks } from './util/index.js';
|
|
8
|
-
import { getArgs } from './util/helpers.js';
|
|
9
|
-
|
|
10
|
-
const mockFs = volume => {
|
|
11
|
-
vol.fromJSON(volume);
|
|
12
|
-
const unpatch = patchFs(vol);
|
|
13
|
-
return () => {
|
|
14
|
-
vol.reset();
|
|
15
|
-
unpatch();
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
test('should return npm package url', t => {
|
|
20
|
-
const options = { npm: { name: 'my-cool-package' } };
|
|
21
|
-
const npmClient = factory(npm, { options });
|
|
22
|
-
t.is(npmClient.getPackageUrl(), 'https://www.npmjs.com/package/my-cool-package');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('should return npm package url (custom registry)', t => {
|
|
26
|
-
const options = { npm: { name: 'my-cool-package', publishConfig: { registry: 'https://registry.example.org/' } } };
|
|
27
|
-
const npmClient = factory(npm, { options });
|
|
28
|
-
t.is(npmClient.getPackageUrl(), 'https://registry.example.org/package/my-cool-package');
|
|
29
|
-
});
|
|
7
|
+
import { mkTmpDir, getArgs } from './util/helpers.js';
|
|
30
8
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
9
|
+
describe('npm', async () => {
|
|
10
|
+
test('should return npm package url', async () => {
|
|
11
|
+
const options = { npm: { name: 'my-cool-package' } };
|
|
12
|
+
const npmClient = factory(npm, { options });
|
|
13
|
+
assert.equal(npmClient.getPackageUrl(), 'https://www.npmjs.com/package/my-cool-package');
|
|
14
|
+
});
|
|
36
15
|
|
|
37
|
-
test('should return npm package url (custom registry
|
|
38
|
-
|
|
39
|
-
npm
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
const npmClient = factory(npm, { options });
|
|
45
|
-
t.is(npmClient.getPackageUrl(), 'https://registry.example.org/custom/public-path/my-cool-package');
|
|
46
|
-
});
|
|
16
|
+
test('should return npm package url (custom registry)', async () => {
|
|
17
|
+
const options = { npm: { name: 'my-cool-package', publishConfig: { registry: 'https://registry.example.org/' } } };
|
|
18
|
+
const npmClient = factory(npm, { options });
|
|
19
|
+
assert.equal(npmClient.getPackageUrl(), 'https://registry.example.org/package/my-cool-package');
|
|
20
|
+
});
|
|
47
21
|
|
|
48
|
-
test('should return
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
22
|
+
test('should return npm package url (custom publicPath)', async () => {
|
|
23
|
+
const options = { npm: { name: 'my-cool-package', publishConfig: { publicPath: '/custom/public-path' } } };
|
|
24
|
+
const npmClient = factory(npm, { options });
|
|
25
|
+
assert.equal(npmClient.getPackageUrl(), 'https://www.npmjs.com/custom/public-path/my-cool-package');
|
|
26
|
+
});
|
|
53
27
|
|
|
54
|
-
test('should
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
28
|
+
test('should return npm package url (custom registry and publicPath)', async () => {
|
|
29
|
+
const options = {
|
|
30
|
+
npm: {
|
|
31
|
+
name: 'my-cool-package',
|
|
32
|
+
publishConfig: { registry: 'https://registry.example.org/', publicPath: '/custom/public-path' }
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const npmClient = factory(npm, { options });
|
|
36
|
+
assert.equal(npmClient.getPackageUrl(), 'https://registry.example.org/custom/public-path/my-cool-package');
|
|
37
|
+
});
|
|
61
38
|
|
|
62
|
-
test('should
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
stub.restore();
|
|
68
|
-
});
|
|
39
|
+
test('should return default tag', async () => {
|
|
40
|
+
const npmClient = factory(npm);
|
|
41
|
+
const tag = await npmClient.resolveTag();
|
|
42
|
+
assert.equal(tag, 'latest');
|
|
43
|
+
});
|
|
69
44
|
|
|
70
|
-
test('should
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
45
|
+
test('should resolve default tag for pre-release', async t => {
|
|
46
|
+
const npmClient = factory(npm);
|
|
47
|
+
t.mock.method(npmClient, 'getRegistryPreReleaseTags', () => []);
|
|
48
|
+
const tag = await npmClient.resolveTag('1.0.0-0');
|
|
49
|
+
assert.equal(tag, 'next');
|
|
50
|
+
});
|
|
75
51
|
|
|
76
|
-
test('should
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
exec.restore();
|
|
83
|
-
});
|
|
52
|
+
test('should guess tag from registry for pre-release', async t => {
|
|
53
|
+
const npmClient = factory(npm);
|
|
54
|
+
t.mock.method(npmClient, 'getRegistryPreReleaseTags', () => ['alpha']);
|
|
55
|
+
const tag = await npmClient.resolveTag('1.0.0-0');
|
|
56
|
+
assert.equal(tag, 'alpha');
|
|
57
|
+
});
|
|
84
58
|
|
|
85
|
-
test('should
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.
|
|
89
|
-
|
|
59
|
+
test('should derive tag from pre-release version', async () => {
|
|
60
|
+
const npmClient = factory(npm);
|
|
61
|
+
const tag = await npmClient.resolveTag('1.0.2-alpha.3');
|
|
62
|
+
assert.equal(tag, 'alpha');
|
|
63
|
+
});
|
|
90
64
|
|
|
91
|
-
|
|
65
|
+
test('should use provided (default) tag even for pre-release', async t => {
|
|
66
|
+
const options = { npm: { tag: 'latest' } };
|
|
67
|
+
const npmClient = factory(npm, { options });
|
|
68
|
+
t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
69
|
+
await npmClient.bump('1.0.0-next.0');
|
|
70
|
+
assert.equal(npmClient.getContext('tag'), 'latest');
|
|
71
|
+
});
|
|
92
72
|
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
test('should throw when `npm version` fails', async t => {
|
|
74
|
+
const npmClient = factory(npm);
|
|
75
|
+
t.mock.method(npmClient.shell, 'exec', () =>
|
|
76
|
+
Promise.reject(new Error('npm ERR! Version not changed, might want --allow-same-version'))
|
|
77
|
+
);
|
|
78
|
+
await assert.rejects(npmClient.bump('1.0.0-next.0'), { message: /Version not changed/ });
|
|
79
|
+
});
|
|
95
80
|
|
|
96
|
-
test('should return first pre-release tag from package in registry when resolving tag without pre-id', async t => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
};
|
|
103
|
-
const exec = sinon.stub(npmClient.shell, 'exec').resolves(JSON.stringify(response));
|
|
104
|
-
t.is(await npmClient.resolveTag('2.0.0-5'), 'alpha');
|
|
105
|
-
exec.restore();
|
|
106
|
-
});
|
|
81
|
+
test('should return first pre-release tag from package in registry when resolving tag without pre-id', async t => {
|
|
82
|
+
const npmClient = factory(npm);
|
|
83
|
+
const response = { latest: '1.4.1', alpha: '2.0.0-alpha.1', beta: '2.0.0-beta.3' };
|
|
84
|
+
t.mock.method(npmClient.shell, 'exec', () => Promise.resolve(JSON.stringify(response)));
|
|
85
|
+
assert.equal(await npmClient.resolveTag('2.0.0-5'), 'alpha');
|
|
86
|
+
});
|
|
107
87
|
|
|
108
|
-
test('should return default pre-release tag when resolving tag without pre-id', async t => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
88
|
+
test('should return default pre-release tag when resolving tag without pre-id', async t => {
|
|
89
|
+
const npmClient = factory(npm);
|
|
90
|
+
const response = {
|
|
91
|
+
latest: '1.4.1'
|
|
92
|
+
};
|
|
93
|
+
t.mock.method(npmClient.shell, 'exec', () => Promise.resolve(JSON.stringify(response)));
|
|
94
|
+
assert.equal(await npmClient.resolveTag('2.0.0-0'), 'next');
|
|
95
|
+
});
|
|
117
96
|
|
|
118
|
-
test('should handle erroneous output when resolving tag without pre-id', async t => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
97
|
+
test('should handle erroneous output when resolving tag without pre-id', async t => {
|
|
98
|
+
const npmClient = factory(npm);
|
|
99
|
+
t.mock.method(npmClient.shell, 'exec', () => Promise.resolve(''));
|
|
100
|
+
assert.equal(await npmClient.resolveTag('2.0.0-0'), 'next');
|
|
101
|
+
});
|
|
124
102
|
|
|
125
|
-
test('should handle errored request when resolving tag without pre-id', async t => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
103
|
+
test('should handle errored request when resolving tag without pre-id', async t => {
|
|
104
|
+
const npmClient = factory(npm);
|
|
105
|
+
t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
106
|
+
assert.equal(await npmClient.resolveTag('2.0.0-0'), 'next');
|
|
107
|
+
});
|
|
131
108
|
|
|
132
|
-
test('should add registry to commands when specified', async t => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
t.is(exec.args[0][0], 'npm ping --registry registry.example.org');
|
|
142
|
-
t.is(exec.args[1][0], 'npm whoami --registry registry.example.org');
|
|
143
|
-
t.regex(exec.args[2][0], /npm show release-it@[a-z]+ version --registry registry\.example\.org/);
|
|
144
|
-
exec.restore();
|
|
145
|
-
});
|
|
109
|
+
test('should add registry to commands when specified', async t => {
|
|
110
|
+
const npmClient = factory(npm);
|
|
111
|
+
npmClient.setContext({ publishConfig: { registry: 'registry.example.org' } });
|
|
112
|
+
const exec = t.mock.method(npmClient.shell, 'exec', command => {
|
|
113
|
+
if (command === 'npm whoami --registry registry.example.org') return Promise.resolve('john');
|
|
114
|
+
const re = /npm access (list collaborators --json|ls-collaborators) release-it --registry registry.example.org/;
|
|
115
|
+
if (re.test.command) return Promise.resolve(JSON.stringify({ john: ['write'] }));
|
|
116
|
+
return Promise.resolve();
|
|
117
|
+
});
|
|
146
118
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
exec.restore();
|
|
156
|
-
});
|
|
119
|
+
await runTasks(npmClient);
|
|
120
|
+
assert.equal(exec.mock.calls[0].arguments[0], 'npm ping --registry registry.example.org');
|
|
121
|
+
assert.equal(exec.mock.calls[1].arguments[0], 'npm whoami --registry registry.example.org');
|
|
122
|
+
assert.match(
|
|
123
|
+
exec.mock.calls[2].arguments[0],
|
|
124
|
+
/npm show release-it@[a-z]+ version --registry registry\.example\.org/
|
|
125
|
+
);
|
|
126
|
+
});
|
|
157
127
|
|
|
158
|
-
test('should throw
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
128
|
+
test('should not throw when executing tasks', async t => {
|
|
129
|
+
const npmClient = factory(npm);
|
|
130
|
+
t.mock.method(npmClient.shell, 'exec', command => {
|
|
131
|
+
if (command === 'npm whoami') return Promise.resolve('john');
|
|
132
|
+
const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
|
|
133
|
+
if (re.test.command) return Promise.resolve(JSON.stringify({ john: ['write'] }));
|
|
134
|
+
return Promise.resolve();
|
|
135
|
+
});
|
|
136
|
+
await assert.doesNotReject(runTasks(npmClient));
|
|
137
|
+
});
|
|
165
138
|
|
|
166
|
-
test('should
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
exec.withArgs('npm access').rejects(new Error(accessError));
|
|
175
|
-
await runTasks(npmClient);
|
|
176
|
-
t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
|
|
177
|
-
exec.restore();
|
|
178
|
-
});
|
|
139
|
+
test('should throw if npm is down', async t => {
|
|
140
|
+
const npmClient = factory(npm);
|
|
141
|
+
t.mock.method(npmClient.shell, 'exec', command => {
|
|
142
|
+
if (command === 'npm ping') return Promise.reject();
|
|
143
|
+
return Promise.resolve();
|
|
144
|
+
});
|
|
145
|
+
await assert.rejects(runTasks(npmClient), { message: /^Unable to reach npm registry/ });
|
|
146
|
+
});
|
|
179
147
|
|
|
180
|
-
test('should not throw if npm returns 400 for unsupported ping/whoami/access', async t => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
});
|
|
148
|
+
test('should not throw if npm returns 400/404 for unsupported ping/whoami/access', async t => {
|
|
149
|
+
const npmClient = factory(npm);
|
|
150
|
+
const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
151
|
+
const pingError = "npm ERR! code E404\nnpm ERR! 404 Package '--ping' not found : ping";
|
|
152
|
+
const whoamiError = "npm ERR! code E404\nnpm ERR! 404 Package '--whoami' not found : whoami";
|
|
153
|
+
const accessError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/collaborators';
|
|
154
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error(pingError)), 0);
|
|
155
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error(whoamiError)), 1);
|
|
156
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error(accessError)), 2);
|
|
157
|
+
await runTasks(npmClient);
|
|
158
|
+
assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
|
|
159
|
+
});
|
|
193
160
|
|
|
194
|
-
test('should not throw if npm returns
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
.
|
|
202
|
-
.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
});
|
|
161
|
+
test('should not throw if npm returns 400 for unsupported ping/whoami/access', async t => {
|
|
162
|
+
const npmClient = factory(npm);
|
|
163
|
+
const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
164
|
+
const pingError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/ping?write=true';
|
|
165
|
+
const whoamiError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/whoami';
|
|
166
|
+
const accessError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/collaborators';
|
|
167
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error(pingError)), 0);
|
|
168
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error(whoamiError)), 1);
|
|
169
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error(accessError)), 2);
|
|
170
|
+
await runTasks(npmClient);
|
|
171
|
+
assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
|
|
172
|
+
});
|
|
207
173
|
|
|
208
|
-
test('should throw if user is not authenticated', async t => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
174
|
+
test('should throw if user is not authenticated', async t => {
|
|
175
|
+
const npmClient = factory(npm);
|
|
176
|
+
const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
177
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(), 1);
|
|
178
|
+
await assert.rejects(runTasks(npmClient), { message: /^Not authenticated with npm/ });
|
|
179
|
+
});
|
|
215
180
|
|
|
216
|
-
test('should throw if user is not a collaborator (v9)', async t => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
});
|
|
181
|
+
test('should throw if user is not a collaborator (v9)', async t => {
|
|
182
|
+
const npmClient = factory(npm);
|
|
183
|
+
t.mock.method(npmClient.shell, 'exec', command => {
|
|
184
|
+
if (command === 'npm whoami') return Promise.resolve('ada');
|
|
185
|
+
if (command === 'npm --version') return Promise.resolve('9.2.0');
|
|
186
|
+
if (command === 'npm access list collaborators --json release-it')
|
|
187
|
+
return Promise.resolve(JSON.stringify({ john: ['write'] }));
|
|
188
|
+
return Promise.resolve();
|
|
189
|
+
});
|
|
190
|
+
await assert.rejects(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
|
|
191
|
+
});
|
|
225
192
|
|
|
226
|
-
test('should throw if user is not a collaborator (v8)', async t => {
|
|
227
|
-
|
|
228
|
-
const exec = sinon.stub(npmClient.shell, 'exec').resolves();
|
|
229
|
-
exec.withArgs('npm whoami').resolves('ada');
|
|
230
|
-
exec.withArgs('npm --version').resolves('8.2.0');
|
|
231
|
-
exec
|
|
232
|
-
.withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
|
|
233
|
-
.resolves(JSON.stringify({ john: ['write'] }));
|
|
234
|
-
await t.throwsAsync(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
|
|
235
|
-
exec.restore();
|
|
236
|
-
});
|
|
193
|
+
test('should throw if user is not a collaborator (v8)', async t => {
|
|
194
|
+
const npmClient = factory(npm);
|
|
237
195
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
new Error(
|
|
246
|
-
'npm ERR! code E404\nnpm ERR! 404 Not Found - GET https://registry.npmjs.org/-/package/release-it/collaborators?format=cli - File not found'
|
|
247
|
-
)
|
|
248
|
-
);
|
|
249
|
-
await t.notThrowsAsync(runTasks(npmClient));
|
|
250
|
-
exec.restore();
|
|
251
|
-
});
|
|
196
|
+
t.mock.method(npmClient.shell, 'exec', command => {
|
|
197
|
+
if (command === 'npm whoami') return Promise.resolve('ada');
|
|
198
|
+
if (command === 'npm --version') return Promise.resolve('8.2.0');
|
|
199
|
+
const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
|
|
200
|
+
if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
|
|
201
|
+
return Promise.resolve();
|
|
202
|
+
});
|
|
252
203
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const npmClient = factory(npm, { options });
|
|
256
|
-
npmClient.setContext({ name: '@scoped/pkg' });
|
|
257
|
-
const exec = sinon.spy(npmClient.shell, 'exec');
|
|
258
|
-
await npmClient.publish();
|
|
259
|
-
t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag beta');
|
|
260
|
-
exec.restore();
|
|
261
|
-
});
|
|
204
|
+
await assert.rejects(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
|
|
205
|
+
});
|
|
262
206
|
|
|
263
|
-
test('should not
|
|
264
|
-
|
|
265
|
-
npmClient.setContext({ name: 'pkg', private: true });
|
|
266
|
-
const exec = sinon.spy(npmClient.shell, 'exec');
|
|
267
|
-
await npmClient.publish();
|
|
268
|
-
const publish = exec.args.filter(arg => arg[0].startsWith('npm publish'));
|
|
269
|
-
t.is(publish.length, 0);
|
|
270
|
-
t.regex(npmClient.log.warn.lastCall.args[0], /package is private/);
|
|
271
|
-
});
|
|
207
|
+
test('should not throw if user is not a collaborator on a new package', async t => {
|
|
208
|
+
const npmClient = factory(npm);
|
|
272
209
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
210
|
+
t.mock.method(npmClient.shell, 'exec', command => {
|
|
211
|
+
if (command === 'npm whoami') return Promise.resolve('ada');
|
|
212
|
+
const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
|
|
213
|
+
const message =
|
|
214
|
+
'npm ERR! code E404\nnpm ERR! 404 Not Found - GET https://registry.npmjs.org/-/package/release-it/collaborators?format=cli - File not found';
|
|
215
|
+
if (re.test(command)) return Promise.reject(new Error(message));
|
|
216
|
+
return Promise.resolve();
|
|
217
|
+
});
|
|
276
218
|
|
|
277
|
-
|
|
219
|
+
await assert.doesNotReject(runTasks(npmClient));
|
|
220
|
+
});
|
|
278
221
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
222
|
+
test('should handle 2FA and publish with OTP', async t => {
|
|
223
|
+
const npmClient = factory(npm);
|
|
224
|
+
npmClient.setContext({ name: 'pkg' });
|
|
282
225
|
|
|
283
|
-
|
|
284
|
-
otpCallback: () =>
|
|
285
|
-
npmClient.publish({
|
|
286
|
-
otp: '123',
|
|
287
|
-
otpCallback: () => npmClient.publish({ otp: '123456' })
|
|
288
|
-
})
|
|
289
|
-
});
|
|
226
|
+
const exec = t.mock.method(npmClient.shell, 'exec');
|
|
290
227
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
t.is(exec.thirdCall.args[0].trim(), 'npm publish . --tag latest --otp 123456');
|
|
228
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error('Initial error with one-time pass.')), 0);
|
|
229
|
+
exec.mock.mockImplementationOnce(() => Promise.reject(new Error('The provided one-time pass is incorrect.')), 1);
|
|
230
|
+
exec.mock.mockImplementationOnce(() => Promise.resolve(), 2);
|
|
295
231
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
232
|
+
await npmClient.publish({
|
|
233
|
+
otpCallback: () =>
|
|
234
|
+
npmClient.publish({
|
|
235
|
+
otp: '123',
|
|
236
|
+
otpCallback: () => npmClient.publish({ otp: '123456' })
|
|
237
|
+
})
|
|
238
|
+
});
|
|
299
239
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
exec
|
|
305
|
-
.withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
|
|
306
|
-
.resolves(JSON.stringify({ john: ['write'] }));
|
|
307
|
-
await runTasks(npmClient);
|
|
308
|
-
t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
|
|
309
|
-
exec.restore();
|
|
310
|
-
});
|
|
240
|
+
assert.equal(exec.mock.callCount(), 3);
|
|
241
|
+
assert.equal(exec.mock.calls[0].arguments[0].trim(), 'npm publish . --tag latest');
|
|
242
|
+
assert.equal(exec.mock.calls[1].arguments[0].trim(), 'npm publish . --tag latest --otp 123');
|
|
243
|
+
assert.equal(exec.mock.calls[2].arguments[0].trim(), 'npm publish . --tag latest --otp 123456');
|
|
311
244
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const exec = sinon.stub(npmClient.shell, 'exec').resolves();
|
|
316
|
-
await runTasks(npmClient);
|
|
317
|
-
t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest --registry=http://my-internal-registry.local');
|
|
318
|
-
exec.restore();
|
|
319
|
-
});
|
|
245
|
+
assert.equal(npmClient.log.warn.mock.callCount(), 1);
|
|
246
|
+
assert.equal(npmClient.log.warn.mock.calls[0].arguments[0], 'The provided OTP is incorrect or has expired.');
|
|
247
|
+
});
|
|
320
248
|
|
|
321
|
-
test('should
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
249
|
+
test('should publish', async t => {
|
|
250
|
+
const npmClient = factory(npm);
|
|
251
|
+
const exec = t.mock.method(npmClient.shell, 'exec', command => {
|
|
252
|
+
if (command === 'npm whoami') return Promise.resolve('john');
|
|
253
|
+
const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
|
|
254
|
+
if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
|
|
255
|
+
return Promise.resolve();
|
|
256
|
+
});
|
|
257
|
+
await runTasks(npmClient);
|
|
258
|
+
assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
|
|
259
|
+
});
|
|
326
260
|
|
|
327
|
-
test('should
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
})
|
|
261
|
+
test('should use extra publish arguments', async t => {
|
|
262
|
+
const options = { npm: { skipChecks: true, publishArgs: '--registry=http://my-internal-registry.local' } };
|
|
263
|
+
const npmClient = factory(npm, { options });
|
|
264
|
+
const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
265
|
+
await runTasks(npmClient);
|
|
266
|
+
assert.equal(
|
|
267
|
+
exec.mock.calls.at(-1).arguments[0].trim(),
|
|
268
|
+
'npm publish . --tag latest --registry=http://my-internal-registry.local'
|
|
269
|
+
);
|
|
337
270
|
});
|
|
338
|
-
const options = { npm };
|
|
339
|
-
const npmClient = factory(npm, { options });
|
|
340
|
-
const exec = sinon.stub(npmClient.shell, 'exec').resolves();
|
|
341
|
-
exec
|
|
342
|
-
.withArgs('npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/')
|
|
343
|
-
.resolves('john');
|
|
344
|
-
exec
|
|
345
|
-
.withArgs(
|
|
346
|
-
/npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg --registry https:\/\/gitlab\.com\/api\/v4\/projects\/my-scope%2Fmy-pkg\/packages\/npm\//
|
|
347
|
-
)
|
|
348
|
-
.resolves(JSON.stringify({ john: ['write'] }));
|
|
349
|
-
|
|
350
|
-
await runTasks(npmClient);
|
|
351
|
-
|
|
352
|
-
t.deepEqual(getArgs(exec.args, 'npm'), [
|
|
353
|
-
'npm ping --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
|
|
354
|
-
'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
|
|
355
|
-
'npm show @my-scope/my-pkg@latest version --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
|
|
356
|
-
'npm --version',
|
|
357
|
-
'npm version 1.0.1 --no-git-tag-version',
|
|
358
|
-
'npm publish . --tag latest --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
|
|
359
|
-
]);
|
|
360
|
-
|
|
361
|
-
exec.restore();
|
|
362
|
-
resetFs();
|
|
363
|
-
});
|
|
364
271
|
|
|
365
|
-
test('should
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
version: '1.0.0'
|
|
370
|
-
})
|
|
272
|
+
test('should skip checks', async () => {
|
|
273
|
+
const options = { npm: { skipChecks: true } };
|
|
274
|
+
const npmClient = factory(npm, { options });
|
|
275
|
+
await assert.doesNotReject(npmClient.init());
|
|
371
276
|
});
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
277
|
+
|
|
278
|
+
test('should publish to a different/scoped registry', async t => {
|
|
279
|
+
const tmp = mkTmpDir();
|
|
280
|
+
process.chdir(tmp);
|
|
281
|
+
writeFileSync(
|
|
282
|
+
join(tmp, 'package.json'),
|
|
283
|
+
JSON.stringify({
|
|
284
|
+
name: '@my-scope/my-pkg',
|
|
285
|
+
version: '1.0.0',
|
|
286
|
+
publishConfig: {
|
|
287
|
+
access: 'public',
|
|
288
|
+
'@my-scope:registry': 'https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
);
|
|
292
|
+
const options = { npm };
|
|
293
|
+
const npmClient = factory(npm, { options });
|
|
294
|
+
const exec = t.mock.method(npmClient.shell, 'exec', command => {
|
|
295
|
+
const cmd = 'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/';
|
|
296
|
+
if (command === cmd) return Promise.resolve('john');
|
|
297
|
+
const re =
|
|
298
|
+
/npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg --registry https:\/\/gitlab\.com\/api\/v4\/projects\/my-scope%2Fmy-pkg\/packages\/npm\//;
|
|
299
|
+
if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
|
|
300
|
+
return Promise.resolve();
|
|
301
|
+
});
|
|
385
302
|
await runTasks(npmClient);
|
|
386
|
-
} catch (error) {
|
|
387
|
-
t.regex(error.toString(), /Version not changed/);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
t.deepEqual(getArgs(exec.args, 'npm'), [
|
|
391
|
-
'npm ping',
|
|
392
|
-
'npm whoami',
|
|
393
|
-
'npm show @my-scope/my-pkg@latest version',
|
|
394
|
-
'npm --version',
|
|
395
|
-
'npm version 1.0.1 --no-git-tag-version'
|
|
396
|
-
]);
|
|
397
|
-
|
|
398
|
-
exec.restore();
|
|
399
|
-
resetFs();
|
|
400
|
-
});
|
|
401
303
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
304
|
+
assert.deepEqual(getArgs(exec, 'npm'), [
|
|
305
|
+
'npm ping --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
|
|
306
|
+
'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
|
|
307
|
+
'npm show @my-scope/my-pkg@latest version --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
|
|
308
|
+
'npm --version',
|
|
309
|
+
'npm version 1.0.1 --no-git-tag-version',
|
|
310
|
+
'npm publish . --tag latest --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
|
|
311
|
+
]);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('should not publish when `npm version` fails', async t => {
|
|
315
|
+
const tmp = mkTmpDir();
|
|
316
|
+
process.chdir(tmp);
|
|
317
|
+
writeFileSync(join(tmp, 'package.json'), JSON.stringify({ name: '@my-scope/my-pkg', version: '1.0.0' }));
|
|
318
|
+
const options = { npm };
|
|
319
|
+
const npmClient = factory(npm, { options });
|
|
320
|
+
|
|
321
|
+
const exec = t.mock.method(npmClient.shell, 'exec', command => {
|
|
322
|
+
if (command === 'npm whoami') return Promise.resolve('john');
|
|
323
|
+
const re = /npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg/;
|
|
324
|
+
if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
|
|
325
|
+
if (command === 'npm version 1.0.1 --no-git-tag-version')
|
|
326
|
+
return Promise.reject('npm ERR! Version not changed, might want --allow-same-version');
|
|
327
|
+
return Promise.resolve();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
await assert.rejects(runTasks(npmClient), /Version not changed/);
|
|
331
|
+
|
|
332
|
+
assert.deepEqual(getArgs(exec, 'npm'), [
|
|
333
|
+
'npm ping',
|
|
334
|
+
'npm whoami',
|
|
335
|
+
'npm show @my-scope/my-pkg@latest version',
|
|
336
|
+
'npm --version',
|
|
337
|
+
'npm version 1.0.1 --no-git-tag-version'
|
|
338
|
+
]);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test('should add allow-same-version argument', async t => {
|
|
342
|
+
const options = { npm: { skipChecks: true, allowSameVersion: true } };
|
|
343
|
+
const npmClient = factory(npm, { options });
|
|
344
|
+
|
|
345
|
+
const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
410
346
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
347
|
+
await runTasks(npmClient);
|
|
348
|
+
const versionArgs = getArgs(exec, 'npm version');
|
|
349
|
+
assert.match(versionArgs[0], / --allow-same-version/);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('should add version arguments', async t => {
|
|
353
|
+
const options = { npm: { skipChecks: true, versionArgs: ['--workspaces-update=false', '--allow-same-version'] } };
|
|
354
|
+
const npmClient = factory(npm, { options });
|
|
355
|
+
const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
|
|
356
|
+
await runTasks(npmClient);
|
|
357
|
+
const versionArgs = getArgs(exec, 'npm version');
|
|
358
|
+
assert.match(versionArgs[0], / --workspaces-update=false --allow-same-version/);
|
|
359
|
+
});
|
|
418
360
|
});
|