release.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/usr/bin/env node
  2. import { execSync } from 'child_process'
  3. import inquirer from 'inquirer'
  4. import fs from 'fs'
  5. function run(cmd, options = {}) {
  6. execSync(cmd, { stdio: 'inherit', ...options })
  7. }
  8. function runSilent(cmd) {
  9. return execSync(cmd, { encoding: 'utf-8' }).trim()
  10. }
  11. function getVersion() {
  12. return JSON.parse(fs.readFileSync('package.json', 'utf8')).version
  13. }
  14. function getLastTag() {
  15. try {
  16. return runSilent('git describe --tags --abbrev=0')
  17. } catch {
  18. return null // 没有 tag
  19. }
  20. }
  21. function getCommitsSince(tag) {
  22. const range = tag ? `${tag}..HEAD` : ''
  23. try {
  24. return runSilent(`git log ${range} --pretty=format:"- %s (%h)"`)
  25. } catch {
  26. return ''
  27. }
  28. }
  29. function updateChangelog(version, mode = 'incremental') {
  30. const lastTag = getLastTag()
  31. const commits = getCommitsSince(lastTag)
  32. if (!commits) {
  33. console.log('⚠️ 没有新提交,但依旧会记录版本变化。')
  34. }
  35. const date = new Date().toISOString().slice(0, 10)
  36. const newEntry = `\n## ${version} (${date})\n${commits || '- No changes'}\n`
  37. let changelog = ''
  38. if (fs.existsSync('CHANGELOG.md')) {
  39. changelog = fs.readFileSync('CHANGELOG.md', 'utf-8')
  40. }
  41. if (mode === 'full') {
  42. fs.writeFileSync('CHANGELOG.md', newEntry, 'utf-8')
  43. } else {
  44. fs.writeFileSync('CHANGELOG.md', newEntry + changelog, 'utf-8')
  45. }
  46. console.log(`✅ changelog 已更新到版本 ${version}`)
  47. }
  48. async function main() {
  49. console.log('🚀 开始发布流程...')
  50. // 选择版本类型
  51. const { versionType } = await inquirer.prompt([
  52. {
  53. type: 'list',
  54. name: 'versionType',
  55. message: '请选择版本类型:',
  56. default: 'patch',
  57. choices: [
  58. { name: '补丁版本 (patch)', value: 'patch' },
  59. { name: '小版本 (minor)', value: 'minor' },
  60. { name: '大版本 (major)', value: 'major' },
  61. ],
  62. },
  63. ])
  64. // 更新版本号(不自动打 tag)
  65. run(`npm version ${versionType} --no-git-tag-version`)
  66. const version = getVersion()
  67. // 选择 changelog 生成模式
  68. const { changelogMode } = await inquirer.prompt([
  69. {
  70. type: 'list',
  71. name: 'changelogMode',
  72. message: '请选择 changelog 生成模式:',
  73. default: 'incremental',
  74. choices: [
  75. { name: '全量生成(会覆盖整个文件)', value: 'full' },
  76. { name: '增量生成(只追加本次更新的内容)', value: 'incremental' },
  77. ],
  78. },
  79. ])
  80. // 生成 changelog
  81. updateChangelog(`v${version}`, changelogMode)
  82. // 提交变更
  83. if (fs.existsSync('package-lock.json')) {
  84. run('git add package.json package-lock.json')
  85. } else {
  86. run('git add package.json')
  87. }
  88. run('git add CHANGELOG.md')
  89. try {
  90. run(`git diff --cached --quiet || git commit -m "chore: release v${version}"`)
  91. } catch {
  92. console.log('⚠️ 没有需要提交的文件,跳过 commit')
  93. }
  94. // 打 tag & 推送
  95. run(`git tag v${version}`)
  96. run('git push && git push --tags')
  97. console.log('\x1b[36m=========================\x1b[0m')
  98. console.log('\x1b[32m🎉 发布成功!\x1b[0m')
  99. console.log(`\x1b[33m📦 版本号:v${version}\x1b[0m`)
  100. console.log('\x1b[35m⚙️ 部署:\x1b[0m')
  101. console.log('\x1b[34m * 测试环境:npm run deploy:test\x1b[0m')
  102. console.log('\x1b[34m * 开发环境:npm run build:dev\x1b[0m')
  103. console.log('\x1b[34m * 生产环境:npm run build\x1b[0m')
  104. console.log('\x1b[36m=========================\x1b[0m')
  105. }
  106. main()