在之前的分享中,我们团队已经成功运用了 Gitlab CI(参考耗时三天,我将 Gitlab CI 由 shell executor 平滑迁移 Docker 环境),并且已经结构化提交 git commit 记录(参考规范化 git commit 信息)。我们希望更进一步,给用户交付的时候同时整理出一份完整的更新日志,尽管按照之前规范化 git commit 信息的内容,可以手工生成一份 Release Note 交付给用户,但是我们希望结合 CI 更加自动化,自动管理发布版本,自动生成更新日志,因此我们引入了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 大致的工作流如下:
CHANGELOG.md
,npm publish
等等(通过插件完成)由 CI 自动执行之后的效果就像这样,在 Git tag 页面可以看到 tag 信息,同时包含更新记录:
如果启用了@semantic-release/git插件,还会将生成的CHANGELOG.md
反向 push 回中央仓库:
默认的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
这几个分支才会触发自动发布流程,并且只有 feat 和 fix 提交才会触发版本升级,版本号按照语义化版本规则自动生成。即:
x.y.z
格式组织(git tag 会加上v
前缀,如v1.0.0
)1.0.0
–> 1.0.1
)1.0.0
–> 1.1.0
)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 记录,版本由1.0.0
升级到了1.1.0
向master
分支 push 一条fix: 🐛 测试fix
记录:
只有一条 fix 记录,版本由1.1.0
升级到了1.1.1
同时包含多条 feat/fix 记录,push 之后只会升级一次版本,不会跨多版本:
既包含 feat 记录,也包含 fix 记录时,则只会升级次版本号或主板本号(是否有 break changes 决定升级主板本)
break change feat 提交将会升级主版本号:
关于 semantic-release 详细的工作流程,建议阅读官方文档: Workflow configuration
经过评估之后,我们团队对目前的工作流稍微进行了调整,在不大变动团队原本习惯的开发流程的基础上,做了一下调整,并且 semantic-release 的默认配置也能很好的适配我们目前的分支模型:
同时,为了让 commit 信息尽可能的好看,减少不必要的 Merge from
提交记录,我们使用 rebase 的方式合并代码,参考以下步骤:
在 Gitlab 的项目设置中将Merge requests
中的Merge method
设置为Fast-forward merge
,这样 merge request 审核代码的时候就不会产生Merge from xxx
本地合并远程仓库的代码使用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.gradle
或package.json
等),如果需要自动管理这些地方的版本,与 git tag 版本保持一致,可以引入@semantic-release/exec插件,自己写脚本,通过脚本自动化修改这些地方的版本号。
还需要注意的是 semantic-release 默认产生的 commit 记录为了避免不必要的 CI 流程,会在 commit 记录加上[skip ci]
(见上面的截图)来跳过 CI,如果你的流水线需要由 git tag 触发,可以配置@semantic-release/git插件,自定义 commit 记录,去掉[skip ci]
。
觉得有帮助的话,不妨考虑购买付费文章来支持我们 🙂 :
付费文章