|
@@ -2,20 +2,36 @@ import inquirer from 'inquirer'
|
|
import { execSync } from 'child_process'
|
|
import { execSync } from 'child_process'
|
|
import fs from 'fs'
|
|
import fs from 'fs'
|
|
|
|
|
|
-// 运行命令工具
|
|
|
|
|
|
+// 运行命令
|
|
const run = (cmd) => execSync(cmd, { stdio: 'inherit' })
|
|
const run = (cmd) => execSync(cmd, { stdio: 'inherit' })
|
|
|
|
|
|
-// 检查 Git 工作区是否干净
|
|
|
|
|
|
+// Git 工作区是否干净
|
|
const isGitClean = () => {
|
|
const isGitClean = () => {
|
|
const status = execSync('git status --porcelain').toString().trim()
|
|
const status = execSync('git status --porcelain').toString().trim()
|
|
return status === ''
|
|
return status === ''
|
|
}
|
|
}
|
|
|
|
|
|
-// 检查是否有符合规范的提交(feat、fix 等)
|
|
|
|
-const hasConventionalCommits = () => {
|
|
|
|
- const log = execSync('git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:%s')
|
|
|
|
- .toString()
|
|
|
|
- .trim()
|
|
|
|
|
|
+// 获取最新 tag
|
|
|
|
+const getLatestTag = () => {
|
|
|
|
+ try {
|
|
|
|
+ return execSync('git describe --tags --abbrev=0').toString().trim()
|
|
|
|
+ } catch {
|
|
|
|
+ return ''
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 获取最新 tag 到现在的提交记录
|
|
|
|
+const getCommitsSinceTag = (tag) => {
|
|
|
|
+ try {
|
|
|
|
+ const range = tag ? `${tag}..HEAD` : 'HEAD'
|
|
|
|
+ return execSync(`git log ${range} --pretty=format:%s`).toString().trim()
|
|
|
|
+ } catch {
|
|
|
|
+ return ''
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 检查是否有符合 Angular 规范的提交
|
|
|
|
+const hasConventionalCommits = (log) => {
|
|
return /^(feat|fix|docs|style|refactor|perf|test|chore)(\(.+\))?:/m.test(log)
|
|
return /^(feat|fix|docs|style|refactor|perf|test|chore)(\(.+\))?:/m.test(log)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -25,44 +41,76 @@ if (!isGitClean()) {
|
|
process.exit(1)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
|
|
-// 2. 检查是否有符合规范的提交
|
|
|
|
-if (!hasConventionalCommits()) {
|
|
|
|
|
|
+// 2. 检查提交记录
|
|
|
|
+const latestTag = getLatestTag()
|
|
|
|
+const commitLog = getCommitsSinceTag(latestTag)
|
|
|
|
+
|
|
|
|
+if (!hasConventionalCommits(commitLog)) {
|
|
console.error('⚠️ 没有检测到符合 Angular 提交规范的提交,跳过 changelog 生成!')
|
|
console.error('⚠️ 没有检测到符合 Angular 提交规范的提交,跳过 changelog 生成!')
|
|
process.exit(1)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
|
|
-// 3. 询问生成 changelog 的方式
|
|
|
|
|
|
+// 3. 选择发布模式
|
|
const { mode } = await inquirer.prompt([
|
|
const { mode } = await inquirer.prompt([
|
|
{
|
|
{
|
|
type: 'list',
|
|
type: 'list',
|
|
name: 'mode',
|
|
name: 'mode',
|
|
- message: '请选择生成 changelog 的方式:',
|
|
|
|
|
|
+ message: '请选择发布模式:',
|
|
|
|
+ choices: [
|
|
|
|
+ { name: '正式发版(更新版本号 + 生成 tag + 推送)', value: 'release' },
|
|
|
|
+ { name: '自测模式(仅生成 changelog,不推送)', value: 'test' },
|
|
|
|
+ ],
|
|
|
|
+ },
|
|
|
|
+])
|
|
|
|
+
|
|
|
|
+// 4. 选择 changelog 生成方式
|
|
|
|
+const { changelogMode } = await inquirer.prompt([
|
|
|
|
+ {
|
|
|
|
+ type: 'list',
|
|
|
|
+ name: 'changelogMode',
|
|
|
|
+ message: '请选择 changelog 生成方式:',
|
|
choices: [
|
|
choices: [
|
|
- { name: '只追加日志(推荐)', value: 'incremental' },
|
|
|
|
|
|
+ { name: '增量追加日志(推荐)', value: 'incremental' },
|
|
{ name: '全量生成日志(会覆盖原文件)', value: 'all' },
|
|
{ name: '全量生成日志(会覆盖原文件)', value: 'all' },
|
|
],
|
|
],
|
|
- default: 'incremental',
|
|
|
|
},
|
|
},
|
|
])
|
|
])
|
|
|
|
|
|
-// 4. 生成 changelog
|
|
|
|
-if (mode === 'all') {
|
|
|
|
|
|
+// 5. 生成 changelog
|
|
|
|
+if (changelogMode === 'all') {
|
|
run('npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0')
|
|
run('npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0')
|
|
} else {
|
|
} else {
|
|
run('npx conventional-changelog -p angular -i CHANGELOG.md -s')
|
|
run('npx conventional-changelog -p angular -i CHANGELOG.md -s')
|
|
}
|
|
}
|
|
|
|
|
|
-// 5. 检查 changelog 是否真的更新
|
|
|
|
-const changelog = fs.readFileSync('CHANGELOG.md', 'utf8')
|
|
|
|
-if (!changelog.includes(new Date().getFullYear().toString())) {
|
|
|
|
|
|
+// 6. 检查 changelog 是否更新
|
|
|
|
+const changelogContent = fs.readFileSync('CHANGELOG.md', 'utf8')
|
|
|
|
+if (!changelogContent.includes(new Date().getFullYear().toString())) {
|
|
console.error('⚠️ changelog 没有生成新的内容,请检查提交记录!')
|
|
console.error('⚠️ changelog 没有生成新的内容,请检查提交记录!')
|
|
process.exit(1)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
|
|
-// 6. 更新版本号(补丁版本)
|
|
|
|
-run('npm version patch -m "new version published: v%s"')
|
|
|
|
|
|
+// 7. 如果是正式模式,选择版本号递增类型
|
|
|
|
+if (mode === 'release') {
|
|
|
|
+ const { versionType } = await inquirer.prompt([
|
|
|
|
+ {
|
|
|
|
+ type: 'list',
|
|
|
|
+ name: 'versionType',
|
|
|
|
+ message: '请选择版本号递增类型:',
|
|
|
|
+ choices: [
|
|
|
|
+ { name: '补丁版本(patch)', value: 'patch' },
|
|
|
|
+ { name: '小版本(minor)', value: 'minor' },
|
|
|
|
+ { name: '大版本(major)', value: 'major' },
|
|
|
|
+ ],
|
|
|
|
+ },
|
|
|
|
+ ])
|
|
|
|
|
|
-// 7. 推送代码和 tag
|
|
|
|
-run('git push && git push --tags')
|
|
|
|
|
|
+ // 更新版本号 & 提交 tag
|
|
|
|
+ run(`npm version ${versionType} -m "release: v%s"`)
|
|
|
|
|
|
-console.log('✅ 发布完成!')
|
|
|
|
|
|
+ // 推送代码 & tag
|
|
|
|
+ run('git push && git push --tags')
|
|
|
|
+ console.log('✅ 正式发版完成!')
|
|
|
|
+} else {
|
|
|
|
+ console.log('✅ 自测模式完成(未生成 tag / 未推送)')
|
|
|
|
+}
|