From 5eb66e6a843f24f195f081a5df8f2608d3cf17e4 Mon Sep 17 00:00:00 2001 From: Barrior Date: Mon, 22 Jan 2024 10:01:09 +0800 Subject: [PATCH] chore: improve release command --- scripts/release.mjs | 105 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/scripts/release.mjs b/scripts/release.mjs index 074ebbd..63880f6 100644 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -1,7 +1,8 @@ -import { execSync } from 'child_process' +import { execaCommand, execaCommandSync } from 'execa' import { readdirSync, readFileSync, writeFileSync } from 'fs' import inquirer from 'inquirer' import { forEach } from 'lodash-es' +import ora from 'ora' import path from 'path' import { fileURLToPath } from 'url' @@ -10,6 +11,7 @@ import logger from './logger.mjs' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const packages = path.resolve(__dirname, '../packages') const projectNames = readdirSync(packages) +const spinner = ora() const projectMap = { 'core-react': { @@ -24,16 +26,69 @@ const projectMap = { } class Release { + newVersion = '' + constructor() { this.run() } async run() { + this.catchErrorAndCleanWorkspace() + this.checkBranchAndStatus() + await this.runLintAndTest() await this.askReleaseVersion() this.bumpVersion() + await this.buildProduct() + await this.publishToNpm() this.commit() } + /** + * 命令过程出现错误时,清除构建过程产生的文件,还原到命令执行前的状态 + */ + catchErrorAndCleanWorkspace() { + process.on('uncaughtException', () => { + logger.error('命令过程执行存在错误,还原文件变更至初始状态') + execaCommandSync('git checkout .', { stdio: 'inherit' }) + }) + } + + /** + * 检查分支名称是否正确:必须是 master 或者 main 分支才允许 + * 检查分支状态是否正确:必须是干净的分支,不存在未提交的内容 + */ + checkBranchAndStatus() { + // 检查分支名称 + const branchName = execaCommandSync('git symbolic-ref HEAD --short').stdout + if (!['master', 'main'].includes(branchName)) { + logger.error('命令执行分支不正确,只允许 master 或者 main 分支') + process.exit(1) + } + + // 检查分支状态 + const status = execaCommandSync('git status --porcelain').stdout + if (status) { + logger.error('分支状态不正确,存在未提交的文件') + process.exit(1) + } + } + + /** + * 校验代码风格与运行测试用例 + */ + async runLintAndTest() { + spinner.start('代码风格校验') + await execaCommand('yarn lint') + spinner.succeed() + + spinner.start('测试用例验证') + await execaCommand('yarn test --verbose=false') + spinner.succeed() + } + + /** + * 咨询发布版本号 + */ async askReleaseVersion() { // 读取 core package.json const filePath = path.resolve(packages, './core-react/package.json') @@ -51,7 +106,7 @@ class Release { const answers = await inquirer.prompt(questions) - // Trim the answers of input + // 去除输入内容首尾空白 forEach(answers, (value, key) => { answers[key] = value.trim() }) @@ -63,6 +118,11 @@ class Release { * 将每个项目自身的版本号、依赖项的版本号更新为最新的 */ bumpVersion() { + if (!this.newVersion) { + logger.error('发布版本号不存在') + process.exit(1) + } + projectNames.forEach((projectName) => { // 读取内容 const filePath = path.resolve(packages, `./${projectName}/package.json`) @@ -86,13 +146,48 @@ class Release { }) } + /** + * 构建产品,将源码编译到 dist 目录 + */ + async buildProduct() { + for (let i = 0; i < projectNames.length; i++) { + const projectName = projectNames[i] + const dirPath = path.resolve(packages, `./${projectName}`) + + spinner.start(`执行 ${projectName} 构建命令`) + await execaCommand('npm run build', { cwd: dirPath }) + spinner.succeed() + } + } + + /** + * 发布到 npm + */ + async publishToNpm() { + for (let i = 0; i < projectNames.length; i++) { + const projectName = projectNames[i] + const dirPath = path.resolve(packages, `./${projectName}`) + + spinner.start(`发布 ${projectName}`) + await execaCommand('npm publish', { cwd: dirPath }) + spinner.succeed() + } + } + /** * 提交 git */ commit() { - execSync(`git commit -am "build: v${this.newVersion}" -n`, { - stdio: 'inherit', - }) + const commitFiles = projectNames + .map((projectName) => `packages/${projectName}/package.json`) + .join(' ') + execaCommandSync(`git add ${commitFiles}`) + execaCommandSync(`git commit -m chore(release):\\ v${this.newVersion} -n`) + execaCommandSync(`git tag v${this.newVersion}`) + logger.log(`文件变更提交 Git 完成`) + + // execaCommandSync('git push origin --tags') + // logger.log(`推送 Git 到远程 origin 仓库完成`) } }