10

团队敏捷实践 —— 使用 semantic-release 自动管理发布版本 - DTeam 团队日志

 4 years ago
source link: https://blog.dteam.top/posts/2020-05/semantic-release.html?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在之前的分享中,我们团队已经成功运用了 Gitlab CI(参考耗时三天,我将 Gitlab CI 由 shell executor 平滑迁移 Docker 环境),并且已经结构化提交 git commit 记录(参考规范化 git commit 信息)。我们希望更进一步,给用户交付的时候同时整理出一份完整的更新日志,尽管按照之前规范化 git commit 信息的内容,可以手工生成一份 Release Note 交付给用户,但是我们希望结合 CI 更加自动化,自动管理发布版本,自动生成更新日志,因此我们引入了semantic-release进一步自动化管理我们的发布流程。

semantic-release 概述

有关semantic-release的详细介绍可以阅读官方文档,这里只做一些概述性的总结。和之前在规范化 git commit 信息一文介绍的standard-version相比,semantic-release更适合在 CI 环境中运行,它自带支持各种 git server 的认证支持,如 Github,Gitlab,Bitbucket 等等,此外,还支持插件,以便完成其他后续的流程步骤,比如自动生成 git tag 和 release note 之后再 push 回中央仓库,自动发布 npm 包等等。

semantic-release 会根据规范化的 commit 信息生成发布日志,默认使用 angular 规则。其他规则可以配置插件完成。

semantic-release 大致的工作流如下:

  • 提交到特定的分支触发 release 流程
  • 验证 commit 信息,生成 release note,打 git tag
  • 其他后续流程,如生成CHANGELOG.mdnpm publish等等(通过插件完成)

由 CI 自动执行之后的效果就像这样,在 Git tag 页面可以看到 tag 信息,同时包含更新记录:

tag页面

如果启用了@semantic-release/git插件,还会将生成的CHANGELOG.md 反向 push 回中央仓库:

CHANGELOG出现在中央仓库

默认的branches配置如下:

['+([0-9])?(.{+([0-9]),x}).x', 'master', 'next', 'next-major', {name: 'beta', prerelease: true}, {name: 'alpha', prerelease: true}]

代表默认情况下 push 到X.Y.x, master, next, next-major, beta, alpha这几个分支才会触发自动发布流程,并且只有 featfix 提交才会触发版本升级,版本号按照语义化版本规则自动生成。即:

  • 版本号按照x.y.z格式组织(git tag 会加上v前缀,如v1.0.0
  • bug fix 发布会增加修订版本号(如 1.0.0 –> 1.0.1
  • feature 发布会增加次版本号(如1.0.0 –> 1.1.0
  • break change feature 发布会增加主版本号(如1.1.1 –> 2.0.0,官方建议这种不兼容的升级应该推送到 next 分支开发,之后合并到 master

第一次使用 semantic-release 发布会生成1.0.0版本,同时,semantic-release 会兼顾到旧的发布版本,如果之前有过 git tag,则会在旧的 tag 基础之上再做语义化版本控制。

这里演示下实际效果:

  • 第一次使用 semantic-release 发布,生成了1.0.0版本,并在此基础之上,向master分支 push 一条feat: 🎸 测试feat发布记录:

    只有一个feat提交

    只有一条 feat 记录,版本由1.0.0升级到了1.1.0

  • master分支 push 一条fix: 🐛 测试fix记录:

    只有一个fix提交

    只有一条 fix 记录,版本由1.1.0升级到了1.1.1

  • 同时包含多条 feat/fix 记录,push 之后只会升级一次版本,不会跨多版本:

    多条feat提交
    多条fix提交
  • 既包含 feat 记录,也包含 fix 记录时,则只会升级次版本号或主板本号(是否有 break changes 决定升级主板本)

    混合feat和fix提交
  • break change feat 提交将会升级主版本号:

    break-change提交

关于 semantic-release 详细的工作流程,建议阅读官方文档: Workflow configuration

我们的敏捷实践

经过评估之后,我们团队对目前的工作流稍微进行了调整,在不大变动团队原本习惯的开发流程的基础上,做了一下调整,并且 semantic-release 的默认配置也能很好的适配我们目前的分支模型:

  • 使用git-cz提交格式化的 commit 记录(沿用之前规范化 git commit 信息的实践)
  • 使用commitlint检测 commit 格式是否符合规范(沿用之前规范化 git commit 信息的实践)
  • 所有的特性功能,以及不紧急的 bug fix 一律提交到 dev 分支(此分支不会触发 release 流程)
  • 所有的 hot fix 直接提交到 master 分支,以便快速发布紧急修复

同时,为了让 commit 信息尽可能的好看,减少不必要的 Merge from 提交记录,我们使用 rebase 的方式合并代码,参考以下步骤:

  • 在 Gitlab 的项目设置中将Merge requests中的Merge method设置为Fast-forward merge,这样 merge request 审核代码的时候就不会产生Merge from xxx

    fast-forward-merge
  • 本地合并远程仓库的代码使用git pull --rebase,而不是git pull,可以通过以下命令配置以后默认 pull 的规则就是 rebase,不用每次都加 --rebase:

    git config --global pull.rebase true
    

最后,在项目工程中添加.releaserc配置如下:

{
  "plugins": [
    [
      "@semantic-release/commit-analyzer",
      {
        "preset": "conventionalcommits"
      }
    ],
    [
      "@semantic-release/release-notes-generator",
      {
        "preset": "conventionalcommits"
      }
    ],
    "@semantic-release/changelog",
    "@semantic-release/gitlab",
    "@semantic-release/git"
  ]
}

完成 .gitlab-ci.yml 配置如下(仅部分关键的配置片段):

# 添加了 lint 过程用于检测 commitlint 结果
# 添加了 release 过程用于自动化产生 git tag 和 CHANGELOG.md
stages:
  - lint
  - build
  - test
  - release
  - deploy

commitlint:
  stage: lint
  image: node:lts
  script: |
    npm install -g @commitlint/cli @commitlint/config-conventional
    if [ "${CI_COMMIT_BEFORE_SHA}" = "0000000000000000000000000000000000000000" ]; then
      echo "commitlint from HEAD^"
      npx commitlint -x @commitlint/config-conventional -f HEAD^
    else
      echo "commitlint from ${CI_COMMIT_BEFORE_SHA}"
      npx commitlint -x @commitlint/config-conventional -f "${CI_COMMIT_BEFORE_SHA}"
    fi
  dependencies: []
  tags:
    - docker

build: ...

test: ...

release:
  stage: release
  image: node:lts
  script:
    - npm install -g semantic-release @semantic-release/gitlab @semantic-release/changelog conventional-changelog-conventionalcommits @semantic-release/git
    - npx semantic-release
  dependencies: []
  # 仅在中央仓库的分支发生提交时才触发 release 流程
  only:
    - branches@upstream_path/upstream_project
  tags:
    - docker

deploy: ...

至此,我们完成了通过 CI 自动管理版本号和发布日志的需求,以后面向用户交付的时候,直接根据CHANGELOG.md的内容整理下就可以了,大大节省了人力,同时,还留下了发布痕迹,方便追溯历史版本。

另外,需要注意的是上述的配置并不会修改源码部分的版本号配置内容(如build.gradlepackage.json等),如果需要自动管理这些地方的版本,与 git tag 版本保持一致,可以引入@semantic-release/exec插件,自己写脚本,通过脚本自动化修改这些地方的版本号。

还需要注意的是 semantic-release 默认产生的 commit 记录为了避免不必要的 CI 流程,会在 commit 记录加上[skip ci](见上面的截图)来跳过 CI,如果你的流水线需要由 git tag 触发,可以配置@semantic-release/git插件,自定义 commit 记录,去掉[skip ci]


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK