UNPKG

@142vip/release-version

Version:

通用型版本迭代Cli工具,支持版本迭代更新,Git提交信息、打标记并推送到远程

276 lines (269 loc) 10.8 kB
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 };