logo

鱼肚的博客

Don't Repeat Yourself

手动维护npm包容易出现一些问题:忘记编译、多提交一些无关文件、网络不通等。自动化发布能够极大地简化这个过程。

最近刚调整过一次自动化流程,可以自动发布版本,及根据Commit消息生成Changelog。实现如下图所示的效果:

Demo效果图

下面介绍下具体的配置过程。

前置要求

在开始之前,需要准备如下的材料:

  1. npm token,用于发布。获取方式参见:https://docs.npmjs.com/creating-and-viewing-authentication-tokens

步骤列表

  1. 设置约定式提交 Conventional Commits生成Changelog
  2. 设置Github Actions自动发布版本
  3. 设置自动生成Github Release及Changelog

设置约定式提交Conventional Commits生成Changelog

约定式提交 Conventional Commits 是一种Commit消息规范,它要求开发者按照规范编写Git Commit消息,并可根据Git Commit消息自动生成Changelog。

本文中Changelog的生成也是根据此规范自动处理的。

在npm工程中,要设置Conventional Commits,需要做如下几个方面的处理

设置npm version命令自动生成Changelog

可以在package.jsonscripts中添加一个version命令,内容如下:

1 "scripts": {
2   "version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
3 },

同时,安装下相应的依赖conventional-changelog-cli,全局安装或在项目中安装均可。我用的是全局安装

1npm i -g conventional-changelog-cli

这样配置以后,在执行npm version patch|minor|major时,会相应地自动更新Changelog文件。

设置Git hook

上面的conventional-changelog设置好了机制,另外还需要提交的时候遵守约定式提交 Conventional Commits规范才能实现自动生成Changelog的功能。

为了避免写出不规范的Commit消息,可考虑添加Git hook,在提交代码时检查Commit消息是否合法。

首先安装husky用来管理git hook:

1npm i -D husky

再添加husky的配置文件.huskyrc,添加提交消息的git hook

1{
2  "hooks": {
3    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
4  }
5}

这里我们使用了commitlint进行commit消息的规范校验。

一般来说,使用Angular团队推荐的Conventional Commits规范格式即可,有需要的可适当自定义。

我使用的commitlint配置文件(commitlint.config.js)如下:

1module.exports = {
2  extends: ['@commitlint/config-conventional']
3}

相应地,还需要安装commitlint的命令行工具及规范包,推荐还是全局安装即可:

1npm install -g @commitlint/cli @commitlint/config-conventional

如上步骤配置完之后,就有了一套可用的changelog生成机制了。

Git commit之后,试试npm version patch,看看是不是发布了新版本,并自动添加了CHANGELOG.md文件?

设置Github Actions自动发布版本

上一节讲的还都是一些本地操作,本地使用npm version patch|minor|major发布版本时自动生成Changelog。下面要讲的就是怎么跟Github结合,以及如何使用Github Actions发布版本。

设置postversion script

在package.json中添加postversion脚本,可在npm version patch|minor|major之后,自动将代码和tag推送到Github上。

设置方式为:

1 "scripts": {
2     "postversion": "git push --follow-tags",
3 },

如果不添加这种脚本,自己每次手动push也行。

设置项目的Github Secrets

自动化发布过程中,涉及到npm身份的校验。我们是用的NPM_TOKEN来解决这个问题。(NPM_TOKEN的生成见https://docs.npmjs.com/creating-and-viewing-authentication-tokens)

但是NPM_TOKEN是不能直接写在代码中的,安全性太低,需要保存在Github Secrets中,再在Actions中再使用。

关于Secrets的创建,可以参阅Github上的指南:https://help.github.com/cn/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets。

  1. 在 GitHub 上,导航到仓库的主页面。
  2. 在仓库名称下,单击 Settings(设置)
  3. 在左侧边栏中,单击 Secrets(密码)
  4. 在 "Name"(名称)输入框中输入密码的名称。
  5. 输入密码的值。
  6. 单击 Add secret(添加密码)

设置自动发布

在上面的步骤中,我们使用npm version patch|minor|major等创建了Tag,并使用postversion中的脚本自动将其推送到了Github仓库。

现在我们可以通过检测新tag,自动执行工作流来进行npm publish的操作。Github上可用的CI/CD服务很多,这里我选择的是其自带的Github Actions

要使用Github Actions,只需要在相应的仓库中,创建一个 .github/workflows/目录,并在其中加入一些.yml后缀的配置文件即可。

我们先添加一个用于npm publish的配置文件.github/workflows/publish.yml,文件名是随意定义的。

1on:
2  push:
3    tags:
4    - 'v*'  # 这段的意思是仅在出现名为 v 字符开头的tag时,触发此任务,如v1.2.1
5name: Publish
6jobs:
7  # test: # test任务可选,视情况决定是否添加
8  #   runs-on: ubuntu-latest
9  #   steps:
10  #   - uses: actions/checkout@v2
11  #   - uses: actions/setup-node@v1
12  #     with:
13  #       node-version: 10
14  #   - run: npm i
15  #   - run: npm test
16
17  publish:
18    # needs: test
19    runs-on: ubuntu-latest
20    steps:
21    - uses: actions/checkout@v2
22    - uses: actions/setup-node@v1
23      with:
24        node-version: 10
25        registry-url: https://registry.npmjs.org/
26    - run: npm i
27    - run: npm publish # 如果发布之前需要构建,可添加prepublishOnly的生命周期脚本
28      env:
29        NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} # 上面在Github Secrets中设置的密钥

如果发布之前需要执行构建 ,可在 - run: npm publish之前加一段类似于- run: npm run build之类的指令,或者在package.json中添加prepublishOnly的脚本:

1{
2  "scripts": {
3    "version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
4    "postversion": "git push --follow-tags",
5    "prepublishOnly": "npm run build"
6 }
7}

按上面的方式设置之后,每次执行npm version patch|minor|major之后,会自动地在Github Actions中创建一个名为publish的任务,可在Git仓库的 actions 路径下找到它。

现在应已经能正常地执行npm仓库的发布了。

设置自动生成Github Release

上面已经有了自动化的npm版本发布,且也已经自动地在CHANGELOG.md文件中生成了changelog。不过它现在还实现不了

Demo效果图

这样的效果。

需要调用下Github Release方面的API,生成对应的Release才可以。

因为我们已经在CHANGELOG.md中自动填充上了changelog,所以此处不再需要conventional commits的解析了。我选择的是用一个解析changelog.md生成Github Release的工具:github-release-from-changelog

下面我们再修改下 .github/workflows/publish.yml,添加上对Github Release的支持。

1on:
2  push:
3    tags:
4    - 'v*'
5name: Publish
6jobs:
7  # test:
8  #   runs-on: ubuntu-latest
9  #   steps:
10  #   - uses: actions/checkout@v2
11  #   - uses: actions/setup-node@v1
12  #     with:
13  #       node-version: 10
14  #   - run: npm i
15  #   - run: npm test
16
17  publish:
18    # needs: test
19    runs-on: ubuntu-latest
20    steps:
21    - uses: actions/checkout@v2
22    - uses: actions/setup-node@v1
23      with:
24        node-version: 10
25        registry-url: https://registry.npmjs.org/
26    - run: npm i
27    - run: npm publish
28      env:
29        NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
30
31  github-release:
32    needs: publish
33    runs-on: ubuntu-latest
34    steps:
35    - uses: actions/checkout@v2
36    - uses: actions/setup-node@v1
37      with:
38        node-version: 10
39        registry-url: https://registry.npmjs.org/
40    - run: npm i -g github-release-from-changelog
41    - run: github-release-from-changelog
42      env:
43        GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

上面的github-release任务即是本次新增的Github Release任务,它的主要内容就是安装和执行github-release-from-changelog

过程中会用到名为GITHUB_TOKEN的密钥,用于访问Github相关的API,不过这个私钥是Github Actions默认提供的,不需要额外设置。

上面的步骤完成之后,整个事情就大功告成了!

总结

总结下上面进行的改动,有如下几点:

  1. 获取NPM_TOKEN,用于发布。获取方式参见:https://docs.npmjs.com/creating-and-viewing-authentication-tokens

  2. 将上面获取到的NPM_TOKEN添加到Github Secrets中,设置变量名为NPM_TOKEN。设置方式参见:https://help.github.com/cn/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets。

  3. 全局安装一些工具包,或安装在项目的devDependencies中

    1npm i -g @commitlint/cli @commitlint/config-conventional conventional-changelog-cli
    2
    3# 或 npm i -D @commitlint/cli @commitlint/config-conventional conventional-changelog-cli
  4. 在项目中devDependencies中安装如下包:

    1npm i -D husky
  5. package.json中添加如下的scripts

    1{
    2  "scripts": {
    3    "version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
    4    "postversion": "git push --follow-tags",
    5    "prepublishOnly": "npm run build"
    6 }
    7}

    其中build命令为工程自己配置,不需要的话可以删除prepublishOnly脚本。

  6. 在项目中添加如下的配置文件:

    1. .huskyrc 用于设置 git hooks

      1{
      2  "hooks": {
      3    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
      4  }
      5}
    2. commitlint.config.js 用于设置Git Commit消息规范

      1module.exports = {
      2  extends: ['@commitlint/config-conventional']
      3}
    3. .github/workflows/publish.yml用于设置Github Actions

      1on:
      2  push:
      3    tags:
      4    - 'v*'
      5name: Publish
      6jobs:
      7  # test:
      8  #   runs-on: ubuntu-latest
      9  #   steps:
      10  #   - uses: actions/checkout@v2
      11  #   - uses: actions/setup-node@v1
      12  #     with:
      13  #       node-version: 10
      14  #   - run: npm i
      15  #   - run: npm test
      16
      17  publish:
      18    # needs: test
      19    runs-on: ubuntu-latest
      20    steps:
      21    - uses: actions/checkout@v2
      22    - uses: actions/setup-node@v1
      23      with:
      24        node-version: 10
      25        registry-url: https://registry.npmjs.org/
      26    - run: npm i
      27    - run: npm publish
      28      env:
      29        NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
      30
      31  github-release:
      32    needs: publish
      33    runs-on: ubuntu-latest
      34    steps:
      35    - uses: actions/checkout@v2
      36    - uses: actions/setup-node@v1
      37      with:
      38        node-version: 10
      39        registry-url: https://registry.npmjs.org/
      40    - run: npm i -g github-release-from-changelog
      41    - run: github-release-from-changelog
      42      env:
      43        GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}