|
@@ -1,116 +1,92 @@
|
|
import inquirer from 'inquirer'
|
|
import inquirer from 'inquirer'
|
|
import { execSync } from 'child_process'
|
|
import { execSync } from 'child_process'
|
|
import fs from 'fs'
|
|
import fs from 'fs'
|
|
|
|
+import path from 'path'
|
|
|
|
|
|
-// 运行命令
|
|
|
|
|
|
+// changelog 文件路径
|
|
|
|
+const changelogPath = path.resolve('CHANGELOG.md')
|
|
|
|
+
|
|
|
|
+// 运行命令工具
|
|
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 === ''
|
|
}
|
|
}
|
|
|
|
|
|
-// 获取最新 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 ''
|
|
|
|
- }
|
|
|
|
|
|
+// 检查是否有符合规范的提交(feat、fix 等)
|
|
|
|
+const hasConventionalCommits = () => {
|
|
|
|
+ const lastTag = execSync('git describe --tags --abbrev=0', { stdio: 'pipe' }).toString().trim()
|
|
|
|
+ const log = execSync(`git log ${lastTag}..HEAD --pretty=format:%s`, { stdio: 'pipe' })
|
|
|
|
+ .toString()
|
|
|
|
+ .trim()
|
|
|
|
+ return /^(feat|fix|docs|style|refactor|perf|test|chore)(\(.+\))?:/m.test(log)
|
|
}
|
|
}
|
|
|
|
|
|
-// 检查是否有符合 Angular 规范的提交
|
|
|
|
-const hasConventionalCommits = (log) => {
|
|
|
|
- return /^(feat|fix|docs|style|refactor|perf|test|chore)(\(.+\))?:/m.test(log)
|
|
|
|
|
|
+// 1. 确保 changelog 文件存在
|
|
|
|
+if (!fs.existsSync(changelogPath)) {
|
|
|
|
+ console.log(`📄 检测到不存在 ${changelogPath},已自动创建空文件`)
|
|
|
|
+ fs.writeFileSync(changelogPath, '', 'utf-8')
|
|
}
|
|
}
|
|
|
|
|
|
-// 1. 检查 git 工作区
|
|
|
|
|
|
+// 2. 检查 git 工作区
|
|
if (!isGitClean()) {
|
|
if (!isGitClean()) {
|
|
console.error('❌ Git 工作区有未提交的改动,请先提交后再发版!')
|
|
console.error('❌ Git 工作区有未提交的改动,请先提交后再发版!')
|
|
process.exit(1)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
|
|
-// 2. 检查提交记录
|
|
|
|
-const latestTag = getLatestTag()
|
|
|
|
-const commitLog = getCommitsSinceTag(latestTag)
|
|
|
|
-
|
|
|
|
-if (!hasConventionalCommits(commitLog)) {
|
|
|
|
- console.error('⚠️ 没有检测到符合 Angular 提交规范的提交,跳过 changelog 生成!')
|
|
|
|
- process.exit(1)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// 3. 选择发布模式
|
|
|
|
-const { mode } = await inquirer.prompt([
|
|
|
|
|
|
+// 3. 询问发布模式
|
|
|
|
+const { publishMode } = await inquirer.prompt([
|
|
{
|
|
{
|
|
type: 'list',
|
|
type: 'list',
|
|
- name: 'mode',
|
|
|
|
|
|
+ name: 'publishMode',
|
|
message: '请选择发布模式:',
|
|
message: '请选择发布模式:',
|
|
choices: [
|
|
choices: [
|
|
- { name: '正式发版(更新版本号 + 生成 tag + 推送)', value: 'release' },
|
|
|
|
|
|
+ { name: '正常发版(更新版本号并推送)', value: 'normal' },
|
|
{ name: '自测模式(仅生成 changelog,不推送)', value: 'test' },
|
|
{ name: '自测模式(仅生成 changelog,不推送)', value: 'test' },
|
|
],
|
|
],
|
|
|
|
+ default: 'normal',
|
|
},
|
|
},
|
|
])
|
|
])
|
|
|
|
|
|
-// 4. 选择 changelog 生成方式
|
|
|
|
-const { changelogMode } = await inquirer.prompt([
|
|
|
|
|
|
+// 4. 检查是否有符合规范的提交
|
|
|
|
+if (!hasConventionalCommits()) {
|
|
|
|
+ console.error('⚠️ 没有检测到符合 Angular 提交规范的提交,跳过 changelog 生成!')
|
|
|
|
+ process.exit(1)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 5. 询问生成 changelog 的方式
|
|
|
|
+const { mode } = await inquirer.prompt([
|
|
{
|
|
{
|
|
type: 'list',
|
|
type: 'list',
|
|
- name: 'changelogMode',
|
|
|
|
|
|
+ name: 'mode',
|
|
message: '请选择 changelog 生成方式:',
|
|
message: '请选择 changelog 生成方式:',
|
|
choices: [
|
|
choices: [
|
|
- { name: '增量追加日志(推荐)', value: 'incremental' },
|
|
|
|
|
|
+ { name: '只追加日志(推荐)', value: 'incremental' },
|
|
{ name: '全量生成日志(会覆盖原文件)', value: 'all' },
|
|
{ name: '全量生成日志(会覆盖原文件)', value: 'all' },
|
|
],
|
|
],
|
|
|
|
+ default: 'incremental',
|
|
},
|
|
},
|
|
])
|
|
])
|
|
|
|
|
|
-// 5. 生成 changelog
|
|
|
|
-if (changelogMode === 'all') {
|
|
|
|
|
|
+// 6. 生成 changelog
|
|
|
|
+if (mode === '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')
|
|
}
|
|
}
|
|
|
|
|
|
-// 6. 检查 changelog 是否更新
|
|
|
|
-const changelogContent = fs.readFileSync('CHANGELOG.md', 'utf8')
|
|
|
|
-if (!changelogContent.includes(new Date().getFullYear().toString())) {
|
|
|
|
- console.error('⚠️ changelog 没有生成新的内容,请检查提交记录!')
|
|
|
|
- process.exit(1)
|
|
|
|
|
|
+// 7. 如果是自测模式,提前结束
|
|
|
|
+if (publishMode === 'test') {
|
|
|
|
+ console.log('✅ 自测模式完成(未生成 tag / 未推送)')
|
|
|
|
+ process.exit(0)
|
|
}
|
|
}
|
|
|
|
|
|
-// 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' },
|
|
|
|
- ],
|
|
|
|
- },
|
|
|
|
- ])
|
|
|
|
|
|
+// 8. 更新版本号(补丁版本)
|
|
|
|
+run('npm version patch -m "new version published: v%s"')
|
|
|
|
|
|
- // 更新版本号 & 提交 tag
|
|
|
|
- run(`npm version ${versionType} -m "release: v%s"`)
|
|
|
|
|
|
+// 9. 推送代码和 tag
|
|
|
|
+run('git push && git push --tags')
|
|
|
|
|
|
- // 推送代码 & tag
|
|
|
|
- run('git push && git push --tags')
|
|
|
|
- console.log('✅ 正式发版完成!')
|
|
|
|
-} else {
|
|
|
|
- console.log('✅ 自测模式完成(未生成 tag / 未推送)')
|
|
|
|
-}
|
|
|
|
|
|
+console.log('✅ 发布完成!')
|