release-it 15.5.1 → 15.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -340,9 +340,8 @@ While mostly used as a CLI tool, release-it can be used as a dependency to integ
340
340
 
341
341
  ## Example projects using release-it
342
342
 
343
- - [antonmedv/fx](https://github.com/antonmedv/fx)
343
+ - [axios/axios](https://github.com/axios/axios)
344
344
  - [blockchain/blockchain-wallet-v4-frontend](https://github.com/blockchain/blockchain-wallet-v4-frontend)
345
- - [callstack/linaria](https://github.com/callstack/linaria)
346
345
  - [callstack/react-native-paper](https://github.com/callstack/react-native-paper)
347
346
  - [ember-cli/ember-cli](https://github.com/ember-cli/ember-cli)
348
347
  - [js-cookie/js-cookie](https://github.com/js-cookie/js-cookie)
@@ -11,6 +11,7 @@
11
11
  "commitMessage": "Release ${version}",
12
12
  "commitArgs": [],
13
13
  "tag": true,
14
+ "tagExclude": null,
14
15
  "tagName": null,
15
16
  "tagMatch": null,
16
17
  "tagAnnotation": "Release ${version}",
@@ -93,7 +93,8 @@ class GitBase extends Plugin {
93
93
  getLatestTagName() {
94
94
  const context = Object.assign({}, this.config.getContext(), { version: '*' });
95
95
  const match = format(this.options.tagMatch || this.options.tagName || '${version}', context);
96
- return this.exec(`git describe --tags --match=${match} --abbrev=0`, { options }).then(
96
+ const exclude = this.options.tagExclude ? ` --exclude=${format(this.options.tagExclude, context)}` : '';
97
+ return this.exec(`git describe --tags --match=${match} --abbrev=0${exclude}`, { options }).then(
97
98
  stdout => stdout || null,
98
99
  () => null
99
100
  );
@@ -12,7 +12,7 @@ class GitRelease extends GitBase {
12
12
  getInitialOptions(options) {
13
13
  const baseOptions = super.getInitialOptions(...arguments);
14
14
  const git = options.git || defaultConfig.git;
15
- const gitOptions = _.pick(git, ['tagName', 'tagMatch', 'pushRepo', 'changelog']);
15
+ const gitOptions = _.pick(git, ['tagExclude', 'tagName', 'tagMatch', 'pushRepo', 'changelog']);
16
16
  return _.defaults(baseOptions, gitOptions);
17
17
  }
18
18
 
@@ -2,7 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import got from 'got';
4
4
  import { globby } from 'globby';
5
- import FormData from 'form-data';
5
+ import { FormData, fileFromSync } from 'node-fetch';
6
6
  import allSettled from 'promise.allsettled';
7
7
  import _ from 'lodash';
8
8
  import Release from '../GitRelease.js';
@@ -232,7 +232,7 @@ class GitLab extends Release {
232
232
  const endpoint = `projects/${id}/uploads`;
233
233
 
234
234
  const body = new FormData();
235
- body.append('file', fs.createReadStream(filePath));
235
+ body.set('file', fileFromSync(filePath));
236
236
  const options = { body };
237
237
 
238
238
  try {
@@ -132,34 +132,43 @@ class npm extends Plugin {
132
132
  );
133
133
  }
134
134
 
135
- isCollaborator() {
135
+ async isCollaborator() {
136
136
  const registry = this.getRegistry();
137
137
  const registryArg = registry ? ` --registry ${registry}` : '';
138
138
  const name = this.getName();
139
139
  const { username } = this.getContext();
140
140
  if (username === undefined) return true;
141
141
  if (username === null) return false;
142
- return this.exec(`npm access ls-collaborators ${name}${registryArg}`, { options }).then(
143
- output => {
144
- try {
145
- const collaborators = JSON.parse(output);
146
- const permissions = collaborators[username];
147
- return permissions && permissions.includes('write');
148
- } catch (err) {
149
- this.debug(err);
150
- return false;
151
- }
152
- },
153
- err => {
142
+
143
+ try {
144
+ let npmVersion = await this.exec('npm --version', { options });
145
+
146
+ let accessCommand;
147
+ if (semver.gt(npmVersion, '9.0.0')) {
148
+ accessCommand = 'npm access list collaborators --json';
149
+ } else {
150
+ accessCommand = 'npm access ls-collaborators';
151
+ }
152
+
153
+ const output = await this.exec(`${accessCommand} ${name}${registryArg}`, { options });
154
+
155
+ try {
156
+ const collaborators = JSON.parse(output);
157
+ const permissions = collaborators[username];
158
+ return permissions && permissions.includes('write');
159
+ } catch (err) {
154
160
  this.debug(err);
155
- if (/code E400/.test(err)) {
156
- this.log.warn('Ignoring response from unsupported `npm access` command.');
157
- } else {
158
- this.log.warn(`Unable to verify if user ${username} is a collaborator for ${name}.`);
159
- }
160
- return true;
161
+ return false;
161
162
  }
162
- );
163
+ } catch (err) {
164
+ this.debug(err);
165
+ if (/code E400/.test(err)) {
166
+ this.log.warn('Ignoring response from unsupported `npm access` command.');
167
+ } else {
168
+ this.log.warn(`Unable to verify if user ${username} is a collaborator for ${name}.`);
169
+ }
170
+ return true;
171
+ }
163
172
  }
164
173
 
165
174
  async getLatestRegistryVersion() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-it",
3
- "version": "15.5.1",
3
+ "version": "15.6.1",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -60,14 +60,13 @@
60
60
  "license": "MIT",
61
61
  "dependencies": {
62
62
  "@iarna/toml": "2.2.5",
63
- "@octokit/rest": "19.0.5",
63
+ "@octokit/rest": "19.0.7",
64
64
  "async-retry": "1.3.3",
65
- "chalk": "5.1.2",
66
- "cosmiconfig": "8.0.0",
67
- "execa": "6.1.0",
68
- "form-data": "4.0.0",
65
+ "chalk": "5.2.0",
66
+ "cosmiconfig": "8.1.0",
67
+ "execa": "7.0.0",
69
68
  "git-url-parse": "13.1.0",
70
- "globby": "13.1.2",
69
+ "globby": "13.1.3",
71
70
  "got": "12.5.3",
72
71
  "inquirer": "9.1.4",
73
72
  "is-ci": "3.0.1",
@@ -75,9 +74,9 @@
75
74
  "mime-types": "2.1.35",
76
75
  "new-github-release-url": "2.0.0",
77
76
  "node-fetch": "3.3.0",
78
- "open": "8.4.0",
77
+ "open": "8.4.2",
79
78
  "ora": "6.1.2",
80
- "os-name": "5.0.1",
79
+ "os-name": "5.1.0",
81
80
  "promise.allsettled": "1.0.6",
82
81
  "proxy-agent": "5.0.0",
83
82
  "semver": "7.3.8",
@@ -88,19 +87,19 @@
88
87
  "yargs-parser": "21.1.1"
89
88
  },
90
89
  "devDependencies": {
91
- "@octokit/request-error": "3.0.2",
92
- "ava": "5.1.0",
93
- "eslint": "8.28.0",
94
- "eslint-config-prettier": "8.5.0",
95
- "eslint-plugin-ava": "13.2.0",
96
- "eslint-plugin-import": "2.26.0",
90
+ "@octokit/request-error": "3.0.3",
91
+ "ava": "5.2.0",
92
+ "eslint": "8.35.0",
93
+ "eslint-config-prettier": "8.6.0",
94
+ "eslint-plugin-ava": "14.0.0",
95
+ "eslint-plugin-import": "2.27.5",
97
96
  "eslint-plugin-prettier": "4.2.1",
98
97
  "mock-fs": "5.2.0",
99
98
  "mock-stdio": "1.0.3",
100
- "nock": "13.2.9",
99
+ "nock": "13.3.0",
101
100
  "nyc": "15.1.0",
102
- "prettier": "2.8.0",
103
- "sinon": "15.0.0",
101
+ "prettier": "2.8.4",
102
+ "sinon": "15.0.1",
104
103
  "strip-ansi": "7.0.1"
105
104
  },
106
105
  "engines": {
package/test/git.init.js CHANGED
@@ -167,6 +167,23 @@ test.serial('should get the latest tag based on tagMatch', async t => {
167
167
  t.is(gitClient.config.getContext('latestTag'), '21.04.3');
168
168
  });
169
169
 
170
+ test.serial('should get the latest tag based on tagExclude', async t => {
171
+ const shell = factory(Shell);
172
+ const gitClient = factory(Git, {
173
+ options: { git: { tagExclude: '*[-]*' } },
174
+ container: { shell }
175
+ });
176
+ sh.exec('git tag 1.0.0');
177
+ sh.exec('git commit --allow-empty -m "commit 1"');
178
+ sh.exec('git tag 1.0.1-rc.0');
179
+ sh.exec('git tag 1.0.1');
180
+ sh.exec('git commit --allow-empty -m "commit 2"');
181
+ sh.exec('git tag 1.1.0-rc.0');
182
+ sh.exec('git push --tags');
183
+ await gitClient.init();
184
+ t.is(gitClient.config.getContext('latestTag'), '1.0.1');
185
+ });
186
+
170
187
  test.serial('should generate correct changelog', async t => {
171
188
  const gitClient = factory(Git, { options: { git } });
172
189
  sh.exec('git tag 1.0.0');
package/test/npm.js CHANGED
@@ -108,7 +108,7 @@ test('should add registry to commands when specified', async t => {
108
108
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
109
109
  exec.withArgs('npm whoami --registry registry.example.org').resolves('john');
110
110
  exec
111
- .withArgs('npm access ls-collaborators release-it --registry registry.example.org')
111
+ .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it --registry registry.example.org/)
112
112
  .resolves(JSON.stringify({ john: ['write'] }));
113
113
  await runTasks(npmClient);
114
114
  t.is(exec.args[0][0], 'npm ping --registry registry.example.org');
@@ -121,7 +121,9 @@ test('should not throw when executing tasks', async t => {
121
121
  const npmClient = factory(npm);
122
122
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
123
123
  exec.withArgs('npm whoami').resolves('john');
124
- exec.withArgs('npm access ls-collaborators release-it').resolves(JSON.stringify({ john: ['write'] }));
124
+ exec
125
+ .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
126
+ .resolves(JSON.stringify({ john: ['write'] }));
125
127
  await t.notThrowsAsync(runTasks(npmClient));
126
128
  exec.restore();
127
129
  });
@@ -168,7 +170,9 @@ test('should not throw if npm returns 404 for unsupported ping', async t => {
168
170
  const pingError = 'npm ERR! <title>404 - No content for path /-/ping</title>';
169
171
  exec.withArgs('npm ping').rejects(new Error(pingError));
170
172
  exec.withArgs('npm whoami').resolves('john');
171
- exec.withArgs('npm access ls-collaborators release-it').resolves(JSON.stringify({ john: ['write'] }));
173
+ exec
174
+ .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
175
+ .resolves(JSON.stringify({ john: ['write'] }));
172
176
  await runTasks(npmClient);
173
177
  t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
174
178
  exec.restore();
@@ -182,11 +186,24 @@ test('should throw if user is not authenticated', async t => {
182
186
  exec.restore();
183
187
  });
184
188
 
185
- test('should throw if user is not a collaborator', async t => {
189
+ test('should throw if user is not a collaborator (v9)', async t => {
186
190
  const npmClient = factory(npm);
187
191
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
188
192
  exec.withArgs('npm whoami').resolves('ada');
189
- exec.withArgs('npm access ls-collaborators release-it').resolves(JSON.stringify({ john: ['write'] }));
193
+ exec.withArgs('npm --version').resolves('9.2.0');
194
+ exec.withArgs('npm access list collaborators --json release-it').resolves(JSON.stringify({ john: ['write'] }));
195
+ await t.throwsAsync(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
196
+ exec.restore();
197
+ });
198
+
199
+ test('should throw if user is not a collaborator (v8)', async t => {
200
+ const npmClient = factory(npm);
201
+ const exec = sinon.stub(npmClient.shell, 'exec').resolves();
202
+ exec.withArgs('npm whoami').resolves('ada');
203
+ exec.withArgs('npm --version').resolves('8.2.0');
204
+ exec
205
+ .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
206
+ .resolves(JSON.stringify({ john: ['write'] }));
190
207
  await t.throwsAsync(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
191
208
  exec.restore();
192
209
  });
@@ -196,7 +213,7 @@ test('should not throw if user is not a collaborator on a new package', async t
196
213
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
197
214
  exec.withArgs('npm whoami').resolves('ada');
198
215
  exec
199
- .withArgs('npm access ls-collaborators release-it')
216
+ .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
200
217
  .rejects(
201
218
  new Error(
202
219
  'npm ERR! code E404\nnpm ERR! 404 Not Found - GET https://registry.npmjs.org/-/package/release-it/collaborators?format=cli - File not found'
@@ -257,7 +274,9 @@ test('should publish', async t => {
257
274
  const npmClient = factory(npm);
258
275
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
259
276
  exec.withArgs('npm whoami').resolves('john');
260
- exec.withArgs('npm access ls-collaborators release-it').resolves(JSON.stringify({ john: ['write'] }));
277
+ exec
278
+ .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
279
+ .resolves(JSON.stringify({ john: ['write'] }));
261
280
  await runTasks(npmClient);
262
281
  t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
263
282
  exec.restore();
@@ -297,7 +316,7 @@ test('should publish to a different/scoped registry', async t => {
297
316
  .resolves('john');
298
317
  exec
299
318
  .withArgs(
300
- 'npm access ls-collaborators @my-scope/my-pkg --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
319
+ /npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg --registry https:\/\/gitlab\.com\/api\/v4\/projects\/my-scope%2Fmy-pkg\/packages\/npm\//
301
320
  )
302
321
  .resolves(JSON.stringify({ john: ['write'] }));
303
322
 
@@ -307,7 +326,7 @@ test('should publish to a different/scoped registry', async t => {
307
326
  'npm ping --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
308
327
  'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
309
328
  'npm show @my-scope/my-pkg@latest version --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
310
- 'npm access ls-collaborators @my-scope/my-pkg --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
329
+ 'npm --version',
311
330
  'npm version 1.0.1 --no-git-tag-version',
312
331
  'npm publish . --tag latest'
313
332
  ]);
@@ -326,7 +345,9 @@ test('should not publish when `npm version` fails', async t => {
326
345
  const npmClient = factory(npm, { options });
327
346
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
328
347
  exec.withArgs('npm whoami').resolves('john');
329
- exec.withArgs('npm access ls-collaborators @my-scope/my-pkg').resolves(JSON.stringify({ john: ['write'] }));
348
+ exec
349
+ .withArgs(/npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg/)
350
+ .resolves(JSON.stringify({ john: ['write'] }));
330
351
  exec
331
352
  .withArgs('npm version 1.0.1 --no-git-tag-version')
332
353
  .rejects('npm ERR! Version not changed, might want --allow-same-version');
@@ -341,7 +362,7 @@ test('should not publish when `npm version` fails', async t => {
341
362
  'npm ping',
342
363
  'npm whoami',
343
364
  'npm show @my-scope/my-pkg@latest version',
344
- 'npm access ls-collaborators @my-scope/my-pkg',
365
+ 'npm --version',
345
366
  'npm version 1.0.1 --no-git-tag-version'
346
367
  ]);
347
368
 
package/test/tasks.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import test from 'ava';
3
+ import semver from 'semver';
3
4
  import sh from 'shelljs';
4
5
  import _ from 'lodash';
5
6
  import sinon from 'sinon';
@@ -28,6 +29,8 @@ const noop = Promise.resolve();
28
29
 
29
30
  const sandbox = sinon.createSandbox();
30
31
 
32
+ const npmMajorVersion = semver.major(process.env.npm_config_user_agent.match(/npm\/([^ ]+)/)[1]);
33
+
31
34
  const testConfig = {
32
35
  ci: true,
33
36
  config: false
@@ -128,7 +131,7 @@ test.serial('should use pkg.version (in sub dir) w/o tagging repo', async t => {
128
131
  const { stdout } = sh.exec('git describe --tags --match=* --abbrev=0');
129
132
  t.is(stdout.trim(), '1.0.0');
130
133
  const npmArgs = getArgs(exec.args, 'npm');
131
- t.is(npmArgs[4], 'npm version 1.3.0 --no-git-tag-version');
134
+ t.is(npmArgs[5], 'npm version 1.3.0 --no-git-tag-version');
132
135
  exec.restore();
133
136
  });
134
137
 
@@ -174,7 +177,8 @@ test.serial('should release all the things (basic)', async t => {
174
177
  'npm ping',
175
178
  'npm whoami',
176
179
  `npm show ${pkgName}@latest version`,
177
- `npm access ls-collaborators ${pkgName}`,
180
+ 'npm --version',
181
+ `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
178
182
  'npm version 1.0.1 --no-git-tag-version',
179
183
  'npm publish . --tag latest'
180
184
  ]);
@@ -307,7 +311,8 @@ test.serial('should release all the things (pre-release, github, gitlab)', async
307
311
  'npm ping',
308
312
  'npm whoami',
309
313
  `npm show ${pkgName}@latest version`,
310
- `npm access ls-collaborators ${pkgName}`,
314
+ 'npm --version',
315
+ `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
311
316
  'npm version 1.1.0-alpha.0 --no-git-tag-version',
312
317
  'npm publish . --tag alpha'
313
318
  ]);
@@ -346,7 +351,8 @@ test.serial('should publish pre-release without pre-id with different npm.tag',
346
351
  'npm ping',
347
352
  'npm whoami',
348
353
  `npm show ${pkgName}@latest version`,
349
- `npm access ls-collaborators ${pkgName}`,
354
+ 'npm --version',
355
+ `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
350
356
  'npm version 2.0.0-0 --no-git-tag-version',
351
357
  'npm publish . --tag next'
352
358
  ]);
@@ -393,7 +399,7 @@ test.serial('should initially publish non-private scoped npm package privately',
393
399
  await runTasks({}, container);
394
400
 
395
401
  const npmArgs = getArgs(container.shell.exec.args, 'npm');
396
- t.is(npmArgs[5], 'npm publish . --tag latest');
402
+ t.is(npmArgs[6], 'npm publish . --tag latest');
397
403
  exec.restore();
398
404
  });
399
405