release-it 19.0.0-next.1 → 19.0.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cli.js CHANGED
@@ -1,11 +1,8 @@
1
1
  import { readJSON } from './util.js';
2
- import Log from './log.js';
3
2
  import runTasks from './index.js';
4
3
 
5
4
  const pkg = readJSON(new URL('../package.json', import.meta.url));
6
5
 
7
- const log = new Log();
8
-
9
6
  const helpText = `Release It! v${pkg.version}
10
7
 
11
8
  Usage: release-it <increment> [options]
@@ -27,10 +24,10 @@ const helpText = `Release It! v${pkg.version}
27
24
  For more details, please see https://github.com/release-it/release-it`;
28
25
 
29
26
  /** @internal */
30
- export const version = () => log.log(`v${pkg.version}`);
27
+ export const version = () => console.log(`v${pkg.version}`);
31
28
 
32
29
  /** @internal */
33
- export const help = () => log.log(helpText);
30
+ export const help = () => console.log(helpText);
34
31
 
35
32
  export default async options => {
36
33
  if (options.version) {
package/lib/config.js CHANGED
@@ -6,7 +6,7 @@ import defaultsDeep from '@nodeutils/defaults-deep';
6
6
  import { isObjectStrict } from '@phun-ky/typeof';
7
7
  import merge from 'lodash.merge';
8
8
  import get from 'lodash.get';
9
- import { readJSON, getSystemInfo } from './util.js';
9
+ import { e, getSystemInfo, readJSON } from './util.js';
10
10
 
11
11
  const debug = util.debug('release-it:config');
12
12
  const defaultConfig = readJSON(new URL('../config/release-it.json', import.meta.url));
@@ -38,10 +38,36 @@ const getLocalConfig = ({ file, dir = process.cwd() }) => {
38
38
  throw new Error(`Invalid configuration file at ${result.filepath}`);
39
39
  }
40
40
  debug({ cosmiconfig: result });
41
-
42
41
  return result && isObjectStrict(result.config) ? result.config : localConfig;
43
42
  };
44
43
 
44
+ const fetchConfigurationFromGitHub = async configuration => {
45
+ const docs = 'https://github.com/release-it/release-it/blob/main/docs/configuration.md';
46
+
47
+ const regex = /^github:([^/]+)\/([^#:]+)(?::([^#]+))?(?:#(.+))?$/;
48
+ const match = configuration.match(regex);
49
+
50
+ if (!match) {
51
+ throw e(`Invalid Extended Configuration from GitHub: ${configuration}`, docs);
52
+ }
53
+
54
+ const [, owner, repo, file = '.release-it.json', tag] = match;
55
+ const ref = tag ? `refs/tags/${tag}` : 'HEAD';
56
+ const url = new URL(`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${file}`);
57
+
58
+ const response = await fetch(url);
59
+
60
+ if (!response.ok) {
61
+ throw e(`Failed to fetch ${url}: ${response.statusText}`, docs);
62
+ }
63
+
64
+ return response.json();
65
+ };
66
+
67
+ const getRemoteConfiguration = async configuration => {
68
+ return fetchConfigurationFromGitHub(configuration);
69
+ };
70
+
45
71
  class Config {
46
72
  constructor(config = {}) {
47
73
  this.constructorConfig = config;
@@ -86,6 +112,10 @@ class Config {
86
112
  this.defaultConfig
87
113
  );
88
114
  }
115
+
116
+ mergeRemoteOptions(remoteConfiguration) {
117
+ return merge({}, this.options, remoteConfiguration);
118
+ }
89
119
  getContext(path) {
90
120
  const context = merge({}, this.options, this.contextOptions);
91
121
  return path ? get(context, path) : context;
@@ -141,4 +171,6 @@ class Config {
141
171
  }
142
172
  }
143
173
 
174
+ export { getRemoteConfiguration };
175
+
144
176
  export default Config;
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getPlugins } from './plugin/factory.js';
2
2
  import Logger from './log.js';
3
- import Config from './config.js';
3
+ import Config, { getRemoteConfiguration } from './config.js';
4
4
  import Shell from './shell.js';
5
5
  import Prompt from './prompt.js';
6
6
  import Spinner from './spinner.js';
@@ -13,6 +13,15 @@ const runTasks = async (opts, di) => {
13
13
  Object.assign(container, di);
14
14
  container.config = container.config || new Config(opts);
15
15
 
16
+ if (typeof container.config.options?.extends === 'string') {
17
+ /**
18
+ * If the configuration has an 'extends' property, fetch the remote configuration
19
+ * and merge it into the local configuration options.
20
+ */
21
+ const remoteConfiguration = await getRemoteConfiguration(container.config.options.extends);
22
+ container.config.options = container.config.mergeRemoteOptions(remoteConfiguration);
23
+ }
24
+
16
25
  const { config } = container;
17
26
  const { isCI, isVerbose, verbosityLevel, isDryRun, isChangelog, isReleaseVersion } = config;
18
27
 
@@ -62,8 +71,13 @@ const runTasks = async (opts, di) => {
62
71
  const changelog = await reduceUntil(plugins, plugin => plugin.getChangelog(latestVersion));
63
72
 
64
73
  if (isChangelog) {
65
- console.log(changelog);
66
- process.exit(0);
74
+ if (changelog) {
75
+ console.log(changelog);
76
+ process.exit(0);
77
+ } else {
78
+ log.warn('No changelog found');
79
+ process.exit(1);
80
+ }
67
81
  }
68
82
 
69
83
  const incrementBase = { latestVersion, increment, isPreRelease, preReleaseId, preReleaseBase };
@@ -95,13 +109,14 @@ const runTasks = async (opts, di) => {
95
109
  version = version || (await reduceUntil(plugins, plugin => plugin.getIncrementedVersion(incrementBase)));
96
110
  }
97
111
 
98
- if (!version) {
99
- log.obtrusive(`No new version to release`);
100
- }
101
-
102
112
  if (isReleaseVersion) {
103
- console.log(version);
104
- process.exit(0);
113
+ if (version) {
114
+ console.log(version);
115
+ process.exit(0);
116
+ } else {
117
+ log.warn(`No new version to release`);
118
+ process.exit(1);
119
+ }
105
120
  }
106
121
 
107
122
  if (version) {
@@ -125,6 +140,8 @@ const runTasks = async (opts, di) => {
125
140
  await runLifeCycleHook(plugin, hook);
126
141
  }
127
142
  }
143
+ } else {
144
+ log.obtrusive(`No new version to release`);
128
145
  }
129
146
 
130
147
  log.log(`🏁 Done (in ${Math.floor(process.uptime())}s.)`);
package/lib/log.js CHANGED
@@ -24,17 +24,17 @@ class Logger {
24
24
  }
25
25
 
26
26
  info(...args) {
27
- this.log(chalk.grey(...args));
27
+ console.error(chalk.grey(...args));
28
28
  }
29
29
 
30
30
  warn(...args) {
31
- this.log(chalk.yellow('WARNING'), ...args);
31
+ console.error(chalk.yellow('WARNING'), ...args);
32
32
  }
33
33
 
34
34
  verbose(...args) {
35
35
  const { isExternal } = isObjectLoose(args.at(-1)) ? args.at(-1) : {};
36
36
  if (this.shouldLog(isExternal)) {
37
- this.log(...args.filter(str => typeof str === 'string'));
37
+ console.error(...args.filter(str => typeof str === 'string'));
38
38
  }
39
39
  }
40
40
 
@@ -46,7 +46,7 @@ class Logger {
46
46
  .map(cmd => (typeof cmd === 'string' ? cmd : Array.isArray(cmd) ? cmd.join(' ') : ''))
47
47
  .join(' ');
48
48
  const message = [prefix, command, isCached ? '[cached]' : ''].join(' ').trim();
49
- this.log(message);
49
+ console.error(message);
50
50
  }
51
51
  }
52
52
 
@@ -12,10 +12,6 @@ import Release from '../GitRelease.js';
12
12
  import prompts from './prompts.js';
13
13
  import { getCommitsFromChangelog, getResolvedIssuesFromChangelog, searchQueries } from './util.js';
14
14
 
15
- /**
16
- * @typedef {import('@octokit/rest').RestEndpointMethodTypes['repos']['createRelease']['parameters']} CreateReleaseOptions
17
- */
18
-
19
15
  /**
20
16
  * @typedef {import('@octokit/rest').RestEndpointMethodTypes['repos']['createRelease']['parameters']} CreateReleaseOptions
21
17
  */
@@ -411,7 +407,7 @@ class GitHub extends Release {
411
407
 
412
408
  async commentOnResolvedItems() {
413
409
  const { isDryRun } = this.config;
414
- const { owner, project: repo } = this.getContext('repo');
410
+ const { host, owner, project: repo } = this.getContext('repo');
415
411
  const changelog = await this.getChangelog();
416
412
  const { comments } = this.options;
417
413
  const { submit, issue, pr } = comments ?? {};
@@ -423,7 +419,7 @@ class GitHub extends Release {
423
419
  const searchResults = await Promise.all(searchQueries(this.client, owner, repo, shas));
424
420
  const mergedPullRequests = searchResults.flatMap(items => items.map(item => ({ type: 'pr', number: item.number })));
425
421
 
426
- const hostURL = 'https://' + (this.options.host || repo.host);
422
+ const hostURL = 'https://' + (this.options.host || host);
427
423
  const resolvedIssues = getResolvedIssuesFromChangelog(hostURL, owner, repo, changelog);
428
424
 
429
425
  for (const item of [...resolvedIssues, ...mergedPullRequests]) {
@@ -18,7 +18,7 @@ export const getSearchQueries = (base, commits, separator = '+') => {
18
18
 
19
19
  export const searchQueries = (client, owner, repo, shas) =>
20
20
  getSearchQueries(`repo:${owner}/${repo}+type:pr+is:merged`, shas).map(
21
- async q => (await client.search.issuesAndPullRequests({ q })).data.items
21
+ async q => (await client.search.issuesAndPullRequests({ q, advanced_search: true })).data.items
22
22
  );
23
23
 
24
24
  export const getCommitsFromChangelog = changelog => {
@@ -15,7 +15,10 @@ class GitLab extends Release {
15
15
  super(...args);
16
16
  this.registerPrompts(prompts);
17
17
  this.assets = [];
18
- const { certificateAuthorityFile, secure } = this.options;
18
+ const { secure } = this.options;
19
+ const certificateAuthorityFileRef = this.options.certificateAuthorityFileRef || 'CI_SERVER_TLS_CA_FILE';
20
+ const certificateAuthorityFile =
21
+ this.options.certificateAuthorityFile || process.env[certificateAuthorityFileRef] || null;
19
22
  this.certificateAuthorityOption = {};
20
23
 
21
24
  const needsCustomAgent = Boolean(secure === false || certificateAuthorityFile);
@@ -256,6 +256,10 @@ class npm extends Plugin {
256
256
  })
257
257
  .catch(err => {
258
258
  this.debug(err);
259
+ if (this.config.isDryRun && /publish over the previously published version/.test(err)) {
260
+ return Promise.resolve();
261
+ }
262
+
259
263
  if (/one-time pass/.test(err)) {
260
264
  if (otp != null) {
261
265
  this.log.warn('The provided OTP is incorrect or has expired.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-it",
3
- "version": "19.0.0-next.1",
3
+ "version": "19.0.0-next.2",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing-related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -48,9 +48,9 @@
48
48
  "type": "module",
49
49
  "exports": {
50
50
  ".": {
51
+ "types": "./types/index.d.ts",
51
52
  "import": "./lib/index.js",
52
- "require": "./lib/index.js",
53
- "types": "./types/index.d.ts"
53
+ "require": "./lib/index.js"
54
54
  },
55
55
  "./package.json": "./package.json",
56
56
  "./test/util/index.js": "./test/util/index.js"
@@ -69,7 +69,7 @@
69
69
  "lint": "eslint lib test",
70
70
  "format": "prettier --write eslint.config.mjs \"{lib,test}/**/*.js\"",
71
71
  "docs": "remark README.md 'docs/**/*.md' '.github/*.md' -o",
72
- "test": "ava --no-worker-threads && installed-check --ignore ava --ignore nock",
72
+ "test": "node --env-file=.env.test --test && installed-check",
73
73
  "release": "./bin/release-it.js"
74
74
  },
75
75
  "author": {
@@ -90,7 +90,7 @@
90
90
  "execa": "9.5.2",
91
91
  "git-url-parse": "16.0.1",
92
92
  "globby": "14.1.0",
93
- "inquirer": "12.4.3",
93
+ "inquirer": "12.5.0",
94
94
  "issue-parser": "7.0.1",
95
95
  "lodash.get": "4.4.2",
96
96
  "lodash.merge": "4.6.2",
@@ -109,28 +109,22 @@
109
109
  },
110
110
  "devDependencies": {
111
111
  "@eslint/compat": "1.2.7",
112
- "@eslint/eslintrc": "3.3.0",
113
- "@eslint/js": "9.22.0",
112
+ "@eslint/eslintrc": "3.3.1",
113
+ "@eslint/js": "9.23.0",
114
114
  "@octokit/request-error": "6.1.7",
115
- "@types/node": "20.17.17",
116
- "ava": "6.2.0",
117
- "eslint": "9.22.0",
115
+ "@types/node": "20.17.24",
116
+ "eslint": "9.23.0",
118
117
  "eslint-config-prettier": "10.1.1",
119
- "eslint-plugin-ava": "15.0.1",
120
- "eslint-plugin-import-x": "4.6.1",
121
- "eslint-plugin-prettier": "5.2.3",
122
- "fs-monkey": "1.0.6",
118
+ "eslint-plugin-import-x": "4.9.3",
119
+ "eslint-plugin-prettier": "5.2.5",
123
120
  "globals": "16.0.0",
124
121
  "installed-check": "9.3.0",
125
- "knip": "5.45.0",
126
- "memfs": "4.17.0",
122
+ "knip": "5.46.1",
123
+ "mentoss": "0.9.0",
127
124
  "mock-stdio": "1.0.3",
128
- "nock": "14.0.1",
129
125
  "prettier": "3.5.3",
130
126
  "remark-cli": "12.0.1",
131
127
  "remark-preset-webpro": "1.1.1",
132
- "sinon": "19.0.2",
133
- "strip-ansi": "7.1.0",
134
128
  "typescript": "5.8.2"
135
129
  },
136
130
  "overrides": {
@@ -46,6 +46,10 @@
46
46
  "certificateAuthorityFile": {
47
47
  "default": null
48
48
  },
49
+ "certificateAuthorityFileRef": {
50
+ "type": "string",
51
+ "default": "CI_SERVER_TLS_CA_FILE"
52
+ },
49
53
  "secure": {
50
54
  "default": null
51
55
  },
@@ -9,6 +9,10 @@
9
9
  "type": "string",
10
10
  "description": "The JSON schema version used to validate this configuration file"
11
11
  },
12
+ "extends": {
13
+ "type": "string",
14
+ "description": "URL that specifies a configuration to extend"
15
+ },
12
16
  "hooks": {
13
17
  "type": "object",
14
18
  "additionalProperties": true,
package/test/cli.js CHANGED
@@ -1,20 +1,21 @@
1
- import test from 'ava';
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
2
3
  import mockStdIo from 'mock-stdio';
3
4
  import { version, help } from '../lib/cli.js';
4
5
  import { readJSON } from '../lib/util.js';
5
6
 
6
7
  const pkg = readJSON(new URL('../package.json', import.meta.url));
7
8
 
8
- test('should print version', t => {
9
+ test('should print version', () => {
9
10
  mockStdIo.start();
10
11
  version();
11
12
  const { stdout } = mockStdIo.end();
12
- t.is(stdout, `v${pkg.version}\n`);
13
+ assert.equal(stdout, `v${pkg.version}\n`);
13
14
  });
14
15
 
15
- test('should print help', t => {
16
+ test('should print help', () => {
16
17
  mockStdIo.start();
17
18
  help();
18
19
  const { stdout } = mockStdIo.end();
19
- t.regex(stdout, RegExp(`Release It!.+${pkg.version}`));
20
+ assert.match(stdout, new RegExp(`Release It!.+${pkg.version}`));
20
21
  });