@142vip/release-version
Version:
通用型版本迭代Cli工具,支持版本迭代更新,Git提交信息、打标记并推送到远程
276 lines (269 loc) • 10.8 kB
JavaScript
import { VipConsole, VipSymbols, VipColor, VipNodeJS, VipExecutor, VipNpm, VipPackageJSON, vipLogger, VipInquirer } from '@142vip/utils';
import { VersionProgressEventEnum, VersionHooksEnum } from '../enums/index.mjs';
async function updateChangelogDoc(operation) {
if (operation.options.changelog) {
VipConsole.log(`${VipSymbols.info} \u57FA\u4E8E ${VipColor.greenBright("@142vip/changelog")} \u6A21\u5757\u751F\u6210CHANGELOG.md\u6587\u6863\uFF0C\u6267\u884C\u547D\u4EE4\uFF1A ${operation.options.execute}`);
try {
const filePath = VipNodeJS.pathJoin(operation.options.cwd, "CHANGELOG.md");
const baseCommand = `npx changelog --output "${filePath}" --name v${operation.state.newVersion}`;
await VipExecutor.execShell(operation.options.scopeName != null ? { command: `${baseCommand} --scopeName ${operation.options.scopeName}`, description: `MonoRepo\u6A21\u5F0F\uFF0C\u751F\u6210 ${VipColor.greenBright(operation.options.scopeName)} \u6A21\u5757\u7684CHANGELOG\u6587\u6863` } : { command: baseCommand, description: "\u666E\u901A\u6A21\u5F0F\uFF0C\u751F\u6210CHANGELOG\u6587\u6863" });
} catch (e) {
VipConsole.log(`${VipSymbols.error} \u751F\u6210CHANGELOG.md\u6587\u6863\u65F6\u53D1\u751F\u9519\u8BEF`);
VipConsole.error(e);
VipNodeJS.existErrorProcess();
}
VipConsole.log(`${VipSymbols.success} \u751F\u6210CHANGELOG.md\u6587\u6863\u7ED3\u675F`);
}
}
async function gitCommit(operation) {
if (!operation.options.commit)
return operation;
const commitOptions = operation.options.commit;
const { newVersion } = operation.state;
const args = ["--allow-empty"];
if (commitOptions.all) {
args.push("--all");
}
if (commitOptions.skipGitVerify) {
args.push("--no-verify");
}
const commitMessage = VipNpm.formatVersionStr(commitOptions.message, newVersion);
args.push("--message", `'${commitMessage}'`);
await VipExecutor.execShell({ command: `git commit ${args.join(" ")}`, description: "\u63D0\u4EA4git commit\u4FE1\u606F" });
return operation.update({ event: VersionProgressEventEnum.GitCommit, commitMessage });
}
async function gitTag(operation) {
if (!operation.options.tag)
return operation;
const { commit, tag } = operation.options;
const { newVersion } = operation.state;
const args = [
// Create an annotated tag, which is recommended for releases.
// See https://git-scm.com/docs/git-tag
"--annotate",
// Use the same commit message for the tag
"--message",
// 注意格式
`'${VipNpm.formatVersionStr(commit.message, newVersion)}'`
];
const tagName = VipNpm.formatVersionStr(tag.name, newVersion);
args.push(tagName);
await VipExecutor.execShell({ command: `git tag ${args.join(" ")}`, description: "\u521B\u5EFATag\u6807\u7B7E" });
return operation.update({ event: VersionProgressEventEnum.GitTag, tagName });
}
async function gitPush(operation) {
if (!operation.options.push)
return operation;
await VipExecutor.execShell({ command: "git push", description: "\u63A8\u9001\u53D8\u66F4" });
if (operation.options.tag) {
await VipExecutor.execShell({ command: "git push --tags", description: "\u63A8\u9001\u6240\u6709\u6807\u7B7E" });
}
return operation.update({ event: VersionProgressEventEnum.GitPush });
}
async function getNewVersion(operation, preid) {
const { currentVersion } = operation.state;
const newVersion = await VipPackageJSON.promptReleaseVersion(currentVersion, preid);
return operation.update({ newVersion });
}
async function getCurrentVersion(operation, options) {
if (options.currentVersion == null) {
const currentVersion = VipPackageJSON.getCurrentVersion(options.cwd);
const file = VipPackageJSON.getPackagePath(options.cwd);
if (currentVersion != null) {
operation.update({ currentVersionSource: file, currentVersion });
} else {
vipLogger.logByBlank(`\u65E0\u6CD5\u4ECE\u9879\u76EE\u4E2D\u83B7\u53D6\u5F53\u524D\u7248\u672C\u53F7\uFF0C \u68C0\u67E5\u6587\u4EF6: ${file}.`);
}
}
}
function updateVersion(operation) {
VipPackageJSON.updateVersion(operation.state.newVersion, operation.options.cwd);
}
async function runScript(script, operation) {
const { cwd, ignoreScripts } = operation.options;
if (!ignoreScripts) {
const manifest = VipPackageJSON.getPackageJSON(cwd);
if (VipPackageJSON.isPackageJSON(manifest) && VipPackageJSON.hasScript(manifest, script)) {
const existNPM = await VipNpm.isExistNpm();
if (!existNPM) {
vipLogger.log(VipColor.red("\u672A\u5B89\u88C5npm\uFF0C\u8BF7\u5148\u5B89\u88C5node.js\u73AF\u5883\u3002"));
VipNodeJS.existErrorProcess();
}
await VipExecutor.execShell({ command: `npm run ${script} --silent`, description: "\u8FD0\u884C\u811A\u672C\u547D\u4EE4" });
operation.update({ event: VersionProgressEventEnum.NpmScript, script });
}
}
return operation;
}
async function runPreVersionScript(operation) {
await runScript(VersionHooksEnum.PreVersion, operation);
}
async function runVersionScript(operation) {
await runScript(VersionHooksEnum.Version, operation);
}
async function runPostVersionScript(operation) {
await runScript(VersionHooksEnum.PostVersion, operation);
}
function showProgress(progress) {
switch (progress.event) {
case VersionProgressEventEnum.GitCommit:
VipConsole.log(`${VipSymbols.success} Git commit`);
break;
case VersionProgressEventEnum.GitTag:
VipConsole.log(`${VipSymbols.success} Git tag`);
break;
case VersionProgressEventEnum.GitPush:
VipConsole.log(`${VipSymbols.success} Git push`);
break;
case VersionProgressEventEnum.NpmScript:
VipConsole.log(`${VipSymbols.success} Npm run ${progress.script}`);
break;
}
}
async function transformToReleaseOperationOptions(options) {
let tag;
if (typeof options.tag === "string")
tag = { name: options.tag };
else if (options.tag)
tag = { name: "v" };
let commit;
if (typeof options.commit === "string")
commit = { all: !!options.all, skipGitVerify: !!options.skipGitVerify, message: options.commit };
else if (options.commit || tag || options.push)
commit = { all: !!options.all, skipGitVerify: !!options.skipGitVerify, message: "chore: release v" };
return {
commit,
tag,
push: !!options.push,
cwd: options.cwd ?? VipNodeJS.getProcessCwd(),
ignoreScripts: !!options.ignoreScripts,
execute: options.execute,
currentVersion: options.currentVersion,
changelog: !!options.changelog,
// 允许用户不输入
scopeName: options.scopeName
};
}
class ReleaseOperation {
/**
* The options for this operation.
*/
options;
/**
* The current state of the operation.
*/
state = {
release: void 0,
currentVersion: "",
currentVersionSource: "",
newVersion: "",
commitMessage: "",
tagName: ""
};
/**
* Private constructor. Use the `Operation.start()` static method instead.
*/
constructor(options) {
this.options = options;
if (options.currentVersion) {
this.update({ currentVersion: options.currentVersion, currentVersionSource: "user" });
}
}
/**
* The results of the operation.
*/
get results() {
const options = this.options;
const state = this.state;
return {
release: state.release,
currentVersion: state.currentVersion,
newVersion: state.newVersion,
commit: options.commit ? state.commitMessage : false,
tag: options.tag ? state.tagName : false
};
}
/**
* 开始一个全新的`versionBump()`操作
*/
static async start(input) {
const options = await transformToReleaseOperationOptions(input);
return new ReleaseOperation(options);
}
/**
* 更新操作状态和结果,并将更新后的进度报告给上层
*/
update({ event, script, ...newState }) {
Object.assign(this.state, newState);
if (event) {
showProgress({ event, script, ...this.results });
}
return this;
}
}
async function versionBump(options) {
console.log(111, options);
try {
const operation = await versionBumpDryRun(options);
await gitCommit(operation);
await gitTag(operation);
await runPostVersionScript(operation);
await gitPush(operation);
return operation.results;
} catch (e) {
vipLogger.error("\u7248\u672C\u53D1\u5E03\u5931\u8D25");
console.log(e);
}
}
async function versionBumpDryRun(options) {
const operation = await versionBumpInfo(options);
if (options.confirm) {
printSummary(operation);
await promptConfirmRelease();
}
await runPreVersionScript(operation);
updateVersion(operation);
await updateChangelogDoc(operation);
await doExecute(operation);
await runVersionScript(operation);
return operation;
}
async function versionBumpInfo(arg) {
const operation = await ReleaseOperation.start(arg);
await getCurrentVersion(operation, arg);
await getNewVersion(operation, arg.preid ?? "beta");
return operation;
}
function printSummary(operation) {
vipLogger.println();
if (operation.options.changelog) {
VipConsole.log(` generate CHANGELOG.md`);
}
if (operation.options.commit)
VipConsole.log(` commit ${VipColor.bold(VipNpm.formatVersionStr(operation.options.commit.message, operation.state.newVersion))}`);
if (operation.options.tag)
VipConsole.log(` tag ${VipColor.bold(VipNpm.formatVersionStr(operation.options.tag.name, operation.state.newVersion))}`);
if (operation.options.execute)
VipConsole.log(` execute ${VipColor.bold(operation.options.execute)}`);
if (operation.options.push)
VipConsole.log(` push ${VipColor.cyan(VipColor.bold("yes"))}`);
vipLogger.println();
VipConsole.log(` from ${VipColor.bold(operation.state.currentVersion)}`);
VipConsole.log(` to ${VipColor.green(VipColor.bold(operation.state.newVersion))}`);
vipLogger.println();
}
async function promptConfirmRelease() {
const isRelease = await VipInquirer.promptConfirm(`\u662F\u5426\u6267\u884C ${VipColor.redBright("bumpx")}\u547D\u4EE4\uFF0C\u5347\u7EA7\u7248\u672C\uFF1F`, false);
if (!isRelease) {
vipLogger.logByBlank(VipColor.green("\u7528\u6237\u53D6\u6D88\u64CD\u4F5C\uFF0C\u5B89\u5168\u9000\u51FA\uFF0C\u6B22\u8FCE\u4E0B\u6B21\u4F7F\u7528"));
VipNodeJS.existSuccessProcess();
}
}
async function doExecute(operation) {
if (operation.options.execute) {
VipConsole.log(`${VipSymbols.info} Executing Script ${operation.options.execute}`);
await VipExecutor.execShell({ command: operation.options.execute, description: "\u6267\u884Cexecute\u63D0\u4F9B\u7684\u547D\u4EE4" });
VipConsole.log(`${VipSymbols.success} Script Finished`);
}
}
export { versionBumpDryRun as a, versionBumpInfo as b, versionBump as v };