自动 release 工具

无聊,写个自动 release 工具,自动升级版本,自动提交,自动打 tag,自动推送到远程仓库。

1. 安装

1
yarn add chalk semver enquirer  execa

2. package.json

1
"scripts": {
2
   "release": "node ./scripts/release.js"
3
 },

3. scripts/release.js

1
const fs = require('fs')
2
const path = require('path')
3
const chalk = require('chalk')
4
const semver = require('semver')
5
const { version: currentVersion } = require('../package.json')
6
const { prompt } = require('enquirer')
7
const execa = require('execa')
8
9
//  版本列表
10
const versionIncrements = [
11
  'patch',
12
  'minor',
13
  'major',
14
  'prepatch',
15
  'preminor',
16
  'premajor',
17
  'prerelease'
18
]
19
20
// 步骤打印
21
const step = msg => console.log(chalk.green(msg))
22
23
// 增加版本号
24
const inc = i => semver.inc(currentVersion, i)
25
26
// 运行脚本
27
const run = (bin, args, opts = {}) =>
28
  execa(bin, args, { stdio: 'inherit', ...opts })
29
30
/**
31
 * 更新版本号
32
 * @param {string} version
33
 */
34
function updatePackage(version) {
35
  const pkgPath = path.resolve(__dirname, '../package.json')
36
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
37
  pkg.version = version
38
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
39
  step(`updated package.json version to ${version}\n`)
40
}
41
42
/**
43
 * 提交 打标签 推送到远程仓库
44
 * @param {string} version
45
 */
46
async function publish(version) {
47
  try {
48
    await run('git', ['add', '-A'])
49
    await run('git', ['tag', '-a', version, '-m', `Release v${version}`])
50
    await run('git', ['commit', '-m', `release: v${version}`])
51
    await run('git', ['push', '--tags'])
52
    await run('git', ['push'])
53
    step(`push version to ${version}\n`)
54
  } catch (error) {
55
    throw new Error(error)
56
  }
57
}
58
59
// 主函数
60
async function main() {
61
  let version
62
63
  const { release } = await prompt({
64
    type: 'select',
65
    name: 'release',
66
    message: 'Select release type',
67
    choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])
68
  })
69
70
  if (release === 'custom') {
71
    version = (
72
      await prompt({
73
        type: 'input',
74
        name: 'version',
75
        message: 'Input custom version',
76
        initial: version
77
      })
78
    ).version
79
  } else {
80
    version = release.match(/\((.*)\)/)[1]
81
  }
82
83
  if (!semver.valid(version)) {
84
    throw new Error(`invalid target version: ${version}`)
85
  }
86
  updatePackage(version)
87
  await publish(version)
88
}
89
90
main().catch(err => {
91
  console.error(err)
92
})

4. 运行

1
yarn release

5.效果

1
$ yarn release
2
yarn run v1.22.17
3
$ node ./scripts/release.js
4
? Select release type ...
5
> patch (0.1.1)
6
  minor (0.2.0)
7
  major (1.0.0)
8
  prepatch (0.1.1-0)
9
  preminor (0.2.0-0)
10
  premajor (1.0.0-0)
11
  prerelease (0.1.1-0)
12
  custom