浅谈前端组件发布自动化
从09年angularJS被谷歌推出以来,涌现了很多前端应用的开发、设计等方面优秀的解决方案(框架 、库、设计规范)。随着SPA的普及,"Web Components"的设计标准渐渐开始影响各个框架的设计方向,开发人员的逻辑封装也不再像以前效率相对低下,优秀的框架给开发者提供了简洁且快速的组件化解决方案。
本文会尽量少的贴代码,尽量少的谈及工程化本身的东西,旨在分享组件自动化基础设施建设过程中的些许经验。
工程化的内容后续会有单独的“毒文”来祸害大家。
一、应用背景
如上所述,中大型的团队或公司在未来的一段时间,都需要封装足够多且完善的UI组件来支撑快速开发和降低学习成本。这样的情境下,就会有一些人,去进行组件的标准化开发,快速迭代,而随之而来且亟待解决的问题:
- 组件的版本控制
- 组件的产出物(包、文档)的相关性
- 组件构建、测试的冗长的工作量
- 组件发布环节的不可控
- 权限的不可控性(组件仓库权限、源码仓库权限)
组件发布自动化的目的不仅仅是减少你的手动构建、发布的工作量,它的本质是对多人协同组件开发方式的规范和管控。
没有这些你的前端研发工作也能支撑?能用和好用是两个概念。
二、涉众及相关技术栈
1、人员涉众
- 开发人员:组件开发者,源码的提交者
- 配置管理员:版本发布的流程、权限控制者,同时也是发布节奏的控制者
2、相关技术名词
了解以下,阅读过程会相对轻松:
- nodeJS:自动化流程中的主要参与者
- gradle:负责整体流程的包装,同时也会用到其生态中的一些插件
- jenkinsFile:推荐用声明式(groovy),本文基于脚本式
- webdav:用来做文档发布的服务器(优势是可控的发布权限),可以用其他web容器替代
3、工程职责
为了详略得当,这里列举工程的一些基础功能(这里默认已经实现,实际需要我们自己根据我们的实际情况去实现),以免赘述与本文不相关的内容。
- 包输出:工程提供构建的基础功能(将源码输出为npm仓库的package的功能)
- 文档输出:工程提供文档输出的功能(推荐markdown手写合成的体验较好的文档输出机制)
- 测试:提供单元或e2e测试的基础框架
三、流程概览
如上图所示,发布流程主要为(区别化的内容加粗):
- 开发人员提交代码(git push)/配置管理员打tag
- git接到push/tag,根据配置好的webhook给jenkins发请求
- jenkins接收请求并触发指定的task,根据jenkinsFile执行pipeline:
- 更新项目并检查设置
- sonar:推送代码到sonar
- 测试:单元测试&&e2e测试
- package构建
- package发布:计算版本号并发布package
- 根据git 的最后一条log计算下一版本号
- 根据git的最后一条log区分发布测试版本(snapshots)还是正式版本(release)
- 构建doc
- 发布doc:根据git的最后一条log区分发布测试版本文档(snapshots)还是正式版本文档(release)
四、流程详述(冗长的细节,可略)
1、update project - 更新项目并检查设置
echo 'update source'
checkout scm
echo 'reset config'
sh './gradlew widget-pkg-reset-config -q'
echo 'update dependencies'
sh 'npm install && npm update'
- 更新(初始化)源码:jenkins行为,更新项目源码
checkout SCM
(也可用gradle或node执行代码更新) - 更新(初始化)gradle依赖:gradle行为,根据gradle配置,更新gradle相关依赖
- 更新(初始化)npm依赖:node行为,根据pkg版本,更新相关依赖(可能为运行时依赖,也可能为开发时依赖)
- 检查配置: gradle包装(node行为),检查当前构建环境的正确性,如当前所需的环境变量、当前所设置的仓储地址、当前所需要的所有构建环境的版本等,如缺失则发出警告并中断集成,如不正确则重置正确环境设置。
2、test - 测试
暂略。
3、build source - 构建资源
echo 'Building source..'
sh 'npm run pre-release'
echo 'Build source completed'
- 构建资源:node行为,进行资源构建。
4、publish source
sh './gradlew widget-pkg-modify-version -q'
sh './gradlew widget-pkg-set-config -q'
echo 'Publish source..'
sh 'npm run publish-to-npm'
- 修改版本号: gradle包装(node行为),根据插件计算出的
currentVersion
,临时修改当前构建结果中的package.json文件的version
字段。 - 设置相关发布配置:gradle包装(node行为)。
- 根据当前版本状态选择发布的仓库
- 根据当前环境变量设置当前发布仓储的认证信息
- 发布资源:node行为,发布构建资源。
5、build doc
echo 'Building doc..'
sh 'npm run build'
echo 'Build doc completed'
- 构建资源:node行为:构建当前使用手册
6、publish doc
echo 'Publish doc..'
sh './gradlew widget-docs-publish -q'
echo 'source doc completed'
- 清除已发布文挡:gradle行为,尝试清除已经发布在webdav上的当前版本文档(为了防止文挡重复发布的文件冗余,因为前端静态有文件名有MD5后缀,重复发布无法覆盖)。
- 修改文挡资源的BASE:gradle包装(node行为),根据文挡发布需要,在webdav的服务目录下,文件夹深度决定url上下文层级,构建后的静态为angular工程,需要上下文,否则无法正常调用web history,所以需要根据当前的
currentVersion
修改当前文档资源的base href
。 - 发布文档:gradle行为,调用gradle插件进行递归发布。
五、专项剖析
1、不同层级的行为及调用关系分析
2、版本的计算
2.1、版本发布到正式、测试仓库的判断规则
检查当前仓储的最后一次行为(git log):
- 如果没有tag,版本从0.1.0-SNAPSHOT开始
- 如果有tag,但最新的commit不是tag,则根据语义规范发布相应版本的SNAPSHOT版本
- 如果有tag,且最新的commit是tag,则根据tag发布
2.2、版本递增计算规则
请综合参照:
- shifted-semver-increment
- Semantic Versioning 2.0.0
- git commit 使用及规范
3、如何指定版本号发布
在发布时遇到的一个问题,java的包发布的版本可以通过gradle变量来指定,那前端的package的版本号如何动态指定?在npm文档搜索无果后,决定写插件去修改“package.json”的version
来指定发布版本。
为了避免导致其他问题,建议在初始化的步骤将“package.json”进行checkout
。
五、总结和思考
1、如何在“简化技术栈”与“前后端自动化统一”上做出取舍?
这其中的一个关键点为,前端自动化为什么要使用gradle。
在本文的实践中,受我们团队已经存在后端的自动化工作流的影响,为了与其统一版本的升级规则,会用到gradle的“axion-release-plugin”插件,以及在发布到webdav时会用到“uk.co.firstzero.webdav”,出于前后端统一和不重复造轮子考虑,前端才增加gradle的使用。
那么我们不用gradle可以吗?当然可以,你只需要以上提到的内容用node再造一遍轮子,祝你好运。
送一个版本计算的node轮子(不再更新,但可供参考):axion-release-node-plugin
2、前端有各种lint,sonar相对前端的意义大吗?
后续补充。