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/test/tasks.js CHANGED
@@ -1,12 +1,10 @@
1
1
  import path from 'node:path';
2
2
  import childProcess from 'node:child_process';
3
3
  import { appendFileSync, mkdirSync, renameSync } from 'node:fs';
4
- import test from 'ava';
4
+ import test, { after, afterEach, before, beforeEach, describe } from 'node:test';
5
+ import assert from 'node:assert/strict';
5
6
  import semver from 'semver';
6
- import _ from 'lodash';
7
- import sinon from 'sinon';
8
- import Log from '../lib/log.js';
9
- import Spinner from '../lib/spinner.js';
7
+ import { MockServer, FetchMocker } from 'mentoss';
10
8
  import Config from '../lib/config.js';
11
9
  import runTasks from '../lib/index.js';
12
10
  import Git from '../lib/plugin/git/Git.js';
@@ -25,487 +23,472 @@ import {
25
23
  interceptCreate as interceptGitHubCreate,
26
24
  interceptAsset as interceptGitHubAsset
27
25
  } from './stub/github.js';
28
- import { factory } from './util/index.js';
26
+ import { factory, LogStub, SpinnerStub } from './util/index.js';
29
27
 
30
- const rootDir = new URL('..', import.meta.url);
28
+ describe('tasks', () => {
29
+ const rootDir = new URL('..', import.meta.url);
31
30
 
32
- const noop = Promise.resolve();
31
+ const github = new MockServer('https://api.github.com');
32
+ const assets = new MockServer('https://uploads.github.com');
33
+ const gitlab = new MockServer('https://gitlab.com/api/v4');
33
34
 
34
- const sandbox = sinon.createSandbox();
35
+ const mocker = new FetchMocker({
36
+ servers: [github, assets, gitlab]
37
+ });
35
38
 
36
- const npmMajorVersion = semver.major(process.env.npm_config_user_agent.match(/npm\/([^ ]+)/)[1]);
39
+ const npmMajorVersion = semver.major(process.env.npm_config_user_agent.match(/npm\/([^ ]+)/)[1]);
37
40
 
38
- const testConfig = {
39
- ci: true,
40
- config: false
41
- };
41
+ const testConfig = {
42
+ ci: true,
43
+ config: false
44
+ };
42
45
 
43
- const log = sandbox.createStubInstance(Log);
44
- const spinner = sandbox.createStubInstance(Spinner);
45
- spinner.show.callsFake(({ enabled = true, task }) => (enabled ? task() : noop));
46
+ const log = new LogStub();
47
+ const spinner = new SpinnerStub();
46
48
 
47
- const getContainer = options => {
48
- const config = new Config(Object.assign({}, testConfig, options));
49
- const shell = new ShellStub({ container: { log, config } });
50
- return {
51
- log,
52
- spinner,
53
- config,
54
- shell
49
+ const getContainer = options => {
50
+ const config = new Config(Object.assign({}, testConfig, options));
51
+ const shell = new ShellStub({ container: { log, config } });
52
+ return { log, spinner, config, shell };
55
53
  };
56
- };
57
54
 
58
- test.before(t => {
59
- t.timeout(90 * 1000);
60
- });
55
+ before(() => {
56
+ mocker.mockGlobal();
57
+ });
61
58
 
62
- test.serial.beforeEach(t => {
63
- const bare = mkTmpDir();
64
- const target = mkTmpDir();
65
- process.chdir(bare);
66
- childProcess.execSync(`git init --bare .`, execOpts);
67
- childProcess.execSync(`git clone ${bare} ${target}`, execOpts);
68
- process.chdir(target);
69
- gitAdd('line', 'file', 'Add file');
70
- t.context = { bare, target };
71
- });
59
+ let bare;
60
+ let target;
61
+ beforeEach(async () => {
62
+ bare = mkTmpDir();
63
+ target = mkTmpDir();
64
+ process.chdir(bare);
65
+ childProcess.execSync(`git init --bare .`, execOpts);
66
+ childProcess.execSync(`git clone ${bare} ${target}`, execOpts);
67
+ process.chdir(target);
68
+ gitAdd('line', 'file', 'Add file');
69
+ });
72
70
 
73
- test.serial.afterEach(() => {
74
- sandbox.resetHistory();
75
- });
71
+ afterEach(() => {
72
+ mocker.clearAll();
73
+ log.resetCalls();
74
+ });
76
75
 
77
- test.serial('should run tasks without throwing errors', async t => {
78
- renameSync('.git', 'foo');
79
- const { name, latestVersion, version } = await runTasks({}, getContainer());
80
- t.true(log.obtrusive.firstCall.args[0].includes(`release ${name} (${latestVersion}...${version})`));
81
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
82
- });
76
+ after(() => {
77
+ mocker.unmockGlobal();
78
+ });
79
+
80
+ test('should run tasks without throwing errors', async () => {
81
+ renameSync('.git', 'foo');
82
+ const { name, latestVersion, version } = await runTasks({}, getContainer());
83
+ assert(log.obtrusive.mock.calls[0].arguments[0].includes(`release ${name} (${latestVersion}...${version})`));
84
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
85
+ });
83
86
 
84
- test.serial('should run tasks without package.json', async t => {
85
- childProcess.execSync('git tag 1.0.0', execOpts);
86
- gitAdd('line', 'file', 'Add file');
87
- const { name } = await runTasks({}, getContainer({ increment: 'major', git: { commit: false } }));
88
- t.true(log.obtrusive.firstCall.args[0].includes(`release ${name} (1.0.0...2.0.0)`));
89
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
90
- t.is(log.warn.callCount, 0);
91
- {
87
+ test('should run tasks without package.json', async () => {
88
+ childProcess.execSync('git tag 1.0.0', execOpts);
89
+ gitAdd('line', 'file', 'Add file');
90
+ const { name } = await runTasks({}, getContainer({ increment: 'major', git: { commit: false } }));
91
+ assert(log.obtrusive.mock.calls[0].arguments[0].includes(`release ${name} (1.0.0...2.0.0)`));
92
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
93
+ assert.equal(log.warn.mock.callCount(), 0);
92
94
  const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', {
93
95
  encoding: 'utf-8'
94
96
  });
95
- t.is(stdout.trim(), '2.0.0');
96
- }
97
- });
98
-
99
- test.serial('should disable plugins', async t => {
100
- gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
101
- childProcess.execSync('git tag 1.2.3', execOpts);
102
- gitAdd('line', 'file', 'Add file');
103
- const container = getContainer({ increment: 'minor', git: false, npm: false });
104
- const { latestVersion, version } = await runTasks({}, container);
105
- t.is(latestVersion, '0.0.0');
106
- t.is(version, '0.1.0');
107
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
108
- });
109
-
110
- test.serial('should run tasks with minimal config and without any warnings/errors', async t => {
111
- gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
112
- childProcess.execSync('git tag 1.2.3', execOpts);
113
- gitAdd('line', 'file', 'More file');
114
- await runTasks({}, getContainer({ increment: 'patch' }));
115
- t.true(log.obtrusive.firstCall.args[0].includes('release my-package (1.2.3...1.2.4)'));
116
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
117
- const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
118
- t.is(stdout.trim(), '1.2.4');
119
- });
97
+ assert.equal(stdout.trim(), '2.0.0');
98
+ });
120
99
 
121
- test.serial('should use pkg.version', async t => {
122
- gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
123
- await runTasks({}, getContainer({ increment: 'minor' }));
124
- t.true(log.obtrusive.firstCall.args[0].includes('release my-package (1.2.3...1.3.0)'));
125
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
126
- const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
127
- t.is(stdout.trim(), '1.3.0');
128
- });
100
+ test('should disable plugins', async () => {
101
+ gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
102
+ childProcess.execSync('git tag 1.2.3', execOpts);
103
+ gitAdd('line', 'file', 'Add file');
104
+ const container = getContainer({ increment: 'minor', git: false, npm: false });
105
+ const { latestVersion, version } = await runTasks({}, container);
106
+ assert.equal(latestVersion, '0.0.0');
107
+ assert.equal(version, '0.1.0');
108
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
109
+ });
129
110
 
130
- test.serial('should use pkg.version (in sub dir) w/o tagging repo', async t => {
131
- gitAdd('{"name":"root-package","version":"1.0.0"}', 'package.json', 'Add package.json');
132
- childProcess.execSync('git tag 1.0.0', execOpts);
133
- mkdirSync('my-package');
134
- process.chdir('my-package');
135
- gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
136
- const container = getContainer({ increment: 'minor', git: { tag: false } });
137
- const exec = sinon.spy(container.shell, 'exec');
138
- await runTasks({}, container);
139
- t.true(log.obtrusive.firstCall.args[0].endsWith('release my-package (1.2.3...1.3.0)'));
140
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
141
- const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
142
- t.is(stdout.trim(), '1.0.0');
143
- const npmArgs = getArgs(exec.args, 'npm');
144
- t.is(npmArgs[5], 'npm version 1.3.0 --no-git-tag-version');
145
- exec.restore();
146
- });
111
+ test('should run tasks with minimal config and without any warnings/errors', async () => {
112
+ gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
113
+ childProcess.execSync('git tag 1.2.3', execOpts);
114
+ gitAdd('line', 'file', 'More file');
115
+ await runTasks({}, getContainer({ increment: 'patch' }));
116
+ assert(log.obtrusive.mock.calls[0].arguments[0].includes('release my-package (1.2.3...1.2.4)'));
117
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
118
+ const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
119
+ assert.equal(stdout.trim(), '1.2.4');
120
+ });
147
121
 
148
- test.serial('should ignore version in pkg.version and use git tag instead', async t => {
149
- gitAdd('{"name":"my-package","version":"0.0.0"}', 'package.json', 'Add package.json');
150
- childProcess.execSync('git tag 1.1.1', execOpts);
151
- gitAdd('line', 'file', 'More file');
152
- await runTasks({}, getContainer({ increment: 'minor', npm: { ignoreVersion: true } }));
153
- t.true(log.obtrusive.firstCall.args[0].includes('release my-package (1.1.1...1.2.0)'));
154
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
155
- const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
156
- t.is(stdout.trim(), '1.2.0');
157
- });
122
+ test('should use pkg.version', async () => {
123
+ gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
124
+ await runTasks({}, getContainer({ increment: 'minor' }));
125
+ assert(log.obtrusive.mock.calls[0].arguments[0].includes('release my-package (1.2.3...1.3.0)'));
126
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
127
+ const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
128
+ assert.equal(stdout.trim(), '1.3.0');
129
+ });
158
130
 
159
- test.serial('should release all the things (basic)', async t => {
160
- const { bare, target } = t.context;
161
- const project = path.basename(bare);
162
- const pkgName = path.basename(target);
163
- const owner = path.basename(path.dirname(bare));
164
- gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
165
- childProcess.execSync('git tag 1.0.0', execOpts);
166
- const sha = gitAdd('line', 'file', 'More file');
167
-
168
- interceptGitHubAuthentication();
169
- interceptGitHubCollaborator({ owner, project });
170
- interceptGitHubCreate({
171
- owner,
172
- project,
173
- body: { tag_name: '1.0.1', name: 'Release 1.0.1', body: `* More file (${sha})`, prerelease: false }
131
+ test('should use pkg.version (in sub dir) w/o tagging repo', async t => {
132
+ gitAdd('{"name":"root-package","version":"1.0.0"}', 'package.json', 'Add package.json');
133
+ childProcess.execSync('git tag 1.0.0', execOpts);
134
+ mkdirSync('my-package');
135
+ process.chdir('my-package');
136
+ gitAdd('{"name":"my-package","version":"1.2.3"}', 'package.json', 'Add package.json');
137
+ const container = getContainer({ increment: 'minor', git: { tag: false } });
138
+ const exec = t.mock.method(container.shell, 'exec');
139
+ await runTasks({}, container);
140
+ assert(log.obtrusive.mock.calls[0].arguments[0].endsWith('release my-package (1.2.3...1.3.0)'));
141
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
142
+ const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
143
+ assert.equal(stdout.trim(), '1.0.0');
144
+ const npmArgs = getArgs(exec, 'npm');
145
+ assert.equal(npmArgs[5], 'npm version 1.3.0 --no-git-tag-version');
174
146
  });
175
147
 
176
- const container = getContainer({
177
- github: { release: true, pushRepo: `https://github.com/${owner}/${project}` },
178
- npm: { name: pkgName }
148
+ test('should ignore version in pkg.version and use git tag instead', async () => {
149
+ gitAdd('{"name":"my-package","version":"0.0.0"}', 'package.json', 'Add package.json');
150
+ childProcess.execSync('git tag 1.1.1', execOpts);
151
+ gitAdd('line', 'file', 'More file');
152
+ await runTasks({}, getContainer({ increment: 'minor', npm: { ignoreVersion: true } }));
153
+ assert(log.obtrusive.mock.calls[0].arguments[0].includes('release my-package (1.1.1...1.2.0)'));
154
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
155
+ const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
156
+ assert.equal(stdout.trim(), '1.2.0');
179
157
  });
180
- const exec = sinon.spy(container.shell, 'exec');
181
158
 
182
- await runTasks({}, container);
159
+ test('should release all the things (basic)', async t => {
160
+ const project = path.basename(bare);
161
+ const pkgName = path.basename(target);
162
+ const owner = path.basename(path.dirname(bare));
163
+ gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
164
+ childProcess.execSync('git tag 1.0.0', execOpts);
165
+ const sha = gitAdd('line', 'file', 'More file');
166
+
167
+ interceptGitHubAuthentication(github);
168
+ interceptGitHubCollaborator(github, { owner, project });
169
+ interceptGitHubCreate(github, {
170
+ owner,
171
+ project,
172
+ body: { tag_name: '1.0.1', name: 'Release 1.0.1', body: `* More file (${sha})`, prerelease: false }
173
+ });
183
174
 
184
- const npmArgs = getArgs(container.shell.exec.args, 'npm');
175
+ const container = getContainer({
176
+ github: { release: true, pushRepo: `https://github.com/${owner}/${project}` },
177
+ npm: { name: pkgName }
178
+ });
185
179
 
186
- t.deepEqual(npmArgs, [
187
- 'npm ping',
188
- 'npm whoami',
189
- `npm show ${pkgName}@latest version`,
190
- 'npm --version',
191
- `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
192
- 'npm version 1.0.1 --no-git-tag-version',
193
- 'npm publish . --tag latest'
194
- ]);
180
+ const exec = t.mock.method(container.shell, 'exec');
195
181
 
196
- t.true(log.obtrusive.firstCall.args[0].endsWith(`release ${pkgName} (1.0.0...1.0.1)`));
197
- t.true(log.log.firstCall.args[0].endsWith(`https://www.npmjs.com/package/${pkgName}`));
198
- t.true(log.log.secondCall.args[0].endsWith(`https://github.com/${owner}/${project}/releases/tag/1.0.1`));
182
+ await runTasks({}, container);
199
183
 
200
- exec.restore();
201
- });
184
+ const npmArgs = getArgs(exec, 'npm');
202
185
 
203
- test.serial('should release with correct tag name', async t => {
204
- const { bare, target } = t.context;
205
- const project = path.basename(bare);
206
- const pkgName = path.basename(target);
207
- const owner = path.basename(path.dirname(bare));
208
- const stdout = childProcess.execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' });
209
- const branchName = stdout.trim();
210
- gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
211
- childProcess.execSync(`git tag ${pkgName}-${branchName}-1.0.0`, execOpts);
212
- const sha = gitAdd('line', 'file', 'More file');
213
-
214
- interceptGitHubCreate({
215
- owner,
216
- project,
217
- body: {
218
- tag_name: `${pkgName}-${branchName}-1.0.1`,
219
- name: 'Release 1.0.1',
220
- body: `* More file (${sha})`,
221
- prerelease: false
222
- }
223
- });
186
+ assert.deepEqual(npmArgs, [
187
+ 'npm ping',
188
+ 'npm whoami',
189
+ `npm show ${pkgName}@latest version`,
190
+ 'npm --version',
191
+ `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
192
+ 'npm version 1.0.1 --no-git-tag-version',
193
+ 'npm publish . --tag latest'
194
+ ]);
224
195
 
225
- const container = getContainer({
226
- git: { tagName: '${npm.name}-${branchName}-${version}' },
227
- github: { release: true, skipChecks: true, pushRepo: `https://github.com/${owner}/${project}` }
196
+ assert(log.obtrusive.mock.calls[0].arguments[0].endsWith(`release ${pkgName} (1.0.0...1.0.1)`));
197
+ assert(log.log.mock.calls[0].arguments[0].endsWith(`https://www.npmjs.com/package/${pkgName}`));
198
+ assert(log.log.mock.calls[1].arguments[0].endsWith(`https://github.com/${owner}/${project}/releases/tag/1.0.1`));
228
199
  });
229
200
 
230
- const exec = sinon.spy(container.shell, 'exec');
201
+ test('should release with correct tag name', async t => {
202
+ const project = path.basename(bare);
203
+ const pkgName = path.basename(target);
204
+ const owner = path.basename(path.dirname(bare));
205
+ const stdout = childProcess.execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' });
206
+ const branchName = stdout.trim();
207
+ gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
208
+ childProcess.execSync(`git tag ${pkgName}-${branchName}-1.0.0`, execOpts);
209
+ const sha = gitAdd('line', 'file', 'More file');
210
+
211
+ interceptGitHubCreate(github, {
212
+ owner,
213
+ project,
214
+ body: {
215
+ tag_name: `${pkgName}-${branchName}-1.0.1`,
216
+ name: 'Release 1.0.1',
217
+ body: `* More file (${sha})`,
218
+ prerelease: false
219
+ }
220
+ });
231
221
 
232
- await runTasks({}, container);
222
+ const container = getContainer({
223
+ git: { tagName: '${npm.name}-${branchName}-${version}' },
224
+ github: { release: true, skipChecks: true, pushRepo: `https://github.com/${owner}/${project}` }
225
+ });
233
226
 
234
- const gitArgs = getArgs(container.shell.exec.args, 'git');
227
+ const exec = t.mock.method(container.shell, 'exec');
235
228
 
236
- t.true(gitArgs.includes(`git tag --annotate --message Release 1.0.1 ${pkgName}-${branchName}-1.0.1`));
237
- t.true(
238
- log.log.secondCall.args[0].endsWith(
239
- `https://github.com/${owner}/${project}/releases/tag/${pkgName}-${branchName}-1.0.1`
240
- )
241
- );
229
+ await runTasks({}, container);
242
230
 
243
- exec.restore();
244
- });
231
+ const gitArgs = getArgs(exec, 'git');
245
232
 
246
- test.serial('should release all the things (pre-release, github, gitlab)', async t => {
247
- const { bare, target } = t.context;
248
- const project = path.basename(bare);
249
- const pkgName = path.basename(target);
250
- const owner = path.basename(path.dirname(bare));
251
- const url = `https://gitlab.com/${owner}/${project}`;
252
- gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
253
- childProcess.execSync('git tag v1.0.0', execOpts);
254
- const sha = gitAdd('line', 'file', 'More file');
255
- childProcess.execSync('git push --follow-tags', execOpts);
256
- const git = factory(Git);
257
- const ref = (await git.getBranchName()) ?? 'HEAD';
258
-
259
- interceptGitHubAuthentication();
260
- interceptGitHubCollaborator({ owner, project });
261
- interceptGitHubCreate({
262
- owner,
263
- project,
264
- body: {
265
- tag_name: 'v1.1.0-alpha.0',
266
- name: 'Release 1.1.0-alpha.0',
267
- body: `Notes for ${pkgName} [v1.1.0-alpha.0]: ${sha}`,
268
- prerelease: true
269
- }
233
+ assert(gitArgs.includes(`git tag --annotate --message Release 1.0.1 ${pkgName}-${branchName}-1.0.1`));
234
+ assert(
235
+ log.log.mock.calls[1].arguments[0].endsWith(`/${owner}/${project}/releases/tag/${pkgName}-${branchName}-1.0.1`)
236
+ );
270
237
  });
271
- interceptGitHubAsset({ owner, project, body: 'lineline' });
272
-
273
- interceptGitLabUser({ owner });
274
- interceptGitLabCollaborator({ owner, project });
275
- interceptGitLabAsset({ owner, project });
276
- interceptGitLabPublish({
277
- owner,
278
- project,
279
- body: {
280
- name: 'Release 1.1.0-alpha.0',
281
- ref,
282
- tag_name: 'v1.1.0-alpha.0',
283
- tag_message: `${owner} ${owner}/${project} ${project}`,
284
- description: `Notes for ${pkgName}: ${sha}`,
285
- assets: {
286
- links: [
287
- {
288
- name: 'file',
289
- url: `${url}/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file`
290
- }
291
- ]
238
+
239
+ test('should release all the things (pre-release, github, gitlab)', async t => {
240
+ const project = path.basename(bare);
241
+ const pkgName = path.basename(target);
242
+ const owner = path.basename(path.dirname(bare));
243
+ const url = `https://gitlab.com/${owner}/${project}`;
244
+ gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
245
+ childProcess.execSync('git tag v1.0.0', execOpts);
246
+ const sha = gitAdd('line', 'file', 'More file');
247
+ childProcess.execSync('git push --follow-tags', execOpts);
248
+ const git = factory(Git);
249
+ const ref = (await git.getBranchName()) ?? 'HEAD';
250
+
251
+ interceptGitHubAuthentication(github);
252
+ interceptGitHubCollaborator(github, { owner, project });
253
+ interceptGitHubAsset(assets, { owner, project, body: 'lineline' });
254
+ interceptGitHubCreate(github, {
255
+ owner,
256
+ project,
257
+ body: {
258
+ tag_name: 'v1.1.0-alpha.0',
259
+ name: 'Release 1.1.0-alpha.0',
260
+ body: `Notes for ${pkgName} [v1.1.0-alpha.0]: ${sha}`,
261
+ prerelease: true
292
262
  }
293
- }
294
- });
263
+ });
295
264
 
296
- const container = getContainer({
297
- increment: 'minor',
298
- preRelease: 'alpha',
299
- git: {
300
- changelog: 'git log --pretty=format:%h ${latestTag}...HEAD',
301
- commitMessage: 'Release ${version} for ${name} (from ${latestVersion})',
302
- tagAnnotation: '${repo.owner} ${repo.repository} ${repo.project}'
303
- },
304
- github: {
305
- release: true,
306
- pushRepo: `https://github.com/${owner}/${project}`,
307
- releaseNotes: 'echo Notes for ${name} [v${version}]: ${changelog}',
308
- assets: ['file']
309
- },
310
- gitlab: {
311
- release: true,
312
- pushRepo: url,
313
- releaseNotes: 'echo Notes for ${name}: ${changelog}',
314
- assets: ['file']
315
- },
316
- npm: { name: pkgName }
317
- });
265
+ interceptGitLabUser(gitlab, { owner });
266
+ interceptGitLabCollaborator(gitlab, { owner, project });
267
+ interceptGitLabAsset(gitlab, { owner, project });
268
+ interceptGitLabPublish(gitlab, {
269
+ owner,
270
+ project,
271
+ body: {
272
+ name: 'Release 1.1.0-alpha.0',
273
+ ref,
274
+ tag_name: 'v1.1.0-alpha.0',
275
+ tag_message: `${owner} ${owner}/${project} ${project}`,
276
+ description: `Notes for ${pkgName}: ${sha}`,
277
+ assets: {
278
+ links: [
279
+ {
280
+ name: 'file',
281
+ url: `${url}/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file`
282
+ }
283
+ ]
284
+ }
285
+ }
286
+ });
318
287
 
319
- const exec = sinon.spy(container.shell, 'exec');
288
+ const container = getContainer({
289
+ increment: 'minor',
290
+ preRelease: 'alpha',
291
+ git: {
292
+ changelog: 'git log --pretty=format:%h ${latestTag}...HEAD',
293
+ commitMessage: 'Release ${version} for ${name} (from ${latestVersion})',
294
+ tagAnnotation: '${repo.owner} ${repo.repository} ${repo.project}'
295
+ },
296
+ github: {
297
+ release: true,
298
+ pushRepo: `https://github.com/${owner}/${project}`,
299
+ releaseNotes: 'echo Notes for ${name} [v${version}]: ${changelog}',
300
+ assets: ['file']
301
+ },
302
+ gitlab: {
303
+ release: true,
304
+ pushRepo: url,
305
+ releaseNotes: 'echo Notes for ${name}: ${changelog}',
306
+ assets: ['file']
307
+ },
308
+ npm: { name: pkgName }
309
+ });
320
310
 
321
- await runTasks({}, container);
311
+ const exec = t.mock.method(container.shell, 'exec');
322
312
 
323
- const npmArgs = getArgs(container.shell.exec.args, 'npm');
324
- t.deepEqual(npmArgs, [
325
- 'npm ping',
326
- 'npm whoami',
327
- `npm show ${pkgName}@latest version`,
328
- 'npm --version',
329
- `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
330
- 'npm version 1.1.0-alpha.0 --no-git-tag-version',
331
- 'npm publish . --tag alpha'
332
- ]);
313
+ process.env['GITLAB_TOKEN'] = '123';
333
314
 
334
- const commitMessage = childProcess.execSync('git log --oneline --format=%B -n 1 HEAD', {
335
- encoding: 'utf-8'
336
- });
337
- t.is(commitMessage.trim(), `Release 1.1.0-alpha.0 for ${pkgName} (from 1.0.0)`);
315
+ await runTasks({}, container);
338
316
 
339
- const tagName = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
340
- t.is(tagName.trim(), 'v1.1.0-alpha.0');
317
+ const npmArgs = getArgs(exec, 'npm');
341
318
 
342
- const tagAnnotation = childProcess.execSync('git for-each-ref refs/tags/v1.1.0-alpha.0 --format="%(contents)"', {
343
- encoding: 'utf-8'
344
- });
345
- t.is(tagAnnotation.trim(), `${owner} ${owner}/${project} ${project}`);
319
+ assert.deepEqual(npmArgs, [
320
+ 'npm ping',
321
+ 'npm whoami',
322
+ `npm show ${pkgName}@latest version`,
323
+ 'npm --version',
324
+ `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
325
+ 'npm version 1.1.0-alpha.0 --no-git-tag-version',
326
+ 'npm publish . --tag alpha'
327
+ ]);
346
328
 
347
- t.true(log.obtrusive.firstCall.args[0].endsWith(`release ${pkgName} (1.0.0...1.1.0-alpha.0)`));
348
- t.true(log.log.firstCall.args[0].endsWith(`https://www.npmjs.com/package/${pkgName}`));
349
- t.true(log.log.secondCall.args[0].endsWith(`https://github.com/${owner}/${project}/releases/tag/v1.1.0-alpha.0`));
350
- t.true(log.log.thirdCall.args[0].endsWith(`${project}/-/releases/v1.1.0-alpha.0`));
351
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
329
+ const commitMessage = childProcess.execSync('git log --oneline --format=%B -n 1 HEAD', {
330
+ encoding: 'utf-8'
331
+ });
332
+ assert.equal(commitMessage.trim(), `Release 1.1.0-alpha.0 for ${pkgName} (from 1.0.0)`);
352
333
 
353
- exec.restore();
354
- });
334
+ const tagName = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
335
+ assert.equal(tagName.trim(), 'v1.1.0-alpha.0');
355
336
 
356
- test.serial('should publish pre-release without pre-id with different npm.tag', async t => {
357
- const { target } = t.context;
358
- const pkgName = path.basename(target);
359
- gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
360
- childProcess.execSync('git tag v1.0.0', execOpts);
361
-
362
- const container = getContainer({ increment: 'major', preRelease: true, npm: { name: pkgName, tag: 'next' } });
363
- const exec = sinon.spy(container.shell, 'exec');
364
-
365
- await runTasks({}, container);
366
-
367
- const npmArgs = getArgs(container.shell.exec.args, 'npm');
368
- t.deepEqual(npmArgs, [
369
- 'npm ping',
370
- 'npm whoami',
371
- `npm show ${pkgName}@latest version`,
372
- 'npm --version',
373
- `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
374
- 'npm version 2.0.0-0 --no-git-tag-version',
375
- 'npm publish . --tag next'
376
- ]);
377
-
378
- const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
379
- t.is(stdout.trim(), 'v2.0.0-0');
380
- t.true(log.obtrusive.firstCall.args[0].endsWith(`release ${pkgName} (1.0.0...2.0.0-0)`));
381
- t.true(log.log.firstCall.args[0].endsWith(`https://www.npmjs.com/package/${pkgName}`));
382
- t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
383
-
384
- exec.restore();
385
- });
337
+ const tagAnnotation = childProcess.execSync('git for-each-ref refs/tags/v1.1.0-alpha.0 --format="%(contents)"', {
338
+ encoding: 'utf-8'
339
+ });
340
+ assert.equal(tagAnnotation.trim(), `${owner} ${owner}/${project} ${project}`);
386
341
 
387
- test.serial('should handle private package correctly, bump lockfile', async t => {
388
- const { target } = t.context;
389
- const pkgName = path.basename(target);
390
- gitAdd(`{"name":"${pkgName}","version":"1.0.0","private":true}`, 'package.json', 'Add package.json');
391
- gitAdd(`{"name":"${pkgName}","version":"1.0.0","private":true}`, 'package-lock.json', 'Add package-lock.json');
342
+ assert(log.obtrusive.mock.calls[0].arguments[0].endsWith(`release ${pkgName} (1.0.0...1.1.0-alpha.0)`));
343
+ assert(log.log.mock.calls[0].arguments[0].endsWith(`https://www.npmjs.com/package/${pkgName}`));
344
+ assert(log.log.mock.calls[1].arguments[0].endsWith(`/${owner}/${project}/releases/tag/v1.1.0-alpha.0`));
345
+ assert(log.log.mock.calls[2].arguments[0].endsWith(`/${project}/-/releases/v1.1.0-alpha.0`));
346
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
347
+ });
392
348
 
393
- const container = getContainer({ npm: { name: pkgName, private: true } });
394
- const exec = sinon.spy(container.shell, 'exec');
349
+ test('should publish pre-release without pre-id with different npm.tag', async t => {
350
+ const pkgName = path.basename(target);
351
+ gitAdd(`{"name":"${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
352
+ childProcess.execSync('git tag v1.0.0', execOpts);
395
353
 
396
- await runTasks({}, container);
354
+ const container = getContainer({ increment: 'major', preRelease: true, npm: { name: pkgName, tag: 'next' } });
355
+ const exec = t.mock.method(container.shell, 'exec');
397
356
 
398
- const npmArgs = getArgs(container.shell.exec.args, 'npm');
399
- t.deepEqual(npmArgs, ['npm version 1.0.1 --no-git-tag-version']);
400
- t.true(log.obtrusive.firstCall.args[0].endsWith(`release ${pkgName} (1.0.0...1.0.1)`));
401
- t.is(log.warn.length, 0);
402
- t.regex(log.log.firstCall.args[0], /Done \(in [0-9]+s\.\)/);
357
+ await runTasks({}, container);
403
358
 
404
- exec.restore();
405
- });
359
+ const npmArgs = getArgs(exec, 'npm');
360
+ assert.deepEqual(npmArgs, [
361
+ 'npm ping',
362
+ 'npm whoami',
363
+ `npm show ${pkgName}@latest version`,
364
+ 'npm --version',
365
+ `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
366
+ 'npm version 2.0.0-0 --no-git-tag-version',
367
+ 'npm publish . --tag next'
368
+ ]);
406
369
 
407
- test.serial('should initially publish non-private scoped npm package privately', async t => {
408
- const { target } = t.context;
409
- const pkgName = path.basename(target);
410
- gitAdd(`{"name":"@scope/${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
370
+ const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
371
+ assert.equal(stdout.trim(), 'v2.0.0-0');
372
+ assert(log.obtrusive.mock.calls[0].arguments[0].endsWith(`release ${pkgName} (1.0.0...2.0.0-0)`));
373
+ assert(log.log.mock.calls[0].arguments[0].endsWith(`https://www.npmjs.com/package/${pkgName}`));
374
+ assert.match(log.log.mock.calls.at(-1).arguments[0], /Done \(in [0-9]+s\.\)/);
375
+ });
411
376
 
412
- const container = getContainer({ npm: { name: pkgName } });
377
+ test('should handle private package correctly, bump lockfile', async t => {
378
+ const pkgName = path.basename(target);
379
+ gitAdd(`{"name":"${pkgName}","version":"1.0.0","private":true}`, 'package.json', 'Add package.json');
380
+ gitAdd(`{"name":"${pkgName}","version":"1.0.0","private":true}`, 'package-lock.json', 'Add package-lock.json');
413
381
 
414
- const exec = sinon.stub(container.shell, 'exec').callThrough();
415
- exec.withArgs(`npm show @scope/${pkgName}@latest version`).rejects();
382
+ const container = getContainer({ npm: { name: pkgName, private: true } });
383
+ const exec = t.mock.method(container.shell, 'exec');
416
384
 
417
- await runTasks({}, container);
385
+ await runTasks({}, container);
418
386
 
419
- const npmArgs = getArgs(container.shell.exec.args, 'npm');
420
- t.is(npmArgs[6], 'npm publish . --tag latest');
421
- exec.restore();
422
- });
387
+ const npmArgs = getArgs(exec, 'npm');
388
+ assert.deepEqual(npmArgs, ['npm version 1.0.1 --no-git-tag-version']);
389
+ assert(log.obtrusive.mock.calls[0].arguments[0].endsWith(`release ${pkgName} (1.0.0...1.0.1)`));
390
+ assert.equal(log.warn.length, 0);
391
+ assert.match(log.log.mock.calls[0].arguments[0], /Done \(in [0-9]+s\.\)/);
392
+ });
423
393
 
424
- test.serial('should use pkg.publishConfig.registry', async t => {
425
- const { target } = t.context;
426
- const pkgName = path.basename(target);
427
- const registry = 'https://my-registry.example.org';
394
+ test('should initially publish non-private scoped npm package privately', async t => {
395
+ const pkgName = path.basename(target);
396
+ gitAdd(`{"name":"@scope/${pkgName}","version":"1.0.0"}`, 'package.json', 'Add package.json');
428
397
 
429
- gitAdd(
430
- JSON.stringify({
431
- name: pkgName,
432
- version: '1.2.3',
433
- publishConfig: { registry }
434
- }),
435
- 'package.json',
436
- 'Add package.json'
437
- );
398
+ const container = getContainer({ npm: { name: pkgName } });
438
399
 
439
- const container = getContainer();
400
+ const original = container.shell.exec.bind(container.shell);
401
+ const exec = t.mock.method(container.shell, 'exec', (...args) => {
402
+ if (args[0] === `npm show @scope/${pkgName}@latest version`) return Promise.reject();
403
+ return original(...args);
404
+ });
440
405
 
441
- const exec = sinon.spy(container.shell, 'exec');
406
+ await runTasks({}, container);
442
407
 
443
- await runTasks({}, container);
408
+ const npmArgs = getArgs(exec, 'npm');
409
+ assert.equal(npmArgs[6], 'npm publish . --tag latest');
410
+ });
444
411
 
445
- const npmArgs = getArgs(exec.args, 'npm');
446
- t.is(npmArgs[0], `npm ping --registry ${registry}`);
447
- t.is(npmArgs[1], `npm whoami --registry ${registry}`);
448
- t.true(container.log.log.firstCall.args[0].endsWith(`${registry}/package/${pkgName}`));
412
+ test('should use pkg.publishConfig.registry', async t => {
413
+ const pkgName = path.basename(target);
414
+ const registry = 'https://my-registry.example.org';
449
415
 
450
- exec.restore();
451
- });
416
+ gitAdd(
417
+ JSON.stringify({
418
+ name: pkgName,
419
+ version: '1.2.3',
420
+ publishConfig: { registry }
421
+ }),
422
+ 'package.json',
423
+ 'Add package.json'
424
+ );
452
425
 
453
- test.serial('should propagate errors', async t => {
454
- const config = {
455
- hooks: {
456
- 'before:init': 'some-failing-command'
457
- }
458
- };
459
- const container = getContainer(config);
426
+ const container = getContainer();
460
427
 
461
- await t.throwsAsync(runTasks({}, container), { message: /some-failing-command/ });
428
+ const exec = t.mock.method(container.shell, 'exec');
462
429
 
463
- t.is(log.error.callCount, 1);
464
- });
430
+ await runTasks({}, container);
465
431
 
466
- test.serial('should use custom changelog command with context', async t => {
467
- const { bare } = t.context;
468
- const project = path.basename(bare);
469
- const owner = path.basename(path.dirname(bare));
470
- childProcess.execSync('git tag v1.0.0', execOpts);
471
- gitAdd('line', 'file', 'More file');
472
-
473
- interceptGitHubAuthentication();
474
- interceptGitHubCollaborator({ owner, project });
475
- interceptGitHubCreate({
476
- owner,
477
- project,
478
- body: {
479
- tag_name: 'v1.1.0',
480
- name: 'Release 1.1.0',
481
- body: 'custom-changelog-generator --from=v1.0.0 --to=v1.1.0',
482
- draft: false,
483
- prerelease: false
484
- }
432
+ const npmArgs = getArgs(exec, 'npm');
433
+ assert.equal(npmArgs[0], `npm ping --registry ${registry}`);
434
+ assert.equal(npmArgs[1], `npm whoami --registry ${registry}`);
435
+ assert(container.log.log.mock.calls[0].arguments[0].endsWith(`${registry}/package/${pkgName}`));
485
436
  });
486
437
 
487
- const container = getContainer({
488
- increment: 'minor',
489
- github: {
490
- release: true,
491
- releaseNotes: 'echo custom-changelog-generator --from=${latestTag} --to=${tagName}',
492
- pushRepo: `https://github.com/${owner}/${project}`
493
- }
438
+ test('should propagate errors', async () => {
439
+ const config = {
440
+ hooks: {
441
+ 'before:init': 'some-failing-command'
442
+ }
443
+ };
444
+ const container = getContainer(config);
445
+
446
+ await assert.rejects(runTasks({}, container), { message: /some-failing-command/ });
447
+
448
+ assert.equal(log.error.mock.callCount(), 1);
494
449
  });
495
450
 
496
- const exec = sinon.spy(container.shell, 'execStringCommand');
451
+ test('should use custom changelog command with context', async t => {
452
+ const project = path.basename(bare);
453
+ const owner = path.basename(path.dirname(bare));
454
+ childProcess.execSync('git tag v1.0.0', execOpts);
455
+ gitAdd('line', 'file', 'More file');
456
+
457
+ interceptGitHubAuthentication(github);
458
+ interceptGitHubCollaborator(github, { owner, project });
459
+ interceptGitHubCreate(github, {
460
+ owner,
461
+ project,
462
+ body: {
463
+ tag_name: 'v1.1.0',
464
+ name: 'Release 1.1.0',
465
+ body: 'custom-changelog-generator --from=v1.0.0 --to=v1.1.0',
466
+ draft: false,
467
+ prerelease: false
468
+ }
469
+ });
470
+
471
+ const container = getContainer({
472
+ increment: 'minor',
473
+ github: {
474
+ release: true,
475
+ releaseNotes: 'echo custom-changelog-generator --from=${latestTag} --to=${tagName}',
476
+ pushRepo: `https://github.com/${owner}/${project}`
477
+ }
478
+ });
497
479
 
498
- await runTasks({}, container);
480
+ const exec = t.mock.method(container.shell, 'execStringCommand');
499
481
 
500
- const command = exec.args.find(([command]) => command.includes('custom-changelog-generator'));
482
+ await runTasks({}, container);
501
483
 
502
- t.is(command[0], 'echo custom-changelog-generator --from=v1.0.0 --to=v1.1.0');
484
+ const command = exec.mock.calls
485
+ .map(call => call.arguments)
486
+ .find(([command]) => command.includes('custom-changelog-generator'));
503
487
 
504
- exec.restore();
505
- });
488
+ assert.equal(command[0], 'echo custom-changelog-generator --from=v1.0.0 --to=v1.1.0');
489
+ });
506
490
 
507
- {
508
- test.serial('should run all hooks', async t => {
491
+ test('should run all hooks', async t => {
509
492
  gitAdd(`{"name":"hooked","version":"1.0.0","type":"module"}`, 'package.json', 'Add package.json');
510
493
  childProcess.execSync(`npm install ${rootDir}`, execOpts);
511
494
  const plugin = "import { Plugin } from 'release-it'; class MyPlugin extends Plugin {}; export default MyPlugin;";
@@ -525,13 +508,13 @@ test.serial('should use custom changelog command with context', async t => {
525
508
  git: { requireCleanWorkingDir: false },
526
509
  hooks
527
510
  });
528
- const exec = sinon.spy(container.shell, 'execFormattedCommand');
511
+ const exec = t.mock.method(container.shell, 'execFormattedCommand');
529
512
 
530
513
  await runTasks({}, container);
531
514
 
532
- const commands = _.flatten(exec.args).filter(arg => typeof arg === 'string' && arg.startsWith('echo'));
515
+ const commands = getArgs(exec, 'echo');
533
516
 
534
- t.deepEqual(commands, [
517
+ assert.deepEqual(commands, [
535
518
  'echo before:init',
536
519
  'echo before:my-plugin:init',
537
520
  'echo after:my-plugin:init',
@@ -593,7 +576,5 @@ test.serial('should use custom changelog command with context', async t => {
593
576
  'echo after:my-plugin:afterRelease',
594
577
  'echo after:afterRelease'
595
578
  ]);
596
-
597
- exec.restore();
598
579
  });
599
- }
580
+ });