first commit

This commit is contained in:
2025-10-26 23:10:15 +08:00
commit 8f0345b7be
14961 changed files with 2356381 additions and 0 deletions

64
node_modules/release-it/lib/args.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
import parseArgs from 'yargs-parser';
const aliases = {
c: 'config',
d: 'dry-run',
h: 'help',
i: 'increment',
v: 'version',
V: 'verbose'
};
const booleanOptions = [
'dry-run',
'ci',
'git',
'npm',
'github',
'gitlab',
'git.addUntrackedFiles',
'git.requireCleanWorkingDir',
'git.requireUpstream',
'git.requireCommits',
'git.requireCommitsFail',
'git.commit',
'git.tag',
'git.push',
'git.getLatestTagFromAllRefs',
'git.skipChecks',
'github.release',
'github.autoGenerate',
'github.preRelease',
'github.draft',
'github.skipChecks',
'github.web',
'github.comments.submit',
'gitlab.release',
'gitlab.autoGenerate',
'gitlab.preRelease',
'gitlab.draft',
'gitlab.useIdsForUrls',
'gitlab.useGenericPackageRepositoryForAssets',
'gitlab.skipChecks',
'npm.publish',
'npm.ignoreVersion',
'npm.allowSameVersion',
'npm.skipChecks'
];
export const parseCliArguments = args => {
const options = parseArgs(args, {
boolean: booleanOptions,
alias: aliases,
configuration: {
'parse-numbers': false,
'camel-case-expansion': false
}
});
if (options.V) {
options.verbose = typeof options.V === 'boolean' ? options.V : options.V.length;
delete options.V;
}
options.increment = options._[0] || options.i;
return options;
};

41
node_modules/release-it/lib/cli.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
import { readJSON } from './util.js';
import runTasks from './index.js';
const pkg = readJSON(new URL('../package.json', import.meta.url));
const helpText = `Release It! v${pkg.version}
Usage: release-it <increment> [options]
Use e.g. "release-it minor" directly as shorthand for "release-it --increment=minor".
-c --config Path to local configuration options [default: ".release-it.json"]
-d --dry-run Do not touch or write anything, but show the commands
-h --help Print this help
-i --increment Increment "major", "minor", "patch", or "pre*" version; or specify version [default: "patch"]
--ci No prompts, no user interaction; activated automatically in CI environments
--only-version Prompt only for version, no further interaction
-v --version Print release-it version number
--release-version Print version number to be released
--changelog Print changelog for the version to be released
-V --verbose Verbose output (user hooks output)
-VV Extra verbose output (also internal commands output)
For more details, please see https://github.com/release-it/release-it`;
/** @internal */
export const version = () => console.log(`v${pkg.version}`);
/** @internal */
export const help = () => console.log(helpText);
export default async options => {
if (options.version) {
version();
} else if (options.help) {
help();
} else {
return runTasks(options);
}
return Promise.resolve();
};

181
node_modules/release-it/lib/config.js generated vendored Normal file
View File

@@ -0,0 +1,181 @@
import util from 'node:util';
import assert from 'node:assert';
import { isCI } from 'ci-info';
import defaultsDeep from '@nodeutils/defaults-deep';
import { isObjectStrict } from '@phun-ky/typeof';
import merge from 'lodash.merge';
import { loadConfig as loadC12 } from 'c12';
import { get, getSystemInfo, readJSON } from './util.js';
const debug = util.debug('release-it:config');
const defaultConfig = readJSON(new URL('../config/release-it.json', import.meta.url));
class Config {
constructor(config = {}) {
this.constructorConfig = config;
this.contextOptions = {};
debug({ system: getSystemInfo() });
}
async init() {
await loadOptions(this.constructorConfig).then(({ options, localConfig }) => {
this._options = options;
this._localConfig = localConfig;
debug(this._options);
});
}
getContext(path) {
const context = merge({}, this.options, this.contextOptions);
return path ? get(context, path) : context;
}
setContext(options) {
debug(options);
merge(this.contextOptions, options);
}
setCI(value = true) {
this.options.ci = value;
}
get defaultConfig() {
return defaultConfig;
}
get isDryRun() {
return Boolean(this.options['dry-run']);
}
get isIncrement() {
return this.options.increment !== false;
}
get isVerbose() {
return Boolean(this.options.verbose);
}
get verbosityLevel() {
return this.options.verbose;
}
get isDebug() {
return debug.enabled;
}
get isCI() {
return Boolean(this.options.ci) || this.isReleaseVersion || this.isChangelog;
}
get isPromptOnlyVersion() {
return this.options['only-version'];
}
get isReleaseVersion() {
return Boolean(this.options['release-version']);
}
get isChangelog() {
return Boolean(this.options['changelog']);
}
get options() {
assert(this._options, `The "options" not resolve yet`);
return this._options;
}
get localConfig() {
assert(this._localConfig, `The "localConfig" not resolve yet`);
return this._localConfig;
}
}
async function loadOptions(constructorConfig) {
const localConfig = await loadLocalConfig(constructorConfig);
const merged = defaultsDeep(
{},
constructorConfig,
{
ci: isCI
},
localConfig,
defaultConfig
);
const expanded = expandPreReleaseShorthand(merged);
return {
options: expanded,
localConfig
};
}
function expandPreReleaseShorthand(options) {
const { increment, preRelease, preReleaseId, snapshot, preReleaseBase } = options;
const isPreRelease = Boolean(preRelease) || Boolean(snapshot);
const inc = snapshot ? 'prerelease' : increment;
const preId = typeof preRelease === 'string' ? preRelease : typeof snapshot === 'string' ? snapshot : preReleaseId;
options.version = {
increment: inc,
isPreRelease,
preReleaseId: preId,
preReleaseBase
};
if (typeof snapshot === 'string' && options.git) {
// Pre set and hard code some options
options.git.tagMatch = `0.0.0-${snapshot}.[0-9]*`;
options.git.getLatestTagFromAllRefs = true;
options.git.requireBranch = '!main';
options.git.requireUpstream = false;
options.npm.ignoreVersion = true;
}
return options;
}
async function loadLocalConfig(constructorConfig) {
const file = resolveFile();
const dir = resolveDir();
const extend = resolveExtend();
const defaultConfig = resolveDefaultConfig();
if (file === false) return {};
const resolvedConfig = await loadC12({
name: 'release-it',
configFile: file,
packageJson: true,
rcFile: false,
envName: false,
cwd: dir,
extend,
defaultConfig
}).catch(() => {
throw new Error(`Invalid configuration file at ${file}`);
});
debug('Loaded local config', resolvedConfig.config);
return isObjectStrict(resolvedConfig.config) ? resolvedConfig.config : {};
function resolveFile() {
if (constructorConfig.config === false) return false;
if (constructorConfig.config === true) return '.release-it';
return constructorConfig.config ?? '.release-it';
}
function resolveDir() {
return constructorConfig.configDir ?? process.cwd();
}
function resolveExtend() {
return constructorConfig.extends === false ? false : undefined;
}
function resolveDefaultConfig() {
return {
extends: constructorConfig.extends === false ? undefined : constructorConfig.extends
};
}
}
export default Config;

163
node_modules/release-it/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,163 @@
import { getPlugins } from './plugin/factory.js';
import Logger from './log.js';
import Config from './config.js';
import Shell from './shell.js';
import Prompt from './prompt.js';
import Spinner from './spinner.js';
import { reduceUntil, parseVersion, castArray } from './util.js';
const runTasks = async (opts, di) => {
let container = {};
try {
Object.assign(container, di);
container.config = container.config || new Config(opts);
await container.config.init();
const { config } = container;
const { isCI, isVerbose, verbosityLevel, isDryRun, isChangelog, isReleaseVersion } = config;
container.log = container.log || new Logger({ isCI, isVerbose, verbosityLevel, isDryRun });
container.spinner = container.spinner || new Spinner({ container, config });
container.prompt = container.prompt || new Prompt({ container: { config } });
container.shell = container.shell || new Shell({ container });
const { log, shell, spinner } = container;
const options = config.getContext();
const { hooks } = options;
const runHook = async (...name) => {
const scripts = hooks[name.join(':')];
if (!scripts || !scripts.length) return;
const context = config.getContext();
const external = true;
for (const script of castArray(scripts)) {
const task = () => shell.exec(script, { external }, context);
await spinner.show({ task, label: script, context, external });
}
};
const runLifeCycleHook = async (plugin, name, ...args) => {
if (plugin === plugins.at(0)) await runHook('before', name);
await runHook('before', plugin.namespace, name);
const willHookRun = (await plugin[name](...args)) !== false;
if (willHookRun) {
await runHook('after', plugin.namespace, name);
}
if (plugin === plugins.at(-1)) await runHook('after', name);
};
const [internal, external] = await getPlugins(config, container);
let plugins = [...external, ...internal];
for (const plugin of plugins) {
await runLifeCycleHook(plugin, 'init');
}
const { increment, isPreRelease, preReleaseId, preReleaseBase } = options.version;
const name = await reduceUntil(plugins, plugin => plugin.getName());
const latestVersion = (await reduceUntil(plugins, plugin => plugin.getLatestVersion())) || '0.0.0';
const changelog = await reduceUntil(plugins, plugin => plugin.getChangelog(latestVersion));
if (isChangelog) {
if (changelog) {
console.log(changelog);
process.exit(0);
} else {
log.warn('No changelog found');
process.exit(1);
}
}
const incrementBase = { latestVersion, increment, isPreRelease, preReleaseId, preReleaseBase };
const { snapshot } = config.options;
if (snapshot && (!incrementBase.latestVersion.startsWith('0.0.0') || incrementBase.latestVersion === '0.0.0')) {
// Reading the latest version first allows to increment the final counter, fake it if it's not a snapshot:
incrementBase.latestVersion = `0.0.0-0`;
}
let version;
if (config.isIncrement) {
incrementBase.increment = await reduceUntil(plugins, plugin => plugin.getIncrement(incrementBase));
version = await reduceUntil(plugins, plugin => plugin.getIncrementedVersionCI(incrementBase));
} else {
version = latestVersion;
}
config.setContext({ name, latestVersion, version, changelog });
if (!isReleaseVersion) {
const action = config.isIncrement ? 'release' : 'update';
const suffix = version && config.isIncrement ? `${latestVersion}...${version}` : `currently at ${latestVersion}`;
log.obtrusive(`🚀 Let's ${action} ${name} (${suffix})`);
log.preview({ title: 'changelog', text: changelog });
}
if (config.isIncrement) {
version = version || (await reduceUntil(plugins, plugin => plugin.getIncrementedVersion(incrementBase)));
}
if (isReleaseVersion) {
if (version) {
console.log(version);
process.exit(0);
} else {
log.warn(`No new version to release`);
process.exit(1);
}
}
if (version) {
config.setContext(parseVersion(version));
if (config.isPromptOnlyVersion) {
config.setCI(true);
}
for (const hook of ['beforeBump', 'bump', 'beforeRelease']) {
for (const plugin of plugins) {
const args = hook === 'bump' ? [version] : [];
await runLifeCycleHook(plugin, hook, ...args);
}
}
plugins = [...internal, ...external];
for (const hook of ['release', 'afterRelease']) {
for (const plugin of plugins) {
await runLifeCycleHook(plugin, hook);
}
}
} else {
log.obtrusive(`No new version to release`);
}
log.log(`🏁 Done (in ${Math.floor(process.uptime())}s.)`);
return {
name,
changelog,
latestVersion,
version
};
} catch (err) {
const { log } = container;
const errorMessage = err.message || err;
const logger = log || console;
err.cause === 'INFO' ? logger.info(errorMessage) : logger.error(errorMessage);
throw err;
}
};
export default runTasks;
export { default as Config } from './config.js';
export { default as Plugin } from './plugin/Plugin.js';

70
node_modules/release-it/lib/log.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
import { EOL } from 'node:os';
import { styleText } from 'node:util';
import { isObjectLoose } from '@phun-ky/typeof';
import { upperFirst } from './util.js';
class Logger {
constructor({ isCI = true, isVerbose = false, verbosityLevel = 0, isDryRun = false } = {}) {
this.isCI = isCI;
this.isVerbose = isVerbose;
this.verbosityLevel = verbosityLevel;
this.isDryRun = isDryRun;
}
shouldLog(isExternal) {
return this.verbosityLevel === 2 || (this.isVerbose && isExternal);
}
log(...args) {
console.log(...args);
}
error(...args) {
console.error([styleText('red', 'ERROR'), ...args].join(' '));
}
info(...args) {
console.error(styleText('grey', args.join(' ')));
}
warn(...args) {
console.error([styleText('yellow', 'WARNING'), ...args].join(' '));
}
verbose(...args) {
const { isExternal } = isObjectLoose(args.at(-1)) ? args.at(-1) : {};
if (this.shouldLog(isExternal)) {
console.error(...args.filter(str => typeof str === 'string'));
}
}
exec(...args) {
const { isDryRun: isExecutedInDryRun, isExternal, isCached } = isObjectLoose(args.at(-1)) ? args.at(-1) : {};
if (this.shouldLog(isExternal) || this.isDryRun) {
const prefix = isExecutedInDryRun == null ? '$' : '!';
const command = args
.map(cmd => (typeof cmd === 'string' ? cmd : Array.isArray(cmd) ? cmd.join(' ') : ''))
.join(' ');
const message = [prefix, command, isCached ? '[cached]' : ''].join(' ').trim();
console.error(message);
}
}
obtrusive(...args) {
if (!this.isCI) this.log();
this.log(...args);
if (!this.isCI) this.log();
}
preview({ title, text }) {
if (text) {
const header = styleText('bold', upperFirst(title));
const body = text.replace(new RegExp(EOL + EOL, 'g'), EOL);
this.obtrusive(`${header}:${EOL}${body}`);
} else {
this.obtrusive(`Empty ${title.toLowerCase()}`);
}
}
}
export default Logger;

126
node_modules/release-it/lib/plugin/GitBase.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
import { EOL } from 'node:os';
import { format, parseGitUrl } from '../util.js';
import Plugin from './Plugin.js';
const options = { write: false };
const changelogFallback = 'git log --pretty=format:"* %s (%h)"';
class GitBase extends Plugin {
async init() {
const remoteUrl = await this.getRemoteUrl();
await this.fetch(remoteUrl);
const branchName = await this.getBranchName();
const repo = parseGitUrl(remoteUrl);
this.setContext({ remoteUrl, branchName, repo });
this.config.setContext({ remoteUrl, branchName, repo });
const latestTag = await this.getLatestTagName();
const secondLatestTag = !this.config.isIncrement ? await this.getSecondLatestTagName(latestTag) : null;
const tagTemplate = this.options.tagName || ((latestTag || '').match(/^v/) ? 'v${version}' : '${version}');
this.config.setContext({ latestTag, secondLatestTag, tagTemplate });
}
getName() {
const repo = this.getContext('repo');
return repo.project;
}
getLatestVersion() {
const { tagTemplate, latestTag } = this.config.getContext();
const prefix = format(tagTemplate.replace(/\$\{version\}/, ''), this.config.getContext());
return latestTag ? latestTag.replace(prefix, '').replace(/^v/, '') : null;
}
async getChangelog() {
const { snapshot } = this.config.getContext();
const { latestTag, secondLatestTag } = this.config.getContext();
const context = { latestTag, from: latestTag, to: 'HEAD' };
const { changelog } = this.options;
if (!changelog) return null;
if (latestTag && !this.config.isIncrement) {
context.from = secondLatestTag;
context.to = `${latestTag}^1`;
}
// For now, snapshots do not get a changelog, as it often goes haywire (easy to add to release manually)
if (snapshot) return '';
if (!context.from && changelog.includes('${from}')) {
return this.exec(changelogFallback);
}
return this.exec(changelog, { context, options });
}
bump(version) {
const { tagTemplate } = this.config.getContext();
const context = Object.assign(this.config.getContext(), { version });
const tagName = format(tagTemplate, context) || version;
this.setContext({ version });
this.config.setContext({ tagName });
}
isRemoteName(remoteUrlOrName) {
return remoteUrlOrName && !remoteUrlOrName.includes('/');
}
async getRemoteUrl() {
const remoteNameOrUrl = this.options.pushRepo || (await this.getRemote()) || 'origin';
return this.isRemoteName(remoteNameOrUrl)
? this.exec(`git remote get-url ${remoteNameOrUrl}`, { options }).catch(() =>
this.exec(`git config --get remote.${remoteNameOrUrl}.url`, { options }).catch(() => null)
)
: remoteNameOrUrl;
}
async getRemote() {
const branchName = await this.getBranchName();
return branchName ? await this.getRemoteForBranch(branchName) : null;
}
getBranchName() {
return this.exec('git rev-parse --abbrev-ref HEAD', { options }).catch(() => null);
}
getRemoteForBranch(branch) {
return this.exec(`git config --get branch.${branch}.remote`, { options }).catch(() => null);
}
fetch(remoteUrl) {
return this.exec('git fetch').catch(err => {
this.debug(err);
throw new Error(`Unable to fetch from ${remoteUrl}${EOL}${err.message}`);
});
}
getLatestTagName() {
const context = Object.assign({}, this.config.getContext(), { version: '*' });
const match = format(this.options.tagMatch || this.options.tagName || '${version}', context);
const exclude = this.options.tagExclude ? ` --exclude=${format(this.options.tagExclude, context)}` : '';
if (this.options.getLatestTagFromAllRefs) {
return this.exec(
`git -c "versionsort.suffix=-" for-each-ref --count=1 --sort=-v:refname --format="%(refname:short)" refs/tags/${match}`,
{ options }
).then(
stdout => stdout || null,
() => null
);
} else {
return this.exec(`git describe --tags --match=${match} --abbrev=0${exclude}`, { options }).then(
stdout => stdout || null,
() => null
);
}
}
async getSecondLatestTagName(latestTag) {
const sha = await this.exec(`git rev-list ${latestTag || '--skip=1'} --tags --max-count=1`, {
options
});
return this.exec(`git describe --tags --abbrev=0 "${sha}^"`, { options }).catch(() => null);
}
}
export default GitBase;

65
node_modules/release-it/lib/plugin/GitRelease.js generated vendored Normal file
View File

@@ -0,0 +1,65 @@
import { pick, readJSON } from '../util.js';
import GitBase from './GitBase.js';
const defaultConfig = readJSON(new URL('../../config/release-it.json', import.meta.url));
class GitRelease extends GitBase {
static isEnabled(options) {
return options.release;
}
getInitialOptions(options) {
const baseOptions = super.getInitialOptions(...arguments);
const git = options.git || defaultConfig.git;
const gitOptions = pick(git, [
'tagExclude',
'tagName',
'tagMatch',
'getLatestTagFromAllRefs',
'pushRepo',
'changelog',
'commit'
]);
return Object.assign({}, gitOptions, baseOptions);
}
get token() {
const { tokenRef } = this.options;
return process.env[tokenRef] || null;
}
async beforeRelease() {
const { releaseNotes: script } = this.options;
const { changelog } = this.config.getContext();
const releaseNotes =
typeof script === 'function' || typeof script === 'string' ? await this.processReleaseNotes(script) : changelog;
this.setContext({ releaseNotes });
if (releaseNotes !== changelog) {
this.log.preview({ title: 'release notes', text: releaseNotes });
}
}
async processReleaseNotes(script) {
if (typeof script === 'function') {
const ctx = Object.assign({}, this.config.getContext(), { [this.namespace]: this.getContext() });
return script(ctx);
}
if (typeof script === 'string') {
return this.exec(script);
}
}
afterRelease() {
const { isReleased, releaseUrl, discussionUrl } = this.getContext();
if (isReleased) {
this.log.log(`🔗 ${releaseUrl}`);
}
if (discussionUrl) {
this.log.log(`🔗 ${discussionUrl}`);
}
}
}
export default GitRelease;

75
node_modules/release-it/lib/plugin/Plugin.js generated vendored Normal file
View File

@@ -0,0 +1,75 @@
import { debug } from 'node:util';
import merge from 'lodash.merge';
import { get } from '../util.js';
class Plugin {
static isEnabled() {
return true;
}
static disablePlugin() {
return null;
}
constructor({ namespace, options = {}, container = {} } = {}) {
this.namespace = namespace;
this.options = Object.freeze(this.getInitialOptions(options, namespace));
this.context = {};
this.config = container.config;
this.log = container.log;
this.shell = container.shell;
this.spinner = container.spinner;
this.prompt = container.prompt;
this.debug = debug(`release-it:${namespace}`);
}
getInitialOptions(options, namespace) {
return options[namespace] || {};
}
init() {}
getName() {}
getLatestVersion() {}
getChangelog() {}
getIncrement() {}
getIncrementedVersionCI() {}
getIncrementedVersion() {}
beforeBump() {}
bump() {}
beforeRelease() {}
release() {}
afterRelease() {}
getContext(path) {
const context = merge({}, this.options, this.context);
return path ? get(context, path) : context;
}
setContext(context) {
merge(this.context, context);
}
exec(command, { options, context = {} } = {}) {
const ctx = Object.assign(context, this.config.getContext(), { [this.namespace]: this.getContext() });
return this.shell.exec(command, options, ctx);
}
registerPrompts(prompts) {
this.prompt.register(prompts, this.namespace);
}
async showPrompt(options) {
options.namespace = this.namespace;
return this.prompt.show(options);
}
step(options) {
const context = Object.assign({}, this.config.getContext(), { [this.namespace]: this.getContext() });
const opts = Object.assign({}, options, { context });
const isException = this.config.isPromptOnlyVersion && ['incrementList', 'publish', 'otp'].includes(opts.prompt);
return this.config.isCI && !isException ? this.spinner.show(opts) : this.showPrompt(opts);
}
}
export default Plugin;

89
node_modules/release-it/lib/plugin/factory.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
import url from 'node:url';
import path from 'node:path';
import util from 'node:util';
import { createRequire } from 'node:module';
import { castArray } from '../util.js';
import Version from './version/Version.js';
import Git from './git/Git.js';
import GitLab from './gitlab/GitLab.js';
import GitHub from './github/GitHub.js';
import npm from './npm/npm.js';
const debug = util.debug('release-it:plugins');
const pluginNames = ['npm', 'git', 'github', 'gitlab', 'version'];
const plugins = {
version: Version,
git: Git,
gitlab: GitLab,
github: GitHub,
npm: npm
};
const load = async pluginName => {
let plugin = null;
try {
const module = await import(pluginName);
plugin = module.default;
} catch (err) {
debug(err);
try {
const module = await import(path.join(process.cwd(), pluginName));
plugin = module.default;
} catch (err) {
debug(err);
// In some cases or tests we might need to support legacy `require.resolve`
const require = createRequire(process.cwd());
const module = await import(url.pathToFileURL(require.resolve(pluginName, { paths: [process.cwd()] })));
plugin = module.default;
}
}
return [getPluginName(pluginName), plugin];
};
/** @internal */
export const getPluginName = pluginName => {
if (pluginName.startsWith('.')) {
return path.parse(pluginName).name;
}
return pluginName;
};
export let getPlugins = async (config, container) => {
const context = config.getContext();
const disabledPlugins = [];
const enabledExternalPlugins = [];
if (context.plugins) {
for (const [pluginName, pluginConfig] of Object.entries(context.plugins)) {
const [name, Plugin] = await load(pluginName);
const [namespace, options] = pluginConfig.length === 2 ? pluginConfig : [name, pluginConfig];
config.setContext({ [namespace]: options });
if (await Plugin.isEnabled(options)) {
const instance = new Plugin({ namespace, options: config.getContext(), container });
debug({ namespace, options: instance.options });
enabledExternalPlugins.push(instance);
disabledPlugins.push(...pluginNames.filter(p => castArray(Plugin.disablePlugin(options)).includes(p)));
}
}
}
const enabledPlugins = await pluginNames.reduce(async (result, plugin) => {
if (plugin in plugins && !disabledPlugins.includes(plugin)) {
const Plugin = plugins[plugin];
const pluginOptions = context[plugin];
if (await Plugin.isEnabled(pluginOptions)) {
const instance = new Plugin({ namespace: plugin, options: context, container });
debug({ namespace: plugin, options: instance.options });
(await result).push(instance);
}
}
return result;
}, []);
return [enabledPlugins, enabledExternalPlugins];
};

242
node_modules/release-it/lib/plugin/git/Git.js generated vendored Normal file
View File

@@ -0,0 +1,242 @@
import { EOL } from 'node:os';
import { spawn } from 'node:child_process';
import matcher from 'wildcard-match';
import { format, e, fixArgs, once, castArray } from '../../util.js';
import GitBase from '../GitBase.js';
import prompts from './prompts.js';
const noop = Promise.resolve();
const invalidPushRepoRe = /^\S+@/;
const options = { write: false };
const docs = 'https://git.io/release-it-git';
async function isGitRepo() {
return await new Promise(resolve => {
const process = spawn('git', ['rev-parse', '--git-dir']);
process.on('close', code => resolve(code === 0));
process.on('error', () => resolve(false));
});
}
class Git extends GitBase {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
static async isEnabled(options) {
return options !== false && (await isGitRepo());
}
async init() {
if (this.options.requireBranch && !(await this.isRequiredBranch(this.options.requireBranch))) {
throw e(`Must be on branch ${this.options.requireBranch}`, docs);
}
if (this.options.requireCleanWorkingDir && !(await this.isWorkingDirClean())) {
throw e(`Working dir must be clean.${EOL}Please stage and commit your changes.`, docs);
}
await super.init();
const remoteUrl = this.getContext('remoteUrl');
if (this.options.push && !remoteUrl) {
throw e(`Could not get remote Git url.${EOL}Please add a remote repository.`, docs);
}
if (this.options.requireUpstream && !(await this.hasUpstreamBranch())) {
throw e(`No upstream configured for current branch.${EOL}Please set an upstream branch.`, docs);
}
if (this.options.requireCommits && (await this.getCommitsSinceLatestTag(this.options.commitsPath)) === 0) {
throw e(`There are no commits since the latest tag.`, docs, this.options.requireCommitsFail);
}
}
rollback() {
this.log.info('Rolling back changes...');
const { tagName } = this.config.getContext();
const { isCommitted, isTagged } = this.getContext();
if (isTagged) {
this.log.info(`Deleting local tag ${tagName}`);
this.exec(`git tag --delete ${tagName}`);
}
this.log.info(`Resetting local changes made`);
this.exec(`git reset --hard HEAD${isCommitted ? '~1' : ''}`);
}
enableRollback() {
this.rollbackOnce = once(this.rollback.bind(this));
process.on('SIGINT', this.rollbackOnce);
process.on('exit', this.rollbackOnce);
}
disableRollback() {
if (this.rollbackOnce) {
process.removeListener('SIGINT', this.rollbackOnce);
process.removeListener('exit', this.rollbackOnce);
}
}
async beforeRelease() {
if (this.options.commit) {
if (this.options.requireCleanWorkingDir) {
this.enableRollback();
}
const changeSet = await this.status();
this.log.preview({ title: 'changeset', text: changeSet });
await this.stageDir();
}
}
async release() {
const { commit, tag, push } = this.options;
await this.step({ enabled: commit, task: () => this.commit(), label: 'Git commit', prompt: 'commit' });
await this.step({ enabled: tag, task: () => this.tag(), label: 'Git tag', prompt: 'tag' });
return !!(await this.step({ enabled: push, task: () => this.push(), label: 'Git push', prompt: 'push' }));
}
async isRequiredBranch() {
const branch = await this.getBranchName();
const requiredBranches = castArray(this.options.requireBranch);
const [branches, negated] = requiredBranches.reduce(
([p, n], b) => (b.startsWith('!') ? [p, [...n, b.slice(1)]] : [[...p, b], n]),
[[], []]
);
return (
(branches.length > 0 ? matcher(branches)(branch) : true) &&
(negated.length > 0 ? !matcher(negated)(branch) : true)
);
}
async hasUpstreamBranch() {
const ref = await this.exec('git symbolic-ref HEAD', { options });
const branch = await this.exec(`git for-each-ref --format="%(upstream:short)" ${ref}`, { options }).catch(
() => null
);
return Boolean(branch);
}
tagExists(tag) {
return this.exec(`git show-ref --tags --quiet --verify -- refs/tags/${tag}`, { options }).then(
() => true,
() => false
);
}
isWorkingDirClean() {
return this.exec('git diff --quiet HEAD', { options }).then(
() => true,
() => false
);
}
async getCommitsSinceLatestTag(commitsPath = '') {
const latestTagName = await this.getLatestTagName();
const ref = latestTagName ? `${latestTagName}..HEAD` : 'HEAD';
return this.exec(`git rev-list ${ref} --count ${commitsPath ? `-- ${commitsPath}` : ''}`, { options }).then(Number);
}
async getUpstreamArgs(pushRepo) {
if (pushRepo && !this.isRemoteName(pushRepo)) {
// Use (only) `pushRepo` if it's configured and looks like a url
return [pushRepo];
} else if (!(await this.hasUpstreamBranch())) {
// Start tracking upstream branch (`pushRepo` is a name if set)
return ['--set-upstream', pushRepo || 'origin', await this.getBranchName()];
} else if (pushRepo && !invalidPushRepoRe.test(pushRepo)) {
return [pushRepo];
} else {
return [];
}
}
stage(file) {
if (!file || !file.length) return noop;
const files = castArray(file);
return this.exec(['git', 'add', ...files]).catch(err => {
this.log.warn(`Could not stage ${files}`);
this.debug(err);
});
}
stageDir({ baseDir = '.' } = {}) {
const { addUntrackedFiles } = this.options;
return this.exec(['git', 'add', baseDir, addUntrackedFiles ? '--all' : '--update']);
}
reset(file) {
const files = castArray(file);
return this.exec(['git', 'checkout', 'HEAD', '--', ...files]).catch(err => {
this.log.warn(`Could not reset ${files}`);
this.debug(err);
});
}
status() {
return this.exec('git status --short --untracked-files=no', { options }).catch(() => null);
}
commit({ message = this.options.commitMessage, args = this.options.commitArgs } = {}) {
const msg = format(message, this.config.getContext());
const commitMessageArgs = msg ? ['--message', msg] : [];
return this.exec(['git', 'commit', ...commitMessageArgs, ...fixArgs(args)]).then(
() => this.setContext({ isCommitted: true }),
err => {
this.debug(err);
if (/nothing (added )?to commit/.test(err) || /nichts zu committen/.test(err)) {
this.log.warn('No changes to commit. The latest commit will be tagged.');
} else {
throw new Error(err);
}
}
);
}
tag({ name, annotation = this.options.tagAnnotation, args = this.options.tagArgs } = {}) {
const message = format(annotation, this.config.getContext());
const tagName = name || this.config.getContext('tagName');
return this.exec(['git', 'tag', '--annotate', '--message', message, ...fixArgs(args), tagName])
.then(() => this.setContext({ isTagged: true }))
.catch(err => {
const { latestTag, tagName } = this.config.getContext();
if (/tag '.+' already exists/.test(err) && latestTag === tagName) {
this.log.warn(`Tag "${tagName}" already exists`);
} else {
throw err;
}
});
}
async push({ args = this.options.pushArgs } = {}) {
const { pushRepo } = this.options;
const upstreamArgs = await this.getUpstreamArgs(pushRepo);
try {
const push = await this.exec(['git', 'push', ...fixArgs(args), ...upstreamArgs]);
this.disableRollback();
return push;
} catch (error) {
try {
await this.rollbackTagPush();
} catch (tagError) {
this.log.warn(`An error was encountered when trying to rollback the tag on the remote: ${tagError.message}`);
}
throw error;
}
}
async rollbackTagPush() {
const { isTagged } = this.getContext();
if (isTagged) {
const { tagName } = this.config.getContext();
this.log.info(`Rolling back remote tag push ${tagName}`);
await this.exec(`git push origin --delete ${tagName}`);
}
}
afterRelease() {
this.disableRollback();
}
}
export default Git;

19
node_modules/release-it/lib/plugin/git/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
import { format, truncateLines } from '../../util.js';
export default {
commit: {
type: 'confirm',
message: context => `Commit (${truncateLines(format(context.git.commitMessage, context), 1, ' [...]')})?`,
default: true
},
tag: {
type: 'confirm',
message: context => `Tag (${format(context.tagName, context)})?`,
default: true
},
push: {
type: 'confirm',
message: () => 'Push?',
default: true
}
};

471
node_modules/release-it/lib/plugin/github/GitHub.js generated vendored Normal file
View File

@@ -0,0 +1,471 @@
import { createReadStream, statSync } from 'node:fs';
import path from 'node:path';
import open from 'open';
import { Octokit } from '@octokit/rest';
import { glob } from 'tinyglobby';
import mime from 'mime-types';
import retry from 'async-retry';
import newGithubReleaseUrl from 'new-github-release-url';
import { ProxyAgent } from 'proxy-agent';
import { format, parseVersion, readJSON, e, castArray } from '../../util.js';
import Release from '../GitRelease.js';
import prompts from './prompts.js';
import { getCommitsFromChangelog, getResolvedIssuesFromChangelog, searchQueries } from './util.js';
/**
* @typedef {import('@octokit/rest').RestEndpointMethodTypes['repos']['createRelease']['parameters']} CreateReleaseOptions
*/
const pkg = readJSON(new URL('../../../package.json', import.meta.url));
const docs = 'https://git.io/release-it-github';
const RETRY_CODES = [408, 413, 429, 500, 502, 503, 504, 521, 522, 524];
const DEFAULT_RETRY_MIN_TIMEOUT = 1000;
const parseErrormsg = err => {
let msg = err;
if (err instanceof Error) {
const { status, message } = err;
const headers = err.response ? err.response.headers : {};
msg = `${headers?.status || status} (${message})`;
}
return msg;
};
const truncateBody = body => {
// https://github.com/release-it/release-it/issues/965
if (body && body.length >= 124000) return body.substring(0, 124000) + '...';
return body;
};
class GitHub extends Release {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
async init() {
await super.init();
const { skipChecks, tokenRef, web, update, assets } = this.options;
if (!this.token || web) {
if (!web) {
this.log.warn(`Environment variable "${tokenRef}" is required for automated GitHub Releases.`);
this.log.warn('Falling back to web-based GitHub Release.');
}
this.setContext({ isWeb: true });
return;
}
if (web && assets) {
this.log.warn('Assets are not included in web-based releases.');
}
if (!skipChecks) {
// If we're running on GitHub Actions, we can skip the authentication and
// collaborator checks. Ref: https://bit.ly/2vsyRzu
if (process.env.GITHUB_ACTIONS) {
this.setContext({ username: process.env.GITHUB_ACTOR });
} else {
if (!(await this.isAuthenticated())) {
throw e(`Could not authenticate with GitHub using environment variable "${tokenRef}".`, docs);
}
if (!(await this.isCollaborator())) {
const { repository } = this.getContext('repo');
const { username } = this.getContext();
throw e(`User ${username} is not a collaborator for ${repository}.`, docs);
}
}
}
if (update) {
const { latestTag } = this.config.getContext();
try {
const { id, upload_url, tag_name } = await this.getLatestRelease();
if (latestTag === tag_name) {
this.setContext({ isUpdate: true, isReleased: true, releaseId: id, upload_url });
} else {
this.setContext({ isUpdate: false });
}
} catch (error) {
this.setContext({ isUpdate: false });
}
if (!this.getContext('isUpdate')) {
this.log.warn(`GitHub release for tag ${latestTag} was not found. Creating new release.`);
}
}
}
async isAuthenticated() {
if (this.config.isDryRun) return true;
try {
this.log.verbose('octokit users#getAuthenticated');
const { data } = await this.client.users.getAuthenticated();
this.setContext({ username: data.login });
return true;
} catch (err) {
this.debug(err);
return false;
}
}
async isCollaborator() {
if (this.config.isDryRun) return true;
const { owner, project: repo } = this.getContext('repo');
const { username } = this.getContext();
try {
const options = { owner, repo, username };
this.log.verbose(`octokit repos#checkCollaborator (${username})`);
await this.client.repos.checkCollaborator(options);
return true;
} catch (err) {
this.debug(err);
return false;
}
}
async release() {
const { assets } = this.options;
const { isWeb, isUpdate } = this.getContext();
const { isCI } = this.config;
const type = isUpdate ? 'update' : 'create';
const publishMethod = `${type}Release`;
if (isWeb) {
const task = () => this.createWebRelease();
return this.step({ task, label: 'Generating link to GitHub Release web interface', prompt: 'release' });
} else if (isCI) {
await this.step({ task: () => this[publishMethod](), label: `GitHub ${type} release` });
await this.step({ enabled: assets, task: () => this.uploadAssets(), label: 'GitHub upload assets' });
return this.step({
task: () => (isUpdate ? Promise.resolve() : this.commentOnResolvedItems()),
label: 'GitHub comment on resolved items'
});
} else {
const release = async () => {
await this[publishMethod]();
await this.uploadAssets();
return isUpdate ? Promise.resolve(true) : this.commentOnResolvedItems();
};
return this.step({ task: release, label: `GitHub ${type} release`, prompt: 'release' });
}
}
handleError(err, bail) {
const message = parseErrormsg(err);
const githubError = new Error(message);
this.log.verbose(err.errors);
this.debug(err);
if (!RETRY_CODES.includes(err.status)) {
return bail(githubError);
}
throw githubError;
}
get client() {
if (this._client) return this._client;
const { proxy, timeout } = this.options;
const repo = this.getContext('repo');
const host = this.options.host || repo.host;
const isGitHub = host === 'github.com';
const baseUrl = `https://${isGitHub ? 'api.github.com' : host}${isGitHub ? '' : '/api/v3'}`;
const options = {
baseUrl,
auth: `token ${this.token}`,
userAgent: `release-it/${pkg.version}`,
log: this.config.isDebug ? console : {},
request: {
timeout
}
};
if (proxy) {
options.request.agent = new ProxyAgent(proxy);
}
const client = new Octokit(options);
this._client = client;
return client;
}
async getLatestRelease() {
const { owner, project: repo } = this.getContext('repo');
try {
const options = { owner, repo };
this.debug(options);
const response = await this.client.repos.listReleases({ owner, repo, per_page: 1, page: 1 });
this.debug(response.data[0]);
return response.data[0];
} catch (err) {
return this.handleError(err, () => {});
}
}
async getOctokitReleaseOptions(options = {}) {
const { owner, project: repo } = this.getContext('repo');
const {
releaseName,
draft = false,
preRelease = false,
autoGenerate = false,
makeLatest = true,
discussionCategoryName = undefined
} = this.options;
const { tagName } = this.config.getContext();
const { version, releaseNotes, isUpdate } = this.getContext();
const { isPreRelease } = parseVersion(version);
const name = format(releaseName, this.config.getContext());
const releaseNotesObject = this.options.releaseNotes;
const body = autoGenerate
? isUpdate
? null
: ''
: truncateBody(releaseNotesObject?.commit ? await this.renderReleaseNotes(releaseNotesObject) : releaseNotes);
/**
* @type {CreateReleaseOptions}
*/
const contextOptions = {
owner,
make_latest: makeLatest.toString(),
repo,
tag_name: tagName,
name,
body,
draft,
prerelease: isPreRelease || preRelease,
generate_release_notes: autoGenerate,
discussion_category_name: discussionCategoryName
};
return Object.assign(options, contextOptions);
}
retry(fn) {
const { retryMinTimeout } = this.options;
return retry(fn, {
retries: 2,
minTimeout: typeof retryMinTimeout === 'number' ? retryMinTimeout : DEFAULT_RETRY_MIN_TIMEOUT
});
}
async createRelease() {
const options = await this.getOctokitReleaseOptions();
const { isDryRun } = this.config;
this.log.exec(`octokit repos.createRelease "${options.name}" (${options.tag_name})`, { isDryRun });
if (isDryRun) {
this.setContext({ isReleased: true, releaseUrl: this.getReleaseUrlFallback(options.tag_name) });
return true;
}
return this.retry(async bail => {
try {
this.debug(options);
const response = await this.client.repos.createRelease(options);
this.debug(response.data);
const { html_url, upload_url, id, discussion_url } = response.data;
this.setContext({
isReleased: true,
releaseId: id,
releaseUrl: html_url,
upload_url,
discussionUrl: discussion_url
});
this.config.setContext({
isReleased: true,
releaseId: id,
releaseUrl: html_url,
upload_url,
discussionUrl: discussion_url
});
this.log.verbose(`octokit repos.createRelease: done (${response.headers.location})`);
return response.data;
} catch (err) {
return this.handleError(err, bail);
}
});
}
uploadAsset(filePath) {
const url = this.getContext('upload_url');
const name = path.basename(filePath);
const contentType = mime.contentType(name) || 'application/octet-stream';
const contentLength = statSync(filePath).size;
return this.retry(async bail => {
try {
const options = {
url,
data: createReadStream(filePath),
name,
headers: {
'content-type': contentType,
'content-length': contentLength
}
};
this.debug(options);
const response = await this.client.repos.uploadReleaseAsset(options);
this.debug(response.data);
this.log.verbose(`octokit repos.uploadReleaseAsset: done (${response.data.browser_download_url})`);
return response.data;
} catch (err) {
return this.handleError(err, bail);
}
});
}
uploadAssets() {
const { assets } = this.options;
const { isReleased } = this.getContext();
const context = this.config.getContext();
const { isDryRun } = this.config;
const patterns = castArray(assets).map(pattern => format(pattern, context));
this.log.exec('octokit repos.uploadReleaseAssets', patterns, { isDryRun });
if (!assets || !isReleased) {
return true;
}
return glob(patterns).then(files => {
if (!files.length) {
this.log.warn(`octokit repos.uploadReleaseAssets: did not find "${assets}" relative to ${process.cwd()}`);
}
if (isDryRun) return Promise.resolve();
return Promise.all(files.map(filePath => this.uploadAsset(filePath)));
});
}
getReleaseUrlFallback(tagName) {
const { host, repository } = this.getContext('repo');
return `https://${host}/${repository}/releases/tag/${tagName}`;
}
async generateWebUrl() {
const repo = this.getContext('repo');
const host = this.options.host || repo.host;
const isGitHub = host === 'github.com';
const options = await this.getOctokitReleaseOptions();
const url = newGithubReleaseUrl({
user: options.owner,
repo: options.repo,
tag: options.tag_name,
isPrerelease: options.prerelease,
title: options.name,
body: options.body
});
return isGitHub ? url : url.replace('github.com', host);
}
async createWebRelease() {
const { isCI } = this.config;
const { tagName } = this.config.getContext();
const url = await this.generateWebUrl();
if (isCI) {
this.setContext({ isReleased: true, releaseUrl: url });
} else {
const isWindows = process.platform === 'win32';
if (isWindows) this.log.info(`Opening ${url}`);
await open(url, { wait: isWindows });
this.setContext({ isReleased: true, releaseUrl: this.getReleaseUrlFallback(tagName) });
}
}
async updateRelease() {
const { isDryRun } = this.config;
const release_id = this.getContext('releaseId');
const options = await this.getOctokitReleaseOptions({ release_id });
this.log.exec(`octokit repos.updateRelease (${options.tag_name})`, { isDryRun });
if (isDryRun) return true;
return this.retry(async bail => {
try {
this.debug(options);
const response = await this.client.repos.updateRelease(options);
this.setContext({ releaseUrl: response.data.html_url });
this.debug(response.data);
this.log.verbose(`octokit repos.updateRelease: done (${response.headers.location})`);
return true;
} catch (err) {
return this.handleError(err, bail);
}
});
}
async commentOnResolvedItems() {
const { isDryRun } = this.config;
const { host, owner, project: repo } = this.getContext('repo');
const changelog = await this.getChangelog();
const { comments } = this.options;
const { submit, issue, pr } = comments ?? {};
const context = this.getContext();
if (!submit || !changelog) return true;
const shas = getCommitsFromChangelog(changelog);
const searchResults = await Promise.all(searchQueries(this.client, owner, repo, shas));
const mergedPullRequests = searchResults.flatMap(items => items.map(item => ({ type: 'pr', number: item.number })));
const hostURL = 'https://' + (this.options.host || host);
const resolvedIssues = getResolvedIssuesFromChangelog(hostURL, owner, repo, changelog);
for (const item of [...resolvedIssues, ...mergedPullRequests]) {
const { type, number } = item;
const comment = format(format(type === 'pr' ? pr : issue, context), context);
const url = `${hostURL}/${owner}/${repo}/${type === 'pr' ? 'pull' : 'issues'}/${number}`;
if (isDryRun) {
this.log.exec(`octokit issues.createComment (${url})`, { isDryRun });
return Promise.resolve();
}
try {
await this.client.issues.createComment({ owner, repo, issue_number: number, body: comment });
this.log.log(`● Commented on ${url}`);
} catch (error) {
this.log.log(`✕ Failed to comment on ${url}`);
}
}
}
async getCommits() {
const { owner, project: repo } = this.getContext('repo');
const { latestTag } = this.config.getContext();
this.debug({ owner, repo, base: latestTag, head: 'HEAD' });
const { data } = await this.client.repos.compareCommits({ owner, repo, base: latestTag, head: 'HEAD' });
return data.commits;
}
async renderReleaseNotes(releaseNotes) {
const { commit: template, excludeMatches = [] } = releaseNotes;
const commits = await this.getCommits();
if (this.options.commit) commits.pop();
return commits
.map(commit => {
commit.commit.subject = commit.commit.message.split('\n')[0];
const partial = template.replace(/(?<!\$)\{((?:[^{}]|\${[^}]+})+)\}/g, (_, block) => {
const rendered = format(block, commit);
return excludeMatches.some(match => rendered.includes(match)) ? '' : rendered;
});
return format(partial, commit);
})
.join('\n');
}
}
export default GitHub;

16
node_modules/release-it/lib/plugin/github/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import { format } from '../../util.js';
const message = context => {
const { isPreRelease, github } = context;
const { releaseName, update } = github;
const name = format(releaseName, context);
return `${update ? 'Update' : 'Create a'} ${isPreRelease ? 'pre-' : ''}release on GitHub (${name})?`;
};
export default {
release: {
type: 'confirm',
message,
default: true
}
};

42
node_modules/release-it/lib/plugin/github/util.js generated vendored Normal file
View File

@@ -0,0 +1,42 @@
// Totally much borrowed from https://github.com/semantic-release/github/blob/master/lib/success.js
import issueParser from 'issue-parser';
/** @internal */
export const getSearchQueries = (base, commits, separator = '+') => {
const encodedSeparator = encodeURIComponent(separator);
return commits.reduce((searches, commit) => {
const lastSearch = searches[searches.length - 1];
if (lastSearch && encodeURIComponent(lastSearch).length + commit.length <= 256 - encodedSeparator.length) {
searches[searches.length - 1] = `${lastSearch}${separator}${commit}`;
} else {
searches.push(`${base}${separator}${commit}`);
}
return searches;
}, []);
};
export const searchQueries = (client, owner, repo, shas) =>
getSearchQueries(`repo:${owner}/${repo}+type:pr+is:merged`, shas).map(
async q => (await client.search.issuesAndPullRequests({ q, advanced_search: true })).data.items
);
export const getCommitsFromChangelog = changelog => {
const regex = /\(([a-f0-9]{7,})\)/i;
return changelog.split('\n').flatMap(message => {
const match = message.match(regex);
if (match) return match[1];
return [];
});
};
export const getResolvedIssuesFromChangelog = (host, owner, repo, changelog) => {
const parser = issueParser('github', { hosts: [host] });
return changelog
.split('\n')
.map(parser)
.flatMap(parsed => parsed.actions.close)
.filter(action => !action.slug || action.slug === `${owner}/${repo}`)
.map(action => ({ type: 'issue', number: parseInt(action.issue, 10) }));
};

338
node_modules/release-it/lib/plugin/gitlab/GitLab.js generated vendored Normal file
View File

@@ -0,0 +1,338 @@
import path from 'node:path';
import fs from 'node:fs'; // import fs here so it can be stubbed in tests
import { readFile } from 'node:fs/promises';
import { glob } from 'tinyglobby';
import { Agent } from 'undici';
import Release from '../GitRelease.js';
import { format, e, castArray } from '../../util.js';
import prompts from './prompts.js';
const docs = 'https://git.io/release-it-gitlab';
const noop = Promise.resolve();
class GitLab extends Release {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
this.assets = [];
const { secure } = this.options;
const certificateAuthorityFileRef = this.options.certificateAuthorityFileRef || 'CI_SERVER_TLS_CA_FILE';
const certificateAuthorityFile =
this.options.certificateAuthorityFile || process.env[certificateAuthorityFileRef] || null;
this.certificateAuthorityOption = {};
const needsCustomAgent = Boolean(secure === false || certificateAuthorityFile);
if (needsCustomAgent) {
this.certificateAuthorityOption.dispatcher = new Agent({
connect: {
rejectUnauthorized: secure,
ca: certificateAuthorityFile ? fs.readFileSync(certificateAuthorityFile) : undefined
}
});
}
}
async init() {
await super.init();
const { skipChecks, tokenRef, tokenHeader } = this.options;
const { repo } = this.getContext();
const hasJobToken = (tokenHeader || '').toLowerCase() === 'job-token';
const origin = this.options.origin || `https://${repo.host}`;
this.setContext({
id: encodeURIComponent(repo.repository),
origin,
baseUrl: `${origin}/api/v4`
});
if (skipChecks) return;
if (!this.token) {
throw e(`Environment variable "${tokenRef}" is required for GitLab releases.`, docs);
}
if (!hasJobToken) {
if (!(await this.isAuthenticated())) {
throw e(`Could not authenticate with GitLab using environment variable "${tokenRef}".`, docs);
}
if (!(await this.isCollaborator())) {
const { user, repo } = this.getContext();
throw e(`User ${user.username} is not a collaborator for ${repo.repository}.`, docs);
}
}
}
async isAuthenticated() {
if (this.config.isDryRun) return true;
const endpoint = `user`;
try {
const { id, username } = await this.request(endpoint, { method: 'GET' });
this.setContext({ user: { id, username } });
return true;
} catch (err) {
this.debug(err);
return false;
}
}
async isCollaborator() {
if (this.config.isDryRun) return true;
const { id, user } = this.getContext();
const endpoint = `projects/${id}/members/all/${user.id}`;
try {
const { access_level } = await this.request(endpoint, { method: 'GET' });
return access_level && access_level >= 30;
} catch (err) {
this.debug(err);
return false;
}
}
async beforeRelease() {
await super.beforeRelease();
await this.checkReleaseMilestones();
}
async checkReleaseMilestones() {
if (this.options.skipChecks) return;
const releaseMilestones = this.getReleaseMilestones();
if (releaseMilestones.length < 1) {
return;
}
this.log.exec(`gitlab releases#checkReleaseMilestones`);
const { id } = this.getContext();
const endpoint = `projects/${id}/milestones`;
const requests = releaseMilestones.map(milestone => {
const options = {
method: 'GET',
searchParams: {
title: milestone,
include_parent_milestones: true
}
};
return this.request(endpoint, options).then(response => {
if (!Array.isArray(response)) {
const { baseUrl } = this.getContext();
throw new Error(
`Unexpected response from ${baseUrl}/${endpoint}. Expected an array but got: ${JSON.stringify(response)}`
);
}
if (response.length === 0) {
const error = new Error(`Milestone '${milestone}' does not exist.`);
this.log.warn(error.message);
throw error;
}
this.log.verbose(`gitlab releases#checkReleaseMilestones: milestone '${milestone}' exists`);
});
});
try {
await Promise.allSettled(requests).then(results => {
for (const result of results) {
if (result.status === 'rejected') {
throw e('Missing one or more milestones in GitLab. Creating a GitLab release will fail.', docs);
}
}
});
} catch (err) {
this.debug(err);
throw err;
}
this.log.verbose('gitlab releases#checkReleaseMilestones: done');
}
getReleaseMilestones() {
const { milestones } = this.options;
return (milestones || []).map(milestone => format(milestone, this.config.getContext()));
}
async release() {
const glRelease = () => this.createRelease();
const glUploadAssets = () => this.uploadAssets();
if (this.config.isCI) {
await this.step({ enabled: this.options.assets, task: glUploadAssets, label: 'GitLab upload assets' });
return await this.step({ task: glRelease, label: 'GitLab release' });
} else {
const release = () => glUploadAssets().then(() => glRelease());
return await this.step({ task: release, label: 'GitLab release', prompt: 'release' });
}
}
async request(endpoint, options) {
const { baseUrl } = this.getContext();
this.debug(Object.assign({ url: `${baseUrl}/${endpoint}` }, options));
const method = (options.method || 'POST').toLowerCase();
const { tokenHeader } = this.options;
const url = `${baseUrl}/${endpoint}${options.searchParams ? `?${new URLSearchParams(options.searchParams)}` : ''}`;
const headers = {
'user-agent': 'webpro/release-it',
[tokenHeader]: this.token
};
// When using fetch() with FormData bodies, we should not set the Content-Type header.
// See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects#sending_files_using_a_formdata_object
if (!(options.body instanceof FormData)) {
headers['Content-Type'] = typeof options.json !== 'undefined' ? 'application/json' : 'text/plain';
}
const requestOptions = {
method,
headers,
...this.certificateAuthorityOption
};
try {
const response = await fetch(
url,
options.json || options.body
? {
...requestOptions,
body: options.json ? JSON.stringify(options.json) : options.body
}
: requestOptions
);
if (!response.ok) {
const body = await response.json();
throw new Error(body.error);
}
const body = await response.json();
this.debug(body);
return body;
} catch (err) {
this.debug(err);
throw err;
}
}
async createRelease() {
const { releaseName } = this.options;
const { tagName, branchName, git: { tagAnnotation } = {} } = this.config.getContext();
const { id, releaseNotes, repo, origin } = this.getContext();
const { isDryRun } = this.config;
const name = format(releaseName, this.config.getContext());
const tagMessage = format(tagAnnotation, this.config.getContext());
const description = releaseNotes || '-';
const releaseUrl = `${origin}/${repo.repository}/-/releases/${tagName}`;
const releaseMilestones = this.getReleaseMilestones();
this.log.exec(`gitlab releases#createRelease "${name}" (${tagName})`, { isDryRun });
if (isDryRun) {
this.setContext({ isReleased: true, releaseUrl });
return true;
}
const endpoint = `projects/${id}/releases`;
const options = {
json: {
name,
ref: branchName,
tag_name: tagName,
tag_message: tagMessage,
description
}
};
if (this.assets.length) {
options.json.assets = {
links: this.assets
};
}
if (releaseMilestones.length) {
options.json.milestones = releaseMilestones;
}
try {
const body = await this.request(endpoint, options);
const releaseUrlSelf = body._links?.self ?? releaseUrl;
this.log.verbose('gitlab releases#createRelease: done');
this.setContext({ isReleased: true, releaseUrl: releaseUrlSelf });
this.config.setContext({ isReleased: true, releaseUrl: releaseUrlSelf });
return true;
} catch (err) {
this.debug(err);
throw err;
}
}
async uploadAsset(filePath) {
const name = path.basename(filePath);
const { useIdsForUrls, useGenericPackageRepositoryForAssets, genericPackageRepositoryName } = this.options;
const { id, origin, repo, version, baseUrl } = this.getContext();
const endpoint = useGenericPackageRepositoryForAssets
? `projects/${id}/packages/generic/${genericPackageRepositoryName}/${version}/${name}`
: `projects/${id}/uploads`;
if (useGenericPackageRepositoryForAssets) {
const options = {
method: 'PUT',
body: await readFile(filePath)
};
try {
const body = await this.request(endpoint, options);
if (!(body.message && body.message == '201 Created')) {
throw new Error(`GitLab asset upload failed`);
}
this.log.verbose(`gitlab releases#uploadAsset: done (${endpoint})`);
this.assets.push({
name,
url: `${baseUrl}/${endpoint}`
});
} catch (err) {
this.debug(err);
throw err;
}
} else {
const body = new FormData();
const rawData = await readFile(filePath);
body.set('file', new Blob([rawData]), name);
const options = { body };
try {
const body = await this.request(endpoint, options);
this.log.verbose(`gitlab releases#uploadAsset: done (${body.url})`);
this.assets.push({
name,
url: useIdsForUrls ? `${origin}${body.full_path}` : `${origin}/${repo.repository}${body.url}`
});
} catch (err) {
this.debug(err);
throw err;
}
}
}
uploadAssets() {
const { assets } = this.options;
const { isDryRun } = this.config;
const context = this.config.getContext();
const patterns = castArray(assets).map(pattern => format(pattern, context));
this.log.exec('gitlab releases#uploadAssets', patterns, { isDryRun });
if (!assets) {
return noop;
}
return glob(patterns).then(files => {
if (!files.length) {
this.log.warn(`gitlab releases#uploadAssets: could not find "${assets}" relative to ${process.cwd()}`);
}
if (isDryRun) return Promise.resolve();
return Promise.all(files.map(filePath => this.uploadAsset(filePath)));
});
}
}
export default GitLab;

9
node_modules/release-it/lib/plugin/gitlab/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import { format } from '../../util.js';
export default {
release: {
type: 'confirm',
message: context => `Create a release on GitLab (${format(context.gitlab.releaseName, context)})?`,
default: true
}
};

284
node_modules/release-it/lib/plugin/npm/npm.js generated vendored Normal file
View File

@@ -0,0 +1,284 @@
import path from 'node:path';
import semver from 'semver';
import urlJoin from 'url-join';
import Plugin from '../Plugin.js';
import { hasAccess, rejectAfter, parseVersion, readJSON, e, fixArgs } from '../../util.js';
import prompts from './prompts.js';
const docs = 'https://git.io/release-it-npm';
const options = { write: false };
const MANIFEST_PATH = './package.json';
const DEFAULT_TAG = 'latest';
const DEFAULT_TAG_PRERELEASE = 'next';
const NPM_BASE_URL = 'https://www.npmjs.com';
const NPM_PUBLIC_PATH = '/package';
const DEFAULT_TIMEOUT = 10;
class npm extends Plugin {
static isEnabled(options) {
return hasAccess(MANIFEST_PATH) && options !== false;
}
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
async init() {
const { name, version: latestVersion, private: isPrivate, publishConfig } = readJSON(path.resolve(MANIFEST_PATH));
this.setContext({ name, latestVersion, private: isPrivate, publishConfig });
this.config.setContext({ npm: { name } });
const { publish, skipChecks } = this.options;
const timeout = Number(this.options.timeout ?? DEFAULT_TIMEOUT) * 1000;
if (publish === false || isPrivate) return;
if (skipChecks) return;
const validations = Promise.all([this.isRegistryUp(), this.isAuthenticated(), this.getLatestRegistryVersion()]);
await Promise.race([validations, rejectAfter(timeout, e(`Timed out after ${timeout}ms.`, docs))]);
const [isRegistryUp, isAuthenticated, latestVersionInRegistry] = await validations;
if (!isRegistryUp) {
throw e(`Unable to reach npm registry (timed out after ${timeout}ms).`, docs);
}
if (!isAuthenticated) {
throw e('Not authenticated with npm. Please `npm login` and try again.', docs);
}
if (!(await this.isCollaborator())) {
const { username } = this.getContext();
throw e(`User ${username} is not a collaborator for ${name}.`, docs);
}
if (!latestVersionInRegistry) {
this.log.warn('No version found in npm registry. Assuming new package.');
} else {
if (!semver.eq(latestVersion, latestVersionInRegistry)) {
this.log.warn(
`Latest version in registry (${latestVersionInRegistry}) does not match package.json (${latestVersion}).`
);
}
}
}
getName() {
return this.getContext('name');
}
getLatestVersion() {
return this.options.ignoreVersion ? null : this.getContext('latestVersion');
}
async bump(version) {
const tag = this.options.tag || (await this.resolveTag(version));
this.setContext({ version, tag });
if (!this.config.isIncrement) return false;
const { versionArgs, allowSameVersion } = this.options;
const args = [version, '--no-git-tag-version', allowSameVersion && '--allow-same-version', ...fixArgs(versionArgs)];
const task = () => this.exec(`npm version ${args.filter(Boolean).join(' ')}`);
return this.spinner.show({ task, label: 'npm version' });
}
release() {
if (this.options.publish === false) return false;
if (this.getContext('private')) return false;
const publish = () => this.publish({ otpCallback });
const otpCallback =
this.config.isCI && !this.config.isPromptOnlyVersion ? null : task => this.step({ prompt: 'otp', task });
return this.step({ task: publish, label: 'npm publish', prompt: 'publish' });
}
isRegistryUp() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
return this.exec(`npm ping${registryArg}`, { options }).then(
() => true,
err => {
if (/code E40[04]|404.*(ping not found|No content for path)/.test(err)) {
this.log.warn('Ignoring response from unsupported `npm ping` command.');
return true;
}
return false;
}
);
}
isAuthenticated() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
return this.exec(`npm whoami${registryArg}`, { options }).then(
output => {
const username = output ? output.trim() : null;
this.setContext({ username });
return true;
},
err => {
this.debug(err);
if (/code E40[04]/.test(err)) {
this.log.warn('Ignoring response from unsupported `npm whoami` command.');
return true;
}
return false;
}
);
}
async isCollaborator() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
const name = this.getName();
const { username } = this.getContext();
if (username === undefined) return true;
if (username === null) return false;
try {
let npmVersion = await this.exec('npm --version', { options });
let accessCommand;
if (semver.gt(npmVersion, '9.0.0')) {
accessCommand = 'npm access list collaborators --json';
} else {
accessCommand = 'npm access ls-collaborators';
}
const output = await this.exec(`${accessCommand} ${name}${registryArg}`, { options });
try {
const collaborators = JSON.parse(output);
const permissions = collaborators[username];
return permissions && permissions.includes('write');
} catch (err) {
this.debug(err);
return false;
}
} catch (err) {
this.debug(err);
if (/code E400/.test(err)) {
this.log.warn('Ignoring response from unsupported `npm access` command.');
} else {
this.log.warn(`Unable to verify if user ${username} is a collaborator for ${name}.`);
}
return true;
}
}
async getLatestRegistryVersion() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
const name = this.getName();
const latestVersion = this.getLatestVersion();
const tag = await this.resolveTag(latestVersion);
return this.exec(`npm show ${name}@${tag} version${registryArg}`, { options }).catch(() => null);
}
getRegistryPreReleaseTags() {
return this.exec(`npm view ${this.getName()} dist-tags --json`, { options }).then(
output => {
try {
const tags = JSON.parse(output);
return Object.keys(tags).filter(tag => tag !== DEFAULT_TAG);
} catch (err) {
this.debug(err);
return [];
}
},
() => []
);
}
getPackageUrl() {
const baseUrl = this.getRegistry() || NPM_BASE_URL;
const publicPath = this.getPublicPath() || NPM_PUBLIC_PATH;
return urlJoin(baseUrl, publicPath, this.getName());
}
getRegistry() {
const { publishConfig } = this.getContext();
const registries = publishConfig
? publishConfig.registry
? [publishConfig.registry]
: Object.keys(publishConfig)
.filter(key => key.endsWith('registry'))
.map(key => publishConfig[key])
: [];
return registries[0];
}
getPublicPath() {
const { publishConfig } = this.getContext();
return (publishConfig && publishConfig.publicPath) ?? '';
}
async guessPreReleaseTag() {
const [tag] = await this.getRegistryPreReleaseTags();
if (tag) {
return tag;
} else {
this.log.warn(`Unable to get pre-release tag(s) from npm registry. Using "${DEFAULT_TAG_PRERELEASE}".`);
return DEFAULT_TAG_PRERELEASE;
}
}
async resolveTag(version) {
const { tag } = this.options;
const { isPreRelease, preReleaseId } = parseVersion(version);
if (!isPreRelease) {
return DEFAULT_TAG;
} else {
return tag || preReleaseId || (await this.guessPreReleaseTag());
}
}
async publish({ otp = this.options.otp, otpCallback } = {}) {
const { publishPath = '.', publishArgs } = this.options;
const { private: isPrivate, tag = DEFAULT_TAG } = this.getContext();
const otpArg = otp ? `--otp ${otp}` : '';
const dryRunArg = this.config.isDryRun ? '--dry-run' : '';
const registry = this.getRegistry();
const registryArg = registry ? `--registry ${registry}` : '';
if (isPrivate) {
this.log.warn('Skip publish: package is private.');
return false;
}
const args = [publishPath, `--tag ${tag}`, otpArg, dryRunArg, registryArg, ...fixArgs(publishArgs)].filter(Boolean);
return this.exec(`npm publish ${args.join(' ')}`, { options })
.then(() => {
this.setContext({ isReleased: true });
})
.catch(err => {
this.debug(err);
if (this.config.isDryRun && /publish over the previously published version/.test(err)) {
return Promise.resolve();
}
if (/one-time pass/.test(err)) {
if (otp != null) {
this.log.warn('The provided OTP is incorrect or has expired.');
}
if (otpCallback) {
return otpCallback(otp => this.publish({ otp, otpCallback }));
}
}
throw err;
});
}
afterRelease() {
const { isReleased } = this.getContext();
if (isReleased) {
this.log.log(`🔗 ${this.getPackageUrl()}`);
}
}
}
export default npm;

12
node_modules/release-it/lib/plugin/npm/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export default {
publish: {
type: 'confirm',
message: context =>
`Publish ${context.npm.name}${context.npm.tag === 'latest' ? '' : `@${context.npm.tag}`} to npm?`,
default: true
},
otp: {
type: 'input',
message: () => `Please enter OTP for npm:`
}
};

131
node_modules/release-it/lib/plugin/version/Version.js generated vendored Normal file
View File

@@ -0,0 +1,131 @@
import { styleText } from 'node:util';
import semver from 'semver';
import Plugin from '../Plugin.js';
const RELEASE_TYPES = ['patch', 'minor', 'major'];
const PRERELEASE_TYPES = ['prepatch', 'preminor', 'premajor'];
const CONTINUATION_TYPES = ['prerelease', 'pre'];
const ALL_RELEASE_TYPES = [...RELEASE_TYPES, ...PRERELEASE_TYPES, ...CONTINUATION_TYPES];
const CHOICES = {
latestIsPreRelease: [CONTINUATION_TYPES[0], ...RELEASE_TYPES],
preRelease: PRERELEASE_TYPES,
default: [...RELEASE_TYPES, ...PRERELEASE_TYPES]
};
const getIncrementChoices = context => {
const { latestIsPreRelease, isPreRelease, preReleaseId, preReleaseBase } = context.version;
const types = latestIsPreRelease ? CHOICES.latestIsPreRelease : isPreRelease ? CHOICES.preRelease : CHOICES.default;
const choices = types.map(increment => ({
name: `${increment} (${semver.inc(context.latestVersion, increment, preReleaseId, preReleaseBase)})`,
value: increment
}));
const otherChoice = {
name: 'Other, please specify...',
value: null
};
return [...choices, otherChoice];
};
const versionTransformer = context => input =>
semver.valid(input)
? semver.gt(input, context.latestVersion)
? styleText('green', input)
: styleText('red', input)
: styleText(['red', 'bold'], input);
const prompts = {
incrementList: {
type: 'list',
message: () => 'Select increment (next version):',
choices: context => getIncrementChoices(context),
pageSize: 9
},
version: {
type: 'input',
message: () => `Please enter a valid version:`,
transformer: context => versionTransformer(context),
validate: input => !!semver.valid(input) || 'The version must follow the semver standard.'
}
};
class Version extends Plugin {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
getIncrement(options) {
return options.increment;
}
getIncrementedVersionCI(options) {
return this.incrementVersion(options);
}
async getIncrementedVersion(options) {
const { isCI } = this.config;
const version = this.incrementVersion(options);
return version || (isCI ? null : await this.promptIncrementVersion(options));
}
promptIncrementVersion(options) {
return new Promise(resolve => {
this.step({
prompt: 'incrementList',
task: increment =>
increment
? resolve(this.incrementVersion(Object.assign({}, options, { increment })))
: this.step({ prompt: 'version', task: resolve })
});
});
}
isPreRelease(version) {
return Boolean(semver.prerelease(version));
}
isValid(version) {
return Boolean(semver.valid(version));
}
incrementVersion({ latestVersion, increment, isPreRelease, preReleaseId, preReleaseBase }) {
if (increment === false) return latestVersion;
const latestIsPreRelease = this.isPreRelease(latestVersion);
const isValidVersion = this.isValid(increment);
if (latestVersion) {
this.setContext({ latestIsPreRelease });
}
if (isValidVersion && semver.gte(increment, latestVersion)) {
return increment;
}
if (isPreRelease && !increment && latestIsPreRelease) {
return semver.inc(latestVersion, 'prerelease', preReleaseId, preReleaseBase);
}
if (this.config.isCI && !increment) {
if (isPreRelease) {
return semver.inc(latestVersion, 'prepatch', preReleaseId, preReleaseBase);
} else {
return semver.inc(latestVersion, 'patch');
}
}
const normalizedType = RELEASE_TYPES.includes(increment) && isPreRelease ? `pre${increment}` : increment;
if (ALL_RELEASE_TYPES.includes(normalizedType)) {
return semver.inc(latestVersion, normalizedType, preReleaseId, preReleaseBase);
}
const coercedVersion = !isValidVersion && semver.coerce(increment);
if (coercedVersion) {
this.log.warn(`Coerced invalid semver version "${increment}" into "${coercedVersion}".`);
return coercedVersion.toString();
}
}
}
export default Version;

33
node_modules/release-it/lib/prompt.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
import inquirer from 'inquirer';
class Prompt {
constructor({ container }) {
this.createPrompt = (container.inquirer || inquirer).prompt;
this.prompts = {};
}
register(pluginPrompts, namespace = 'default') {
this.prompts[namespace] = this.prompts[namespace] || {};
Object.assign(this.prompts[namespace], pluginPrompts);
}
async show({ enabled = true, prompt: promptName, namespace = 'default', task, context }) {
if (!enabled) return false;
const prompt = this.prompts[namespace][promptName];
const options = Object.assign({}, prompt, {
name: promptName,
message: prompt.message(context),
choices: 'choices' in prompt && prompt.choices(context),
transformer: 'transformer' in prompt ? prompt.transformer(context) : undefined
});
const answers = await this.createPrompt([options]);
const doExecute = prompt.type === 'confirm' ? answers[promptName] : true;
return doExecute && task ? await task(answers[promptName]) : false;
}
}
export default Prompt;

120
node_modules/release-it/lib/shell.js generated vendored Normal file
View File

@@ -0,0 +1,120 @@
import util from 'node:util';
import { spawn, exec } from 'node:child_process';
import { format } from './util.js';
const debug = util.debug('release-it:shell');
const noop = Promise.resolve();
class Shell {
constructor({ container }) {
this.log = container.log;
this.config = container.config;
this.cache = new Map();
}
exec(command, options = {}, context = {}) {
if (!command || !command.length) return;
return typeof command === 'string'
? this.execFormattedCommand(format(command, context), options)
: this.execFormattedCommand(command, options);
}
async execFormattedCommand(command, options = {}) {
const { isDryRun } = this.config;
const isWrite = options.write !== false;
const isExternal = options.external === true;
const cacheKey = typeof command === 'string' ? command : command.join(' ');
const isCached = !isExternal && this.cache.has(cacheKey);
if (isDryRun && isWrite) {
this.log.exec(command, { isDryRun });
return noop;
}
this.log.exec(command, { isExternal, isCached });
if (isCached) {
return this.cache.get(cacheKey);
}
const result =
typeof command === 'string'
? this.execStringCommand(command, options, { isExternal })
: this.execWithArguments(command, options, { isExternal });
if (!isExternal && !this.cache.has(cacheKey)) {
this.cache.set(cacheKey, result);
}
return result;
}
execStringCommand(command, options, { isExternal }) {
return new Promise((resolve, reject) => {
const proc = exec(command, (err, stdout, stderr) => {
stdout = stdout.toString().trimEnd();
const code = !err ? 0 : err === 'undefined' ? 1 : err.code;
debug({ command, options, code, stdout, stderr });
if (code === 0) {
resolve(stdout);
} else {
reject(new Error(stderr || stdout));
}
});
proc.stdout.on('data', stdout => this.log.verbose(stdout.toString().trimEnd(), { isExternal }));
proc.stderr.on('data', stderr => this.log.verbose(stderr.toString().trimEnd(), { isExternal }));
});
}
async execWithArguments(command, options = {}, { isExternal } = {}) {
const [program, ...programArgs] = command;
try {
return await new Promise((resolve, reject) => {
const proc = spawn(program, programArgs, {
// we want to capture all output from the process so the extra 2 pipe
stdio: ['inherit', 'pipe', 'pipe'],
...options
});
let stdout = '';
let stderr = '';
proc.stdout.on('data', data => {
stdout += data.toString();
});
proc.stderr.on('data', data => {
stderr += data.toString();
});
proc.on('close', code => {
stdout = stdout === '""' ? '' : stdout;
this.log.verbose(stdout, { isExternal });
debug({ command, options, stdout, stderr });
if (code === 0) {
resolve((stdout || stderr).trim());
} else {
if (stdout) {
this.log.log(`\n${stdout}`);
}
debug({ code, command, options, stdout, stderr });
reject(new Error(stderr || stdout || `Process exited with code ${code}`));
}
});
proc.on('error', err => {
debug(err);
reject(new Error(err.message));
});
});
} catch (err) {
debug(err);
return Promise.reject(err);
}
}
}
export default Shell;

29
node_modules/release-it/lib/spinner.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
import { oraPromise } from 'ora';
import { format } from './util.js';
const noop = Promise.resolve();
class Spinner {
constructor({ container = {} } = {}) {
this.config = container.config;
this.ora = container.ora || oraPromise;
}
show({ enabled = true, task, label, external = false, context }) {
if (!enabled) return noop;
const { config } = this;
this.isSpinnerDisabled = !config.isCI || config.isVerbose || config.isDryRun || config.isDebug;
this.canForce = !config.isCI && !config.isVerbose && !config.isDryRun && !config.isDebug;
const awaitTask = task();
if (!this.isSpinnerDisabled || (external && this.canForce)) {
const text = format(label, context);
this.ora(awaitTask, text);
}
return awaitTask;
}
}
export default Spinner;

206
node_modules/release-it/lib/util.js generated vendored Normal file
View File

@@ -0,0 +1,206 @@
import fs, { close, openSync, statSync, utimesSync, accessSync } from 'node:fs'; // need import fs here due to test stubbing
import util from 'node:util';
import { EOL } from 'node:os';
import gitUrlParse from 'git-url-parse';
import semver from 'semver';
import osName from 'os-name';
import { Eta } from 'eta';
import Log from './log.js';
const debug = util.debug('release-it:shell');
const eta = new Eta({
autoEscape: false,
useWith: true,
tags: ['${', '}'],
parse: { interpolate: '' },
rmWhitespace: false,
autoTrim: false
});
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
const before = (n, func) => {
var result;
if (typeof func != 'function') {
throw new TypeError('Missing argument for `func`');
}
n = parseInt(n);
return function () {
if (--n > 0) {
result = func.apply(this, arguments);
}
if (n <= 1) {
func = undefined;
}
return result;
};
};
const tryStatFile = filePath => {
try {
return statSync(filePath);
} catch (e) {
debug(e);
return null;
}
};
/** @internal */
export const execOpts = {
stdio: process.env.NODE_DEBUG && process.env.NODE_DEBUG.indexOf('release-it') === 0 ? 'pipe' : []
};
export const readJSON = file => JSON.parse(fs.readFileSync(file, 'utf8'));
const pkg = readJSON(new URL('../package.json', import.meta.url));
export const getSystemInfo = () => {
return {
'release-it': pkg.version,
node: process.version,
os: osName()
};
};
export const format = (template = '', context = {}) => {
if (!context || context === null || !template || template === null || template.indexOf('${') === -1) return template;
const log = new Log();
try {
return eta.renderString(template, context);
} catch (error) {
log.error(`Unable to render template with context:\n${template}\n${JSON.stringify(context)}`);
log.error(error);
throw error;
}
};
export const truncateLines = (input, maxLines = 10, surplusText = null) => {
const lines = input.split(EOL);
const surplus = lines.length - maxLines;
const output = lines.slice(0, maxLines).join(EOL);
return surplus > 0 ? (surplusText ? `${output}${surplusText}` : `${output}${EOL}...and ${surplus} more`) : output;
};
export const rejectAfter = (ms, error) =>
wait(ms).then(() => {
throw error;
});
export const parseGitUrl = remoteUrl => {
if (!remoteUrl) return { host: null, owner: null, project: null, protocol: null, remote: null, repository: null };
const normalizedUrl = (remoteUrl || '')
.replace(/^[A-Z]:\\\\/, 'file://') // Assume file protocol for Windows drive letters
.replace(/^\//, 'file://') // Assume file protocol if only /path is given
.replace(/\\+/g, '/'); // Replace forward with backslashes
const parsedUrl = gitUrlParse(normalizedUrl);
const { resource: host, name: project, protocol, href: remote } = parsedUrl;
const owner = protocol === 'file' ? parsedUrl.owner.split('/').at(-1) : parsedUrl.owner; // Fix owner for file protocol
const repository = `${owner}/${project}`;
return { host, owner, project, protocol, remote, repository };
};
export const reduceUntil = async (collection, fn) => {
let result;
for (const item of collection) {
if (result) break;
result = await fn(item);
}
return result;
};
export const hasAccess = path => {
try {
accessSync(path);
return true;
} catch (err) {
return false;
}
};
export const parseVersion = raw => {
if (raw == null) return { version: raw, isPreRelease: false, preReleaseId: null };
const version = semver.valid(raw) ? raw : semver.coerce(raw);
if (!version) return { version: raw, isPreRelease: false, preReleaseId: null };
const parsed = semver.parse(version);
const isPreRelease = parsed.prerelease.length > 0;
const preReleaseId = isPreRelease && isNaN(parsed.prerelease[0]) ? parsed.prerelease[0] : null;
return {
version: version.toString(),
isPreRelease,
preReleaseId
};
};
export const e = (message, docs, fail = true) => {
const error = new Error(docs ? `${message}${EOL}Documentation: ${docs}${EOL}` : message);
error.code = fail ? 1 : 0;
error.cause = fail ? 'ERROR' : 'INFO';
return error;
};
/** @internal */
export const touch = (path, callback) => {
const stat = tryStatFile(path);
if (stat && stat.isDirectory()) {
// don't error just exit
return;
}
const fd = openSync(path, 'a');
close(fd);
const now = new Date();
const mtime = now;
const atime = now;
utimesSync(path, atime, mtime);
if (callback) {
callback();
}
};
export const fixArgs = args => (args ? (typeof args === 'string' ? args.split(' ') : args) : []);
export const upperFirst = string => {
return string ? string.charAt(0).toUpperCase() + string.slice(1) : '';
};
export const castArray = arr => {
return Array.isArray(arr) ? arr : [arr];
};
export const once = fn => {
return before(2, fn);
};
export const pick = (object, keys) => {
return keys.reduce((obj, key) => {
if (object && Object.prototype.hasOwnProperty.call(object, key)) {
obj[key] = object[key];
}
return obj;
}, {});
};
const parsePath = path => {
const result = [];
const regex = /[^.[\]]+|\[(?:(\d+)|["'](.+?)["'])\]/g;
let match;
while ((match = regex.exec(path)) !== null) {
result.push(match[1] ?? match[2] ?? match[0]);
}
return result;
};
export const get = (obj, path, defaultValue = undefined) => {
if (!path || typeof path !== 'string') {
return defaultValue;
}
try {
const keys = parsePath(path);
return keys.reduce((acc, key) => acc?.[key], obj) ?? defaultValue;
} catch {
return defaultValue;
}
};