release-it 19.0.0-next.0 → 19.0.0-next.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/lib/config.js +10 -7
- package/lib/index.js +4 -5
- package/lib/log.js +9 -8
- 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 +12 -11
- package/lib/plugin/gitlab/GitLab.js +2 -3
- package/lib/util.js +71 -42
- package/package.json +20 -16
- package/test/tasks.interactive.js +3 -4
- package/test/tasks.js +1 -2
- package/test/util/index.js +1 -4
package/lib/config.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
2
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
3
3
|
import parseToml from '@iarna/toml/parse-string.js';
|
|
4
|
-
import _ from 'lodash';
|
|
5
4
|
import { isCI } from 'ci-info';
|
|
5
|
+
import defaultsDeep from '@nodeutils/defaults-deep';
|
|
6
|
+
import { isObjectStrict } from '@phun-ky/typeof';
|
|
7
|
+
import merge from 'lodash.merge';
|
|
8
|
+
import get from 'lodash.get';
|
|
6
9
|
import { readJSON, getSystemInfo } from './util.js';
|
|
7
10
|
|
|
8
11
|
const debug = util.debug('release-it:config');
|
|
@@ -35,7 +38,8 @@ const getLocalConfig = ({ file, dir = process.cwd() }) => {
|
|
|
35
38
|
throw new Error(`Invalid configuration file at ${result.filepath}`);
|
|
36
39
|
}
|
|
37
40
|
debug({ cosmiconfig: result });
|
|
38
|
-
|
|
41
|
+
|
|
42
|
+
return result && isObjectStrict(result.config) ? result.config : localConfig;
|
|
39
43
|
};
|
|
40
44
|
|
|
41
45
|
class Config {
|
|
@@ -72,7 +76,7 @@ class Config {
|
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
mergeOptions() {
|
|
75
|
-
return
|
|
79
|
+
return defaultsDeep(
|
|
76
80
|
{},
|
|
77
81
|
this.constructorConfig,
|
|
78
82
|
{
|
|
@@ -82,15 +86,14 @@ class Config {
|
|
|
82
86
|
this.defaultConfig
|
|
83
87
|
);
|
|
84
88
|
}
|
|
85
|
-
|
|
86
89
|
getContext(path) {
|
|
87
|
-
const context =
|
|
88
|
-
return path ?
|
|
90
|
+
const context = merge({}, this.options, this.contextOptions);
|
|
91
|
+
return path ? get(context, path) : context;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
setContext(options) {
|
|
92
95
|
debug(options);
|
|
93
|
-
|
|
96
|
+
merge(this.contextOptions, options);
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
setCI(value = true) {
|
package/lib/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import { getPlugins } from './plugin/factory.js';
|
|
3
2
|
import Logger from './log.js';
|
|
4
3
|
import Config from './config.js';
|
|
5
4
|
import Shell from './shell.js';
|
|
6
5
|
import Prompt from './prompt.js';
|
|
7
6
|
import Spinner from './spinner.js';
|
|
8
|
-
import { reduceUntil, parseVersion } from './util.js';
|
|
7
|
+
import { reduceUntil, parseVersion, castArray } from './util.js';
|
|
9
8
|
|
|
10
9
|
const runTasks = async (opts, di) => {
|
|
11
10
|
let container = {};
|
|
@@ -33,20 +32,20 @@ const runTasks = async (opts, di) => {
|
|
|
33
32
|
if (!scripts || !scripts.length) return;
|
|
34
33
|
const context = config.getContext();
|
|
35
34
|
const external = true;
|
|
36
|
-
for (const script of
|
|
35
|
+
for (const script of castArray(scripts)) {
|
|
37
36
|
const task = () => shell.exec(script, { external }, context);
|
|
38
37
|
await spinner.show({ task, label: script, context, external });
|
|
39
38
|
}
|
|
40
39
|
};
|
|
41
40
|
|
|
42
41
|
const runLifeCycleHook = async (plugin, name, ...args) => {
|
|
43
|
-
if (plugin ===
|
|
42
|
+
if (plugin === plugins.at(0)) await runHook('before', name);
|
|
44
43
|
await runHook('before', plugin.namespace, name);
|
|
45
44
|
const willHookRun = (await plugin[name](...args)) !== false;
|
|
46
45
|
if (willHookRun) {
|
|
47
46
|
await runHook('after', plugin.namespace, name);
|
|
48
47
|
}
|
|
49
|
-
if (plugin ===
|
|
48
|
+
if (plugin === plugins.at(-1)) await runHook('after', name);
|
|
50
49
|
};
|
|
51
50
|
|
|
52
51
|
const [internal, external] = await getPlugins(config, container);
|
package/lib/log.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { EOL } from 'node:os';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
const { isObject, last, filter, isString, lowerCase, upperFirst, isArray } = _;
|
|
3
|
+
import { isObjectLoose } from '@phun-ky/typeof';
|
|
4
|
+
import { upperFirst } from './util.js';
|
|
6
5
|
|
|
7
6
|
class Logger {
|
|
8
7
|
constructor({ isCI = true, isVerbose = false, verbosityLevel = 0, isDryRun = false } = {}) {
|
|
@@ -33,17 +32,19 @@ class Logger {
|
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
verbose(...args) {
|
|
36
|
-
const { isExternal } =
|
|
35
|
+
const { isExternal } = isObjectLoose(args.at(-1)) ? args.at(-1) : {};
|
|
37
36
|
if (this.shouldLog(isExternal)) {
|
|
38
|
-
this.log(...filter(
|
|
37
|
+
this.log(...args.filter(str => typeof str === 'string'));
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
exec(...args) {
|
|
43
|
-
const { isDryRun: isExecutedInDryRun, isExternal, isCached } =
|
|
42
|
+
const { isDryRun: isExecutedInDryRun, isExternal, isCached } = isObjectLoose(args.at(-1)) ? args.at(-1) : {};
|
|
44
43
|
if (this.shouldLog(isExternal) || this.isDryRun) {
|
|
45
44
|
const prefix = isExecutedInDryRun == null ? '$' : '!';
|
|
46
|
-
const command = args
|
|
45
|
+
const command = args
|
|
46
|
+
.map(cmd => (typeof cmd === 'string' ? cmd : Array.isArray(cmd) ? cmd.join(' ') : ''))
|
|
47
|
+
.join(' ');
|
|
47
48
|
const message = [prefix, command, isCached ? '[cached]' : ''].join(' ').trim();
|
|
48
49
|
this.log(message);
|
|
49
50
|
}
|
|
@@ -61,7 +62,7 @@ class Logger {
|
|
|
61
62
|
const body = text.replace(new RegExp(EOL + EOL, 'g'), EOL);
|
|
62
63
|
this.obtrusive(`${header}:${EOL}${body}`);
|
|
63
64
|
} else {
|
|
64
|
-
this.obtrusive(`Empty ${
|
|
65
|
+
this.obtrusive(`Empty ${title.toLowerCase()}`);
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
}
|
package/lib/plugin/GitBase.js
CHANGED
package/lib/plugin/GitRelease.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { readJSON } from '../util.js';
|
|
1
|
+
import { pick, readJSON } from '../util.js';
|
|
3
2
|
import GitBase from './GitBase.js';
|
|
4
3
|
|
|
5
4
|
const defaultConfig = readJSON(new URL('../../config/release-it.json', import.meta.url));
|
|
@@ -12,7 +11,7 @@ class GitRelease extends GitBase {
|
|
|
12
11
|
getInitialOptions(options) {
|
|
13
12
|
const baseOptions = super.getInitialOptions(...arguments);
|
|
14
13
|
const git = options.git || defaultConfig.git;
|
|
15
|
-
const gitOptions =
|
|
14
|
+
const gitOptions = pick(git, [
|
|
16
15
|
'tagExclude',
|
|
17
16
|
'tagName',
|
|
18
17
|
'tagMatch',
|
|
@@ -21,12 +20,13 @@ class GitRelease extends GitBase {
|
|
|
21
20
|
'changelog',
|
|
22
21
|
'commit'
|
|
23
22
|
]);
|
|
24
|
-
|
|
23
|
+
|
|
24
|
+
return Object.assign({}, gitOptions, baseOptions);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
get token() {
|
|
28
28
|
const { tokenRef } = this.options;
|
|
29
|
-
return
|
|
29
|
+
return process.env[tokenRef] || null;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
async beforeRelease() {
|
package/lib/plugin/Plugin.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { debug } from 'node:util';
|
|
2
|
-
import
|
|
2
|
+
import merge from 'lodash.merge';
|
|
3
|
+
import get from 'lodash.get';
|
|
3
4
|
|
|
4
5
|
class Plugin {
|
|
5
6
|
static isEnabled() {
|
|
@@ -40,12 +41,13 @@ class Plugin {
|
|
|
40
41
|
afterRelease() {}
|
|
41
42
|
|
|
42
43
|
getContext(path) {
|
|
43
|
-
const context =
|
|
44
|
-
|
|
44
|
+
const context = merge({}, this.options, this.context);
|
|
45
|
+
|
|
46
|
+
return path ? get(context, path) : context;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
setContext(context) {
|
|
48
|
-
|
|
50
|
+
merge(this.context, context);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
exec(command, { options, context = {} } = {}) {
|
package/lib/plugin/factory.js
CHANGED
|
@@ -2,7 +2,7 @@ import url from 'node:url';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import util from 'node:util';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
|
-
import
|
|
5
|
+
import { castArray } from '../util.js';
|
|
6
6
|
import Version from './version/Version.js';
|
|
7
7
|
import Git from './git/Git.js';
|
|
8
8
|
import GitLab from './gitlab/GitLab.js';
|
|
@@ -54,23 +54,23 @@ export const getPluginName = pluginName => {
|
|
|
54
54
|
export let getPlugins = async (config, container) => {
|
|
55
55
|
const context = config.getContext();
|
|
56
56
|
const disabledPlugins = [];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
context.plugins
|
|
60
|
-
async (result, pluginConfig, pluginName) => {
|
|
57
|
+
const enabledExternalPlugins = [];
|
|
58
|
+
if (context.plugins) {
|
|
59
|
+
for (const [pluginName, pluginConfig] of Object.entries(context.plugins)) {
|
|
61
60
|
const [name, Plugin] = await load(pluginName);
|
|
62
61
|
const [namespace, options] = pluginConfig.length === 2 ? pluginConfig : [name, pluginConfig];
|
|
62
|
+
|
|
63
63
|
config.setContext({ [namespace]: options });
|
|
64
|
+
|
|
64
65
|
if (await Plugin.isEnabled(options)) {
|
|
65
66
|
const instance = new Plugin({ namespace, options: config.getContext(), container });
|
|
66
67
|
debug({ namespace, options: instance.options });
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
enabledExternalPlugins.push(instance);
|
|
69
|
+
|
|
70
|
+
disabledPlugins.push(...pluginNames.filter(p => castArray(Plugin.disablePlugin(options)).includes(p)));
|
|
69
71
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
[]
|
|
73
|
-
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
74
|
|
|
75
75
|
const enabledPlugins = await pluginNames.reduce(async (result, plugin) => {
|
|
76
76
|
if (plugin in plugins && !disabledPlugins.includes(plugin)) {
|
package/lib/plugin/git/Git.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { EOL } from 'node:os';
|
|
2
|
-
import _ from 'lodash';
|
|
3
2
|
import { execa } from 'execa';
|
|
4
3
|
import matcher from 'wildcard-match';
|
|
5
|
-
import { format, e, fixArgs } from '../../util.js';
|
|
4
|
+
import { format, e, fixArgs, once, castArray } from '../../util.js';
|
|
6
5
|
import GitBase from '../GitBase.js';
|
|
7
6
|
import prompts from './prompts.js';
|
|
8
7
|
|
|
@@ -64,7 +63,7 @@ class Git extends GitBase {
|
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
enableRollback() {
|
|
67
|
-
this.rollbackOnce =
|
|
66
|
+
this.rollbackOnce = once(this.rollback.bind(this));
|
|
68
67
|
process.on('SIGINT', this.rollbackOnce);
|
|
69
68
|
process.on('exit', this.rollbackOnce);
|
|
70
69
|
}
|
|
@@ -96,7 +95,7 @@ class Git extends GitBase {
|
|
|
96
95
|
|
|
97
96
|
async isRequiredBranch() {
|
|
98
97
|
const branch = await this.getBranchName();
|
|
99
|
-
const requiredBranches =
|
|
98
|
+
const requiredBranches = castArray(this.options.requireBranch);
|
|
100
99
|
const [branches, negated] = requiredBranches.reduce(
|
|
101
100
|
([p, n], b) => (b.startsWith('!') ? [p, [...n, b.slice(1)]] : [[...p, b], n]),
|
|
102
101
|
[[], []]
|
|
@@ -151,7 +150,7 @@ class Git extends GitBase {
|
|
|
151
150
|
|
|
152
151
|
stage(file) {
|
|
153
152
|
if (!file || !file.length) return noop;
|
|
154
|
-
const files =
|
|
153
|
+
const files = castArray(file);
|
|
155
154
|
return this.exec(['git', 'add', ...files]).catch(err => {
|
|
156
155
|
this.log.warn(`Could not stage ${files}`);
|
|
157
156
|
this.debug(err);
|
|
@@ -164,7 +163,7 @@ class Git extends GitBase {
|
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
reset(file) {
|
|
167
|
-
const files =
|
|
166
|
+
const files = castArray(file);
|
|
168
167
|
return this.exec(['git', 'checkout', 'HEAD', '--', ...files]).catch(err => {
|
|
169
168
|
this.log.warn(`Could not reset ${files}`);
|
|
170
169
|
this.debug(err);
|
|
@@ -4,11 +4,10 @@ import open from 'open';
|
|
|
4
4
|
import { Octokit } from '@octokit/rest';
|
|
5
5
|
import { globby } from 'globby';
|
|
6
6
|
import mime from 'mime-types';
|
|
7
|
-
import _ from 'lodash';
|
|
8
7
|
import retry from 'async-retry';
|
|
9
8
|
import newGithubReleaseUrl from 'new-github-release-url';
|
|
10
9
|
import { ProxyAgent } from 'proxy-agent';
|
|
11
|
-
import { format, parseVersion, readJSON, e } from '../../util.js';
|
|
10
|
+
import { format, parseVersion, readJSON, e, castArray } from '../../util.js';
|
|
12
11
|
import Release from '../GitRelease.js';
|
|
13
12
|
import prompts from './prompts.js';
|
|
14
13
|
import { getCommitsFromChangelog, getResolvedIssuesFromChangelog, searchQueries } from './util.js';
|
|
@@ -34,7 +33,7 @@ const parseErrormsg = err => {
|
|
|
34
33
|
if (err instanceof Error) {
|
|
35
34
|
const { status, message } = err;
|
|
36
35
|
const headers = err.response ? err.response.headers : {};
|
|
37
|
-
msg = `${
|
|
36
|
+
msg = `${headers?.status || status} (${message})`;
|
|
38
37
|
}
|
|
39
38
|
return msg;
|
|
40
39
|
};
|
|
@@ -166,7 +165,7 @@ class GitHub extends Release {
|
|
|
166
165
|
const githubError = new Error(message);
|
|
167
166
|
this.log.verbose(err.errors);
|
|
168
167
|
this.debug(err);
|
|
169
|
-
if (!
|
|
168
|
+
if (!RETRY_CODES.includes(err.status)) {
|
|
170
169
|
return bail(githubError);
|
|
171
170
|
}
|
|
172
171
|
throw githubError;
|
|
@@ -175,7 +174,8 @@ class GitHub extends Release {
|
|
|
175
174
|
get client() {
|
|
176
175
|
if (this._client) return this._client;
|
|
177
176
|
const { proxy, timeout } = this.options;
|
|
178
|
-
const
|
|
177
|
+
const repo = this.getContext('repo');
|
|
178
|
+
const host = this.options.host || repo.host;
|
|
179
179
|
const isGitHub = host === 'github.com';
|
|
180
180
|
const baseUrl = `https://${isGitHub ? 'api.github.com' : host}${isGitHub ? '' : '/api/v3'}`;
|
|
181
181
|
const options = {
|
|
@@ -226,6 +226,7 @@ class GitHub extends Release {
|
|
|
226
226
|
const { isPreRelease } = parseVersion(version);
|
|
227
227
|
const name = format(releaseName, this.config.getContext());
|
|
228
228
|
const releaseNotesObject = this.options.releaseNotes;
|
|
229
|
+
|
|
229
230
|
const body = autoGenerate
|
|
230
231
|
? isUpdate
|
|
231
232
|
? null
|
|
@@ -331,7 +332,7 @@ class GitHub extends Release {
|
|
|
331
332
|
const context = this.config.getContext();
|
|
332
333
|
const { isDryRun } = this.config;
|
|
333
334
|
|
|
334
|
-
const patterns =
|
|
335
|
+
const patterns = castArray(assets).map(pattern => format(pattern, context));
|
|
335
336
|
|
|
336
337
|
this.log.exec('octokit repos.uploadReleaseAssets', patterns, { isDryRun });
|
|
337
338
|
|
|
@@ -356,9 +357,9 @@ class GitHub extends Release {
|
|
|
356
357
|
}
|
|
357
358
|
|
|
358
359
|
async generateWebUrl() {
|
|
359
|
-
const
|
|
360
|
+
const repo = this.getContext('repo');
|
|
361
|
+
const host = this.options.host || repo.host;
|
|
360
362
|
const isGitHub = host === 'github.com';
|
|
361
|
-
|
|
362
363
|
const options = await this.getOctokitReleaseOptions();
|
|
363
364
|
const url = newGithubReleaseUrl({
|
|
364
365
|
user: options.owner,
|
|
@@ -422,13 +423,13 @@ class GitHub extends Release {
|
|
|
422
423
|
const searchResults = await Promise.all(searchQueries(this.client, owner, repo, shas));
|
|
423
424
|
const mergedPullRequests = searchResults.flatMap(items => items.map(item => ({ type: 'pr', number: item.number })));
|
|
424
425
|
|
|
425
|
-
const
|
|
426
|
-
const resolvedIssues = getResolvedIssuesFromChangelog(
|
|
426
|
+
const hostURL = 'https://' + (this.options.host || repo.host);
|
|
427
|
+
const resolvedIssues = getResolvedIssuesFromChangelog(hostURL, owner, repo, changelog);
|
|
427
428
|
|
|
428
429
|
for (const item of [...resolvedIssues, ...mergedPullRequests]) {
|
|
429
430
|
const { type, number } = item;
|
|
430
431
|
const comment = format(format(type === 'pr' ? pr : issue, context), context);
|
|
431
|
-
const url = `${
|
|
432
|
+
const url = `${hostURL}/${owner}/${repo}/${type === 'pr' ? 'pull' : 'issues'}/${number}`;
|
|
432
433
|
|
|
433
434
|
if (isDryRun) {
|
|
434
435
|
this.log.exec(`octokit issues.createComment (${url})`, { isDryRun });
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs, { promises } from 'node:fs'; // import fs here so it can be stubbed in tests
|
|
3
3
|
import { globby } from 'globby';
|
|
4
|
-
import _ from 'lodash';
|
|
5
4
|
import { Agent } from 'undici';
|
|
6
5
|
import Release from '../GitRelease.js';
|
|
7
|
-
import { format, e } from '../../util.js';
|
|
6
|
+
import { format, e, castArray } from '../../util.js';
|
|
8
7
|
import prompts from './prompts.js';
|
|
9
8
|
|
|
10
9
|
const docs = 'https://git.io/release-it-gitlab';
|
|
@@ -309,7 +308,7 @@ class GitLab extends Release {
|
|
|
309
308
|
const { isDryRun } = this.config;
|
|
310
309
|
const context = this.config.getContext();
|
|
311
310
|
|
|
312
|
-
const patterns =
|
|
311
|
+
const patterns = castArray(assets).map(pattern => format(pattern, context));
|
|
313
312
|
|
|
314
313
|
this.log.exec('gitlab releases#uploadAssets', patterns, { isDryRun });
|
|
315
314
|
|
package/lib/util.js
CHANGED
|
@@ -1,25 +1,57 @@
|
|
|
1
1
|
import fs, { close, openSync, statSync, utimesSync, accessSync } from 'node:fs'; // need import fs here due to test stubbing
|
|
2
2
|
import util from 'node:util';
|
|
3
3
|
import { EOL } from 'node:os';
|
|
4
|
-
import _ from 'lodash';
|
|
5
4
|
import gitUrlParse from 'git-url-parse';
|
|
6
5
|
import semver from 'semver';
|
|
7
6
|
import osName from 'os-name';
|
|
7
|
+
import { Eta } from 'eta';
|
|
8
8
|
import Log from './log.js';
|
|
9
9
|
|
|
10
|
+
const debug = util.debug('release-it:shell');
|
|
11
|
+
|
|
12
|
+
const eta = new Eta({
|
|
13
|
+
autoEscape: false,
|
|
14
|
+
useWith: true,
|
|
15
|
+
tags: ['${', '}'],
|
|
16
|
+
parse: { interpolate: '' },
|
|
17
|
+
rmWhitespace: false,
|
|
18
|
+
autoTrim: false
|
|
19
|
+
});
|
|
20
|
+
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
21
|
+
const before = (n, func) => {
|
|
22
|
+
var result;
|
|
23
|
+
if (typeof func != 'function') {
|
|
24
|
+
throw new TypeError('Missing argument for `func`');
|
|
25
|
+
}
|
|
26
|
+
n = parseInt(n);
|
|
27
|
+
return function () {
|
|
28
|
+
if (--n > 0) {
|
|
29
|
+
result = func.apply(this, arguments);
|
|
30
|
+
}
|
|
31
|
+
if (n <= 1) {
|
|
32
|
+
func = undefined;
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const tryStatFile = filePath => {
|
|
38
|
+
try {
|
|
39
|
+
return statSync(filePath);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
debug(e);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
10
46
|
export const execOpts = {
|
|
11
47
|
stdio: process.env.NODE_DEBUG && process.env.NODE_DEBUG.indexOf('release-it') === 0 ? 'pipe' : []
|
|
12
48
|
};
|
|
13
49
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const readJSON = file => JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
50
|
+
export const readJSON = file => JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
17
51
|
|
|
18
52
|
const pkg = readJSON(new URL('../package.json', import.meta.url));
|
|
19
53
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const getSystemInfo = () => {
|
|
54
|
+
export const getSystemInfo = () => {
|
|
23
55
|
return {
|
|
24
56
|
'release-it': pkg.version,
|
|
25
57
|
node: process.version,
|
|
@@ -27,9 +59,11 @@ const getSystemInfo = () => {
|
|
|
27
59
|
};
|
|
28
60
|
};
|
|
29
61
|
|
|
30
|
-
const format = (template = '', context = {}) => {
|
|
62
|
+
export const format = (template = '', context = {}) => {
|
|
63
|
+
if (!context || context === null || !template || template === null || template.indexOf('${') === -1) return template;
|
|
64
|
+
const log = new Log();
|
|
31
65
|
try {
|
|
32
|
-
return
|
|
66
|
+
return eta.renderString(template, context);
|
|
33
67
|
} catch (error) {
|
|
34
68
|
log.error(`Unable to render template with context:\n${template}\n${JSON.stringify(context)}`);
|
|
35
69
|
log.error(error);
|
|
@@ -37,21 +71,19 @@ const format = (template = '', context = {}) => {
|
|
|
37
71
|
}
|
|
38
72
|
};
|
|
39
73
|
|
|
40
|
-
const truncateLines = (input, maxLines = 10, surplusText = null) => {
|
|
74
|
+
export const truncateLines = (input, maxLines = 10, surplusText = null) => {
|
|
41
75
|
const lines = input.split(EOL);
|
|
42
76
|
const surplus = lines.length - maxLines;
|
|
43
77
|
const output = lines.slice(0, maxLines).join(EOL);
|
|
44
78
|
return surplus > 0 ? (surplusText ? `${output}${surplusText}` : `${output}${EOL}...and ${surplus} more`) : output;
|
|
45
79
|
};
|
|
46
80
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
const rejectAfter = (ms, error) =>
|
|
81
|
+
export const rejectAfter = (ms, error) =>
|
|
50
82
|
wait(ms).then(() => {
|
|
51
83
|
throw error;
|
|
52
84
|
});
|
|
53
85
|
|
|
54
|
-
const parseGitUrl = remoteUrl => {
|
|
86
|
+
export const parseGitUrl = remoteUrl => {
|
|
55
87
|
if (!remoteUrl) return { host: null, owner: null, project: null, protocol: null, remote: null, repository: null };
|
|
56
88
|
const normalizedUrl = (remoteUrl || '')
|
|
57
89
|
.replace(/^[A-Z]:\\\\/, 'file://') // Assume file protocol for Windows drive letters
|
|
@@ -59,12 +91,12 @@ const parseGitUrl = remoteUrl => {
|
|
|
59
91
|
.replace(/\\+/g, '/'); // Replace forward with backslashes
|
|
60
92
|
const parsedUrl = gitUrlParse(normalizedUrl);
|
|
61
93
|
const { resource: host, name: project, protocol, href: remote } = parsedUrl;
|
|
62
|
-
const owner = protocol === 'file' ?
|
|
94
|
+
const owner = protocol === 'file' ? parsedUrl.owner.split('/').at(-1) : parsedUrl.owner; // Fix owner for file protocol
|
|
63
95
|
const repository = `${owner}/${project}`;
|
|
64
96
|
return { host, owner, project, protocol, remote, repository };
|
|
65
97
|
};
|
|
66
98
|
|
|
67
|
-
const reduceUntil = async (collection, fn) => {
|
|
99
|
+
export const reduceUntil = async (collection, fn) => {
|
|
68
100
|
let result;
|
|
69
101
|
for (const item of collection) {
|
|
70
102
|
if (result) break;
|
|
@@ -73,7 +105,7 @@ const reduceUntil = async (collection, fn) => {
|
|
|
73
105
|
return result;
|
|
74
106
|
};
|
|
75
107
|
|
|
76
|
-
const hasAccess = path => {
|
|
108
|
+
export const hasAccess = path => {
|
|
77
109
|
try {
|
|
78
110
|
accessSync(path);
|
|
79
111
|
return true;
|
|
@@ -82,7 +114,7 @@ const hasAccess = path => {
|
|
|
82
114
|
}
|
|
83
115
|
};
|
|
84
116
|
|
|
85
|
-
const parseVersion = raw => {
|
|
117
|
+
export const parseVersion = raw => {
|
|
86
118
|
if (raw == null) return { version: raw, isPreRelease: false, preReleaseId: null };
|
|
87
119
|
const version = semver.valid(raw) ? raw : semver.coerce(raw);
|
|
88
120
|
if (!version) return { version: raw, isPreRelease: false, preReleaseId: null };
|
|
@@ -96,14 +128,14 @@ const parseVersion = raw => {
|
|
|
96
128
|
};
|
|
97
129
|
};
|
|
98
130
|
|
|
99
|
-
const e = (message, docs, fail = true) => {
|
|
131
|
+
export const e = (message, docs, fail = true) => {
|
|
100
132
|
const error = new Error(docs ? `${message}${EOL}Documentation: ${docs}${EOL}` : message);
|
|
101
133
|
error.code = fail ? 1 : 0;
|
|
102
134
|
error.cause = fail ? 'ERROR' : 'INFO';
|
|
103
135
|
return error;
|
|
104
136
|
};
|
|
105
137
|
|
|
106
|
-
const touch = (path, callback) => {
|
|
138
|
+
export const touch = (path, callback) => {
|
|
107
139
|
const stat = tryStatFile(path);
|
|
108
140
|
if (stat && stat.isDirectory()) {
|
|
109
141
|
// don't error just exit
|
|
@@ -121,28 +153,25 @@ const touch = (path, callback) => {
|
|
|
121
153
|
}
|
|
122
154
|
};
|
|
123
155
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
156
|
+
export const fixArgs = args => (args ? (typeof args === 'string' ? args.split(' ') : args) : []);
|
|
157
|
+
|
|
158
|
+
export const upperFirst = string => {
|
|
159
|
+
return string ? string.charAt(0).toUpperCase() + string.slice(1) : '';
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const castArray = arr => {
|
|
163
|
+
return Array.isArray(arr) ? arr : [arr];
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const once = fn => {
|
|
167
|
+
return before(2, fn);
|
|
131
168
|
};
|
|
132
169
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
reduceUntil,
|
|
141
|
-
parseGitUrl,
|
|
142
|
-
hasAccess,
|
|
143
|
-
parseVersion,
|
|
144
|
-
readJSON,
|
|
145
|
-
fixArgs,
|
|
146
|
-
e,
|
|
147
|
-
touch
|
|
170
|
+
export const pick = (object, keys) => {
|
|
171
|
+
return keys.reduce((obj, key) => {
|
|
172
|
+
if (object && Object.prototype.hasOwnProperty.call(object, key)) {
|
|
173
|
+
obj[key] = object[key];
|
|
174
|
+
}
|
|
175
|
+
return obj;
|
|
176
|
+
}, {});
|
|
148
177
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "release-it",
|
|
3
|
-
"version": "19.0.0-next.
|
|
3
|
+
"version": "19.0.0-next.1",
|
|
4
4
|
"description": "Generic CLI tool to automate versioning and package publishing-related tasks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"build",
|
|
@@ -79,17 +79,21 @@
|
|
|
79
79
|
"license": "MIT",
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@iarna/toml": "2.2.5",
|
|
82
|
-
"@
|
|
82
|
+
"@nodeutils/defaults-deep": "1.1.0",
|
|
83
|
+
"@octokit/rest": "21.1.1",
|
|
84
|
+
"@phun-ky/typeof": "1.2.3",
|
|
83
85
|
"async-retry": "1.3.3",
|
|
84
86
|
"chalk": "5.4.1",
|
|
85
|
-
"ci-info": "^4.
|
|
87
|
+
"ci-info": "^4.2.0",
|
|
86
88
|
"cosmiconfig": "9.0.0",
|
|
89
|
+
"eta": "3.5.0",
|
|
87
90
|
"execa": "9.5.2",
|
|
88
|
-
"git-url-parse": "16.0.
|
|
91
|
+
"git-url-parse": "16.0.1",
|
|
89
92
|
"globby": "14.1.0",
|
|
90
|
-
"inquirer": "12.4.
|
|
93
|
+
"inquirer": "12.4.3",
|
|
91
94
|
"issue-parser": "7.0.1",
|
|
92
|
-
"lodash": "4.
|
|
95
|
+
"lodash.get": "4.4.2",
|
|
96
|
+
"lodash.merge": "4.6.2",
|
|
93
97
|
"mime-types": "2.1.35",
|
|
94
98
|
"new-github-release-url": "2.0.0",
|
|
95
99
|
"open": "10.1.0",
|
|
@@ -104,30 +108,30 @@
|
|
|
104
108
|
"yargs-parser": "21.1.1"
|
|
105
109
|
},
|
|
106
110
|
"devDependencies": {
|
|
107
|
-
"@eslint/compat": "1.2.
|
|
108
|
-
"@eslint/eslintrc": "3.
|
|
109
|
-
"@eslint/js": "9.
|
|
110
|
-
"@octokit/request-error": "6.1.
|
|
111
|
+
"@eslint/compat": "1.2.7",
|
|
112
|
+
"@eslint/eslintrc": "3.3.0",
|
|
113
|
+
"@eslint/js": "9.22.0",
|
|
114
|
+
"@octokit/request-error": "6.1.7",
|
|
111
115
|
"@types/node": "20.17.17",
|
|
112
116
|
"ava": "6.2.0",
|
|
113
|
-
"eslint": "9.
|
|
114
|
-
"eslint-config-prettier": "10.
|
|
117
|
+
"eslint": "9.22.0",
|
|
118
|
+
"eslint-config-prettier": "10.1.1",
|
|
115
119
|
"eslint-plugin-ava": "15.0.1",
|
|
116
120
|
"eslint-plugin-import-x": "4.6.1",
|
|
117
121
|
"eslint-plugin-prettier": "5.2.3",
|
|
118
122
|
"fs-monkey": "1.0.6",
|
|
119
|
-
"globals": "
|
|
123
|
+
"globals": "16.0.0",
|
|
120
124
|
"installed-check": "9.3.0",
|
|
121
|
-
"knip": "5.
|
|
125
|
+
"knip": "5.45.0",
|
|
122
126
|
"memfs": "4.17.0",
|
|
123
127
|
"mock-stdio": "1.0.3",
|
|
124
128
|
"nock": "14.0.1",
|
|
125
|
-
"prettier": "3.5.
|
|
129
|
+
"prettier": "3.5.3",
|
|
126
130
|
"remark-cli": "12.0.1",
|
|
127
131
|
"remark-preset-webpro": "1.1.1",
|
|
128
132
|
"sinon": "19.0.2",
|
|
129
133
|
"strip-ansi": "7.1.0",
|
|
130
|
-
"typescript": "5.
|
|
134
|
+
"typescript": "5.8.2"
|
|
131
135
|
},
|
|
132
136
|
"overrides": {
|
|
133
137
|
"pac-resolver": "7.0.1",
|
|
@@ -2,7 +2,6 @@ import path from 'node:path';
|
|
|
2
2
|
import { renameSync } from 'node:fs';
|
|
3
3
|
import childProcess from 'node:child_process';
|
|
4
4
|
import test from 'ava';
|
|
5
|
-
import _ from 'lodash';
|
|
6
5
|
import sinon from 'sinon';
|
|
7
6
|
import Log from '../lib/log.js';
|
|
8
7
|
import Spinner from '../lib/spinner.js';
|
|
@@ -105,7 +104,7 @@ test.serial('should not run hooks for disabled release-cycle methods', async t =
|
|
|
105
104
|
|
|
106
105
|
await runTasks({}, container);
|
|
107
106
|
|
|
108
|
-
const commands =
|
|
107
|
+
const commands = exec.args.flat().filter(arg => typeof arg === 'string' && arg.startsWith('echo'));
|
|
109
108
|
|
|
110
109
|
t.true(commands.includes('echo before:init'));
|
|
111
110
|
t.true(commands.includes('echo after:afterRelease'));
|
|
@@ -140,7 +139,7 @@ test.serial('should not run hooks for cancelled release-cycle methods', async t
|
|
|
140
139
|
|
|
141
140
|
await runTasks({}, container);
|
|
142
141
|
|
|
143
|
-
const commands =
|
|
142
|
+
const commands = exec.args.flat().filter(arg => typeof arg === 'string' && arg.startsWith('echo'));
|
|
144
143
|
|
|
145
144
|
t.true(commands.includes('echo before:init'));
|
|
146
145
|
t.true(commands.includes('echo after:afterRelease'));
|
|
@@ -199,7 +198,7 @@ test.serial('should run "after:*:release" plugin hooks', async t => {
|
|
|
199
198
|
|
|
200
199
|
await runTasks({}, container);
|
|
201
200
|
|
|
202
|
-
const commands =
|
|
201
|
+
const commands = exec.args.flat().filter(arg => typeof arg === 'string' && arg.startsWith('echo'));
|
|
203
202
|
|
|
204
203
|
t.true(commands.includes('echo after:git:bump'));
|
|
205
204
|
t.true(commands.includes('echo after:npm:bump'));
|
package/test/tasks.js
CHANGED
|
@@ -3,7 +3,6 @@ import childProcess from 'node:child_process';
|
|
|
3
3
|
import { appendFileSync, mkdirSync, renameSync } from 'node:fs';
|
|
4
4
|
import test from 'ava';
|
|
5
5
|
import semver from 'semver';
|
|
6
|
-
import _ from 'lodash';
|
|
7
6
|
import sinon from 'sinon';
|
|
8
7
|
import Log from '../lib/log.js';
|
|
9
8
|
import Spinner from '../lib/spinner.js';
|
|
@@ -529,7 +528,7 @@ test.serial('should use custom changelog command with context', async t => {
|
|
|
529
528
|
|
|
530
529
|
await runTasks({}, container);
|
|
531
530
|
|
|
532
|
-
const commands =
|
|
531
|
+
const commands = exec.args.flat().filter(arg => typeof arg === 'string' && arg.startsWith('echo'));
|
|
533
532
|
|
|
534
533
|
t.deepEqual(commands, [
|
|
535
534
|
'echo before:init',
|
package/test/util/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import sinon from 'sinon';
|
|
3
2
|
import semver from 'semver';
|
|
4
3
|
import { parseVersion } from '../../lib/util.js';
|
|
@@ -9,10 +8,8 @@ import Spinner from '../../lib/spinner.js';
|
|
|
9
8
|
import Prompt from '../../lib/prompt.js';
|
|
10
9
|
|
|
11
10
|
export let factory = (Definition, { namespace, options = {}, container = {} } = {}) => {
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
options = Object.assign({}, { ci: true, verbose: false, 'dry-run': false, debug: false }, options);
|
|
14
12
|
const ns = namespace || Definition.name.toLowerCase();
|
|
15
|
-
|
|
16
13
|
container.config = container.config || new Config(Object.assign({ config: false }, options));
|
|
17
14
|
container.log = container.log || sinon.createStubInstance(Log);
|
|
18
15
|
|