release-it 19.0.5 → 19.1.0

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
@@ -315,6 +315,7 @@ Since v11, release-it can be extended in many, many ways. Here are some plugins:
315
315
  | [release-it-pnpm][16] | Add basic support for pnpm workspaces, integrates with [bumpp][50] and [changelogithub][51] |
316
316
  | [changesets-release-it-plugin][52] | Combine [Changesets][53] changelog management with release-it |
317
317
  | [release-it-gitea][54] | Gitea plugin to create Gitea releases and upload attachments |
318
+ | [release-it-beautiful-changelog][84] | Generate beautiful changelogs using conventional commits by [@unjs/changelogen][45] |
318
319
 
319
320
  Internally, release-it uses its own plugin architecture (for Git, GitHub, GitLab, npm).
320
321
 
@@ -459,3 +460,4 @@ Are you using release-it at work? Please consider [sponsoring me][14]!
459
460
  [81]: ./.github/CONTRIBUTING.md
460
461
  [82]: https://github.com/release-it/release-it/issues/new
461
462
  [83]: ./LICENSE
463
+ [84]: https://github.com/mohammadGh/release-it-beautiful-changelog
@@ -2,12 +2,12 @@ import path from 'node:path';
2
2
  import semver from 'semver';
3
3
  import urlJoin from 'url-join';
4
4
  import Plugin from '../Plugin.js';
5
- import { hasAccess, rejectAfter, parseVersion, readJSON, e, fixArgs } from '../../util.js';
5
+ import { hasAccess, rejectAfter, parseVersion, readJSON, e, fixArgs, getNpmEnv } from '../../util.js';
6
6
  import prompts from './prompts.js';
7
7
 
8
8
  const docs = 'https://git.io/release-it-npm';
9
9
 
10
- const options = { write: false };
10
+ const getOptions = () => ({ write: false, env: getNpmEnv() });
11
11
 
12
12
  const MANIFEST_PATH = './package.json';
13
13
  const DEFAULT_TAG = 'latest';
@@ -84,8 +84,14 @@ class npm extends Plugin {
84
84
  if (!this.config.isIncrement) return false;
85
85
 
86
86
  const { versionArgs, allowSameVersion } = this.options;
87
- const args = [version, '--no-git-tag-version', allowSameVersion && '--allow-same-version', ...fixArgs(versionArgs)];
88
- const task = () => this.exec(`npm version ${args.filter(Boolean).join(' ')}`);
87
+ const args = [
88
+ version,
89
+ '--no-git-tag-version',
90
+ '--workspaces=false',
91
+ allowSameVersion && '--allow-same-version',
92
+ ...fixArgs(versionArgs)
93
+ ];
94
+ const task = () => this.exec(`npm version ${args.filter(Boolean).join(' ')}`, { options: getOptions() });
89
95
  return this.spinner.show({ task, label: 'npm version' });
90
96
  }
91
97
 
@@ -101,7 +107,7 @@ class npm extends Plugin {
101
107
  isRegistryUp() {
102
108
  const registry = this.getRegistry();
103
109
  const registryArg = registry ? ` --registry ${registry}` : '';
104
- return this.exec(`npm ping${registryArg}`, { options }).then(
110
+ return this.exec(`npm ping${registryArg}`, { options: getOptions() }).then(
105
111
  () => true,
106
112
  err => {
107
113
  if (/code E40[04]|404.*(ping not found|No content for path)/.test(err)) {
@@ -116,7 +122,7 @@ class npm extends Plugin {
116
122
  isAuthenticated() {
117
123
  const registry = this.getRegistry();
118
124
  const registryArg = registry ? ` --registry ${registry}` : '';
119
- return this.exec(`npm whoami${registryArg}`, { options }).then(
125
+ return this.exec(`npm whoami${registryArg}`, { options: getOptions() }).then(
120
126
  output => {
121
127
  const username = output ? output.trim() : null;
122
128
  this.setContext({ username });
@@ -142,7 +148,7 @@ class npm extends Plugin {
142
148
  if (username === null) return false;
143
149
 
144
150
  try {
145
- let npmVersion = await this.exec('npm --version', { options });
151
+ let npmVersion = await this.exec('npm --version', { options: getOptions() });
146
152
 
147
153
  let accessCommand;
148
154
  if (semver.gt(npmVersion, '9.0.0')) {
@@ -151,7 +157,7 @@ class npm extends Plugin {
151
157
  accessCommand = 'npm access ls-collaborators';
152
158
  }
153
159
 
154
- const output = await this.exec(`${accessCommand} ${name}${registryArg}`, { options });
160
+ const output = await this.exec(`${accessCommand} ${name}${registryArg}`, { options: getOptions() });
155
161
 
156
162
  try {
157
163
  const collaborators = JSON.parse(output);
@@ -178,11 +184,11 @@ class npm extends Plugin {
178
184
  const name = this.getName();
179
185
  const latestVersion = this.getLatestVersion();
180
186
  const tag = await this.resolveTag(latestVersion);
181
- return this.exec(`npm show ${name}@${tag} version${registryArg}`, { options }).catch(() => null);
187
+ return this.exec(`npm show ${name}@${tag} version${registryArg}`, { options: getOptions() }).catch(() => null);
182
188
  }
183
189
 
184
190
  getRegistryPreReleaseTags() {
185
- return this.exec(`npm view ${this.getName()} dist-tags --json`, { options }).then(
191
+ return this.exec(`npm view ${this.getName()} dist-tags --json`, { options: getOptions() }).then(
186
192
  output => {
187
193
  try {
188
194
  const tags = JSON.parse(output);
@@ -242,7 +248,7 @@ class npm extends Plugin {
242
248
  async publish({ otp = this.options.otp, otpCallback } = {}) {
243
249
  const { publishPath = '.', publishArgs } = this.options;
244
250
  const { private: isPrivate, tag = DEFAULT_TAG } = this.getContext();
245
- const otpArg = otp ? `--otp ${otp}` : '';
251
+ const otpArgs = otp ? ['--otp', otp] : [];
246
252
  const dryRunArg = this.config.isDryRun ? '--dry-run' : '';
247
253
  const registry = this.getRegistry();
248
254
  const registryArg = registry ? `--registry ${registry}` : '';
@@ -250,8 +256,18 @@ class npm extends Plugin {
250
256
  this.log.warn('Skip publish: package is private.');
251
257
  return false;
252
258
  }
253
- const args = [publishPath, `--tag ${tag}`, otpArg, dryRunArg, registryArg, ...fixArgs(publishArgs)].filter(Boolean);
254
- return this.exec(`npm publish ${args.join(' ')}`, { options })
259
+ const args = [
260
+ publishPath,
261
+ '--tag',
262
+ tag,
263
+ '--workspaces=false',
264
+ ...otpArgs,
265
+ dryRunArg,
266
+ registryArg,
267
+ ...fixArgs(publishArgs)
268
+ ].filter(Boolean);
269
+ const isInteractive = !this.config.isCI;
270
+ return this.exec(['npm', 'publish', ...args], { options: { ...getOptions(), interactive: isInteractive } })
255
271
  .then(() => {
256
272
  this.setContext({ isReleased: true });
257
273
  })
package/lib/shell.js CHANGED
@@ -52,7 +52,8 @@ class Shell {
52
52
 
53
53
  execStringCommand(command, options, { isExternal }) {
54
54
  return new Promise((resolve, reject) => {
55
- const proc = exec(command, (err, stdout, stderr) => {
55
+ const execOptions = options.env ? { env: options.env } : {};
56
+ const proc = exec(command, execOptions, (err, stdout, stderr) => {
56
57
  stdout = stdout.toString().trimEnd();
57
58
  const code = !err ? 0 : err === 'undefined' ? 1 : err.code;
58
59
  debug({ command, options, code, stdout, stderr });
@@ -69,35 +70,41 @@ class Shell {
69
70
 
70
71
  async execWithArguments(command, options = {}, { isExternal } = {}) {
71
72
  const [program, ...programArgs] = command;
73
+ const isInteractive = options.interactive === true;
72
74
 
73
75
  try {
74
76
  return await new Promise((resolve, reject) => {
75
- const proc = spawn(program, programArgs, {
76
- // we want to capture all output from the process so the extra 2 pipe
77
- stdio: ['inherit', 'pipe', 'pipe'],
77
+ const spawnOptions = {
78
+ stdio: isInteractive ? 'inherit' : ['inherit', 'pipe', 'pipe'],
79
+ env: options.env,
78
80
  ...options
79
- });
81
+ };
82
+ delete spawnOptions.interactive;
83
+
84
+ const proc = spawn(program, programArgs, spawnOptions);
80
85
 
81
86
  let stdout = '';
82
87
  let stderr = '';
83
88
 
84
- proc.stdout.on('data', data => {
85
- stdout += data.toString();
86
- });
89
+ if (!isInteractive) {
90
+ proc.stdout.on('data', data => {
91
+ stdout += data.toString();
92
+ });
87
93
 
88
- proc.stderr.on('data', data => {
89
- stderr += data.toString();
90
- });
94
+ proc.stderr.on('data', data => {
95
+ stderr += data.toString();
96
+ });
97
+ }
91
98
 
92
99
  proc.on('close', code => {
93
100
  stdout = stdout === '""' ? '' : stdout;
94
- this.log.verbose(stdout, { isExternal });
101
+ if (!isInteractive) this.log.verbose(stdout, { isExternal });
95
102
  debug({ command, options, stdout, stderr });
96
103
 
97
104
  if (code === 0) {
98
105
  resolve((stdout || stderr).trim());
99
106
  } else {
100
- if (stdout) {
107
+ if (stdout && !isInteractive) {
101
108
  this.log.log(`\n${stdout}`);
102
109
  }
103
110
  debug({ code, command, options, stdout, stderr });
package/lib/util.js CHANGED
@@ -159,6 +159,19 @@ export const touch = (path, callback) => {
159
159
 
160
160
  export const fixArgs = args => (args ? (typeof args === 'string' ? args.split(' ') : args) : []);
161
161
 
162
+ // Remove npm_config_* variables set by others (e.g. pnpm) that npm warns about
163
+ export const getNpmEnv = () => {
164
+ const env = { ...process.env };
165
+ const removeVars = new Set([
166
+ 'npm_config_npm_globalconfig',
167
+ 'npm_config_verify_deps_before_run',
168
+ 'npm_config_overrides',
169
+ 'npm_config__jsr_registry'
170
+ ]);
171
+ for (const key of Object.keys(env)) if (removeVars.has(key.toLowerCase())) delete env[key];
172
+ return env;
173
+ };
174
+
162
175
  export const upperFirst = string => {
163
176
  return string ? string.charAt(0).toUpperCase() + string.slice(1) : '';
164
177
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-it",
3
- "version": "19.0.5",
3
+ "version": "19.1.0",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing-related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -82,7 +82,7 @@
82
82
  "@octokit/rest": "22.0.0",
83
83
  "@phun-ky/typeof": "2.0.3",
84
84
  "async-retry": "1.3.3",
85
- "c12": "3.3.0",
85
+ "c12": "3.3.1",
86
86
  "ci-info": "^4.3.0",
87
87
  "eta": "4.0.1",
88
88
  "git-url-parse": "16.1.0",
package/test/npm.js CHANGED
@@ -155,7 +155,14 @@ describe('npm', async () => {
155
155
  exec.mock.mockImplementationOnce(() => Promise.reject(new Error(whoamiError)), 1);
156
156
  exec.mock.mockImplementationOnce(() => Promise.reject(new Error(accessError)), 2);
157
157
  await runTasks(npmClient);
158
- assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
158
+ assert.deepEqual(exec.mock.calls.at(-1).arguments[0], [
159
+ 'npm',
160
+ 'publish',
161
+ '.',
162
+ '--tag',
163
+ 'latest',
164
+ '--workspaces=false'
165
+ ]);
159
166
  });
160
167
 
161
168
  test('should not throw if npm returns 400 for unsupported ping/whoami/access', async t => {
@@ -168,7 +175,14 @@ describe('npm', async () => {
168
175
  exec.mock.mockImplementationOnce(() => Promise.reject(new Error(whoamiError)), 1);
169
176
  exec.mock.mockImplementationOnce(() => Promise.reject(new Error(accessError)), 2);
170
177
  await runTasks(npmClient);
171
- assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
178
+ assert.deepEqual(exec.mock.calls.at(-1).arguments[0], [
179
+ 'npm',
180
+ 'publish',
181
+ '.',
182
+ '--tag',
183
+ 'latest',
184
+ '--workspaces=false'
185
+ ]);
172
186
  });
173
187
 
174
188
  test('should throw if user is not authenticated', async t => {
@@ -238,9 +252,27 @@ describe('npm', async () => {
238
252
  });
239
253
 
240
254
  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');
255
+ assert.deepEqual(exec.mock.calls[0].arguments[0], ['npm', 'publish', '.', '--tag', 'latest', '--workspaces=false']);
256
+ assert.deepEqual(exec.mock.calls[1].arguments[0], [
257
+ 'npm',
258
+ 'publish',
259
+ '.',
260
+ '--tag',
261
+ 'latest',
262
+ '--workspaces=false',
263
+ '--otp',
264
+ '123'
265
+ ]);
266
+ assert.deepEqual(exec.mock.calls[2].arguments[0], [
267
+ 'npm',
268
+ 'publish',
269
+ '.',
270
+ '--tag',
271
+ 'latest',
272
+ '--workspaces=false',
273
+ '--otp',
274
+ '123456'
275
+ ]);
244
276
 
245
277
  assert.equal(npmClient.log.warn.mock.callCount(), 1);
246
278
  assert.equal(npmClient.log.warn.mock.calls[0].arguments[0], 'The provided OTP is incorrect or has expired.');
@@ -255,7 +287,14 @@ describe('npm', async () => {
255
287
  return Promise.resolve();
256
288
  });
257
289
  await runTasks(npmClient);
258
- assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
290
+ assert.deepEqual(exec.mock.calls.at(-1).arguments[0], [
291
+ 'npm',
292
+ 'publish',
293
+ '.',
294
+ '--tag',
295
+ 'latest',
296
+ '--workspaces=false'
297
+ ]);
259
298
  });
260
299
 
261
300
  test('should use extra publish arguments', async t => {
@@ -263,10 +302,15 @@ describe('npm', async () => {
263
302
  const npmClient = await factory(npm, { options });
264
303
  const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
265
304
  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
- );
305
+ assert.deepEqual(exec.mock.calls.at(-1).arguments[0], [
306
+ 'npm',
307
+ 'publish',
308
+ '.',
309
+ '--tag',
310
+ 'latest',
311
+ '--workspaces=false',
312
+ '--registry=http://my-internal-registry.local'
313
+ ]);
270
314
  });
271
315
 
272
316
  test('should skip checks', async () => {
@@ -306,8 +350,8 @@ describe('npm', async () => {
306
350
  'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
307
351
  'npm show @my-scope/my-pkg@latest version --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
308
352
  '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/'
353
+ 'npm version 1.0.1 --no-git-tag-version --workspaces=false',
354
+ 'npm publish . --tag latest --workspaces=false --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
311
355
  ]);
312
356
  });
313
357
 
@@ -322,7 +366,7 @@ describe('npm', async () => {
322
366
  if (command === 'npm whoami') return Promise.resolve('john');
323
367
  const re = /npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg/;
324
368
  if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
325
- if (command === 'npm version 1.0.1 --no-git-tag-version')
369
+ if (command === 'npm version 1.0.1 --no-git-tag-version --workspaces=false')
326
370
  return Promise.reject('npm ERR! Version not changed, might want --allow-same-version');
327
371
  return Promise.resolve();
328
372
  });
@@ -334,7 +378,7 @@ describe('npm', async () => {
334
378
  'npm whoami',
335
379
  'npm show @my-scope/my-pkg@latest version',
336
380
  'npm --version',
337
- 'npm version 1.0.1 --no-git-tag-version'
381
+ 'npm version 1.0.1 --no-git-tag-version --workspaces=false'
338
382
  ]);
339
383
  });
340
384
 
@@ -5,16 +5,17 @@ const debug = util.debug('release-it:shell-stub');
5
5
 
6
6
  class ShellStub extends Shell {
7
7
  exec(command) {
8
- if (/^(npm (ping|publish|show)|git fetch)/.test(command)) {
9
- debug(command);
8
+ const cmd = Array.isArray(command) ? command.join(' ') : command;
9
+ if (/^(npm (ping|publish|show)|git fetch)/.test(cmd)) {
10
+ debug(cmd);
10
11
  return Promise.resolve();
11
12
  }
12
- if (/^npm whoami/.test(command)) {
13
- debug(command);
13
+ if (/^npm whoami/.test(cmd)) {
14
+ debug(cmd);
14
15
  return Promise.resolve('john');
15
16
  }
16
- if (/^npm access/.test(command)) {
17
- debug(command);
17
+ if (/^npm access/.test(cmd)) {
18
+ debug(cmd);
18
19
  return Promise.resolve(JSON.stringify({ john: ['write'] }));
19
20
  }
20
21
  return super.exec(...arguments);
package/test/tasks.js CHANGED
@@ -140,7 +140,7 @@ describe('tasks', () => {
140
140
  const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
141
141
  assert.equal(stdout.trim(), '1.0.0');
142
142
  const npmArgs = getArgs(exec, 'npm');
143
- assert.equal(npmArgs[5], 'npm version 1.3.0 --no-git-tag-version');
143
+ assert.equal(npmArgs[5], 'npm version 1.3.0 --no-git-tag-version --workspaces=false');
144
144
  });
145
145
 
146
146
  test('should ignore version in pkg.version and use git tag instead', async () => {
@@ -187,8 +187,8 @@ describe('tasks', () => {
187
187
  `npm show ${pkgName}@latest version`,
188
188
  'npm --version',
189
189
  `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
190
- 'npm version 1.0.1 --no-git-tag-version',
191
- 'npm publish . --tag latest'
190
+ 'npm version 1.0.1 --no-git-tag-version --workspaces=false',
191
+ 'npm publish . --tag latest --workspaces=false'
192
192
  ]);
193
193
 
194
194
  assert(log.obtrusive.mock.calls[0].arguments[0].endsWith(`release ${pkgName} (1.0.0...1.0.1)`));
@@ -320,8 +320,8 @@ describe('tasks', () => {
320
320
  `npm show ${pkgName}@latest version`,
321
321
  'npm --version',
322
322
  `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
323
- 'npm version 1.1.0-alpha.0 --no-git-tag-version',
324
- 'npm publish . --tag alpha'
323
+ 'npm version 1.1.0-alpha.0 --no-git-tag-version --workspaces=false',
324
+ 'npm publish . --tag alpha --workspaces=false'
325
325
  ]);
326
326
 
327
327
  const commitMessage = childProcess.execSync('git log --oneline --format=%B -n 1 HEAD', {
@@ -361,8 +361,8 @@ describe('tasks', () => {
361
361
  `npm show ${pkgName}@latest version`,
362
362
  'npm --version',
363
363
  `npm access ${npmMajorVersion >= 9 ? 'list collaborators --json' : 'ls-collaborators'} ${pkgName}`,
364
- 'npm version 2.0.0-0 --no-git-tag-version',
365
- 'npm publish . --tag next'
364
+ 'npm version 2.0.0-0 --no-git-tag-version --workspaces=false',
365
+ 'npm publish . --tag next --workspaces=false'
366
366
  ]);
367
367
 
368
368
  const stdout = childProcess.execSync('git describe --tags --match=* --abbrev=0', { encoding: 'utf-8' });
@@ -383,7 +383,7 @@ describe('tasks', () => {
383
383
  await runTasks({}, container);
384
384
 
385
385
  const npmArgs = getArgs(exec, 'npm');
386
- assert.deepEqual(npmArgs, ['npm version 1.0.1 --no-git-tag-version']);
386
+ assert.deepEqual(npmArgs, ['npm version 1.0.1 --no-git-tag-version --workspaces=false']);
387
387
  assert(log.obtrusive.mock.calls[0].arguments[0].endsWith(`release ${pkgName} (1.0.0...1.0.1)`));
388
388
  assert.equal(log.warn.length, 0);
389
389
  assert.match(log.log.mock.calls[0].arguments[0], /Done \(in [0-9]+s\.\)/);
@@ -404,7 +404,7 @@ describe('tasks', () => {
404
404
  await runTasks({}, container);
405
405
 
406
406
  const npmArgs = getArgs(exec, 'npm');
407
- assert.equal(npmArgs[6], 'npm publish . --tag latest');
407
+ assert.equal(npmArgs[6], 'npm publish . --tag latest --workspaces=false');
408
408
  });
409
409
 
410
410
  test('should use pkg.publishConfig.registry', async t => {