1 背景
目前部门维护自己的组件库(miui-desgin
),并发布到了公司私有的npm
库(由JFrog artifactory
搭建)。组件库几乎是照搬了antd
的文件组织结构和打包方式,而组件的使用说明文档也是使用了dumi
。而组件库和文档的发布仍采取本地执行命令并发布的方式,虽然前期制定了发布流程和规范,但从结果上看,依然的存在很多的问题。例如:发布环境不统一、发布流程无法保证、发布流程需要一定的学习成本、每次发布都需要一定的时间成本等。并且随着项目越来越复杂,参与人数越来越多,以上问题会表现的更加的突出,最终导致的正式包内容的不可控。
而要解决解决手动发布组件库的问题,可以借助Gitlab CICD
的能力,将组件库的发布和文档的部署过程搬到线上来执行。不过由于大部分的开源项目在Github
上,且npm
库的发布也并没有采用自动化,因此可以参考的开源组件自动发布流程并不多。不过借助网络上的零散资料和自身部门对组件库管理的规范,整理出如下的UI库自动发布方案。
2 方案
2.1 手动发布流程
组件库的发布包含两项发布工作,其一是UI库的发布,其二是组件使用文档的发布。根据部门制定的组件库发布规范,手动发的流程大体如下图所示:
⚠ 说明:
- 更新version:由发布者按照语义版本控制规范自行修改
package.json
中的version
字段的属性值。 - 添加版本tag:按版本号加v表示tag名称(例如v1.1.1)。
2.2 发布流程拆分
根据部门目前UI
库的发布过程,结合Gitlab
的CICD
能力,可以将整个发布流程拆分为三个任务,分别是代码测试任务、UI
库发布任务和文档打包部署任务。每个任务具体负责的工作如下图所示:
相比于手动发布流程,自动发布流程添加了code test
和推送release
到Gitlab
两个子任务,这是为了更规范的发布UI
库和文档。
2.3 自动发布流程
借助Gitlab
的CICD
能力,当代码合并到master
之后,会自动触发工作流。然后工作流会依次执行代码测试任务、UI
库发布任务和文档打包部署任务。同时为了保证UI
库和文档发布的严谨,在UI
库发布任务之前添加了审批环节,需要手动触发该任务的执行并经过审批后,才会执行UI
库的发布和文档的部署。
2.4 UI库版本生成规则
UI
库的发布过程借助了semantic-release工具,其完成了UI
库发布任务中除了打包外的所有子任务。UI库版本的生成严格执行语义版本控制规范(X.Y.Z
),并通过分析上次发布代码(当前最新tag
的代码)到当前代码新增的commit message
来确定待发布的版本号。
版本生成规则如下:
-
fix
或perf
类型的提交,会将PATCH
版本号加1 -
feat
类型的提交,会将MINOR
版本号加1 - 包含
breaking change
字符串的提交,会将MAJOR
号加1
如果一次发布过程中包含多个提交,则使用所有提交对应版本号中的最大值作为待发布的版本号。例如原版本号为1.2.3,当前代码相比于1.2.3的代码新增了1个fix
和1个feat
提交,当前代码的版本号为1.3.0(即1.2.4和1.3.0中取较大的值)。
如果一次发布过程中的提交都没有命中版本生成规则,UI库发布的任务依然会执行,但由于没有UI库要发布的内容,会跳过本次的发布(不执行push
、release
、publish
等操作)。从实际效果上看,这种情况下只会执行代码测试和文档的打包部署。这种场景对于只修改和发布说明文档很有意义,可以避免无意义的UI库版本的升级。
2.5 代码推送权限控制
为了更好的规范代码提交和发布流程,应该禁止普通开发者直接往master
提交代码,而是通过新建分支进行开发,之后再发送merge
请求合并到master
,经过代码review
和代码测试之后,最终合并进入master
,并触发组件库发布流程。Gitlab
本身支持设置保护分支和允许推送到master
的角色,一般情况下,可以设置只允许Maintainers
提交代码到master
,这样普通开发者便无法直接提交到master
。
但由于历史原因,部门成员所在的组整体被赋予了Maintainers
角色,为了禁止开发者往master
提交代码,roles
需要选择No one
,而这又会导致release-bot
反向推送到master
失败。不过除了设置角色之外,Gitlab
还支持选择deploy key
。
可以按照官方文档的提示生成并配置deploy key,同时将私钥配置到项目CICD
配置的环境变量中.。通过yml
文件的如下配置,可以在UI
库发布阶段,使用deploy key
来反推代码到master。
before_script
mkdir -p <sub>/.ssh
# 将生成私钥写入.ssh中
echo "$RELEASE_PRIVATE_KEY" > </sub>/.ssh/id_ed25519; chmod 0600 ~/.ssh/id_ed25519
echo "StrictHostKeyChecking no " > /root/.ssh/config
# 重置origin
git remote rm origin
git remote add origin git@$CI_SERVER_HOST:$CI_PROJECT_PATH.git
npm config set registry https://xxxx.xiaomi.net/xxxx/npm/mi-npm
3 流程源码
3.1 依赖
项目使用semantic-release工具来发布UI库,需要提前安装以下的依赖。
-
@semantic-release
-
@semantic-release/commit-analyzer
-
@semantic-release/release-notes-generator
-
@semantic-release/changelog
-
@semantic-release/gitlab
-
@semantic-release/npm
-
@semantic-release/git
3.2 CICD环境变量
需要配置的环境变量有三个:
-
NPM_TOKEN
:@semantic-release/npm
发布包到私有源会默认读取该参数。 -
GITLAB_TOKEN
:@semantic-release/gitlab
发布release
到Gitlab
会读取该参数。 -
RELEASE_PRIVATE_KEY
:deploy key
对应的私钥,用于反向推送版本修改信息和changelog
到master
。
3.3 YML配置
# 规则
.rules
&is-merge-request-to-production
if $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $PRODUCTION_BRANCH
# 生产分支 + 不是由发布机器人反推的commit
&is-release
if $CI_COMMIT_REF_NAME != $PRODUCTION_BRANCH || $CI_COMMIT_TITLE =<sub> /^chore \srelease\s/
when never
# 设置环境变量默认值
variables
# 打包反向推送tag、changelog的git用户名
GIT_AUTHOR_NAME'release-bot'
PRODUCTION_BRANCH'master'
# 审批人
CI_APPROVER xxx@xiaomi.com
default
image node 14-alpine
before_script
# 设置为小米私有源
npm config set registry https://xxxx.xiaomi.net/xxxx/npm/mi-npm
cache
key $CI_PROJECT_ID
paths
node_modules
stages
test
publish_npm
deploy_docs
test
stage test
script
yarn
yarn test
rules
*is-merge-request-to-production
*is-release
when on_success
build_and_publish_npm
stage publish_npm
# node14 + git 环境
image timbru31/node-alpine-git14
approvaltrue
before_script
mkdir -p </sub>/.ssh
echo "$RELEASE_PRIVATE_KEY" > <sub>/.ssh/id_ed25519; chmod 0600 </sub>/.ssh/id_ed25519
echo "StrictHostKeyChecking no " > /root/.ssh/config
git remote rm origin
git remote add origin git@$CI_SERVER_HOST:$CI_PROJECT_PATH.git
npm config set registry https://xxxx.xiaomi.net/xxxx/npm/mi-npm
script
yarn
yarn build
yarn semantic-release
rules
*is-release
when manual
build_and_deploy_docs
stage deploy_docs
script
yarn
yarn deploy:site
rules
*is-release
when on_success
3.4 releaserc配置
{
"branches": ["master"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
"@semantic-release/gitlab",
"@semantic-release/npm",
[
"@semantic-release/git",
{
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore: release ${nextRelease.version}"
}
]
]
}
4 结语
通过以上的改造,基本上解决了手动发布带来的所有问题,使得整个发布过程变得更加的规范可控。同时对于开发者来说,也无需花费时间来学习发布的流程,组件库的发布对于开发者来说只需要点击按钮申请发布就可以了。需要注意的是,在该流程下,changelog
和版本号的生成严重的依赖commit message
,为了更好的规范commit message
的格式,可以借助lint-staged
插件在代码提交时对其进行校验(miui-design
项目中已添加该配置,因此本文不再赘述)。
不过以上的方案也存在一个问题,就是只能发布正式版本的包而无法发布alpha
或者beta
版本的包,我猜测这可能也是开源库没有采用自动化发布的原因之一。不过由于目前部门维护的组件库并不会发布alpha
或者beta
版本,因此暂时没有这个问题,以后有机会再研究一下。