你好,我是黄俊彬。
在过去的很多咨询项目中,我遇到了很多团队都没有很好运用持续集成流水线的实践。从团队协作的角度上来看,在版本发布过程中,经常出现测试依赖开发手工生成制品、版本发布也从开发本地出版本的问题。
而且项目架构如果从单体演进至组件化架构,随着越来越多的组件分离,以前一次构建可能就能出制品,但是组件化后需要先构建多个组件,然后再进行组件的集成,协作的复杂度也会更高。
最终后果就是团队的协作效率低,版本的质量也没办法控制。开发同学日常的开发工作经常被打断,沦为名副其实的“打包工程师”。
如何解决这些问题呢?最好的方式就是创建可靠、可重复的软件发布过程,让整个过程尽可能地自动化,从而提高整体的集成发布效率。通过自动化减少低价值的重复工作。
今天我们将一起来学习持续集成的核心实践——流水线。我会以Sharing项目为例,带你了解如何设计、配置流水线,最后还会使用GitHub Action来搭建一个持续集成流水线。
持续集成流水线是一种软件开发实践。 如下图所示,每当开发提交代码后,都会触发流水线执行对应的步骤,这些步骤通常包含扫描检查、构建、测试、部署等环节。 如果提交的代码不满足流水线上设置的检查时,流水线的执行就会失败,不允许代码合入仓库。
那么使用持续流水线能够给团队带来什么价值呢?
一方面是明显的效率提升。在没有使用持续集成流水线前,版本的构建发布都得依赖本地构建,如果一天需要构建多个版本,那么效率非常低下。有了流水线,就可以自动化地帮我们完成这些低价值的工作。另外,也可以在团队内形成协作规范,大家都以流水线推送的版本为准,可以减少不必要的沟通。
另一方面是明显的质量提升。本地构建的版本不可控的因素非常多,例如开发本地的构建环节、代码是否拉取了最新、签名是否正确等等,都可能影响最终的制品质量。
有了持续集成流水线,我们就有了统一的构建环境和规范的构建过程,有效保证制品质量稳定。另外,我们可以在流水线上设置相应的质量门禁,如静态代码检查、自动化测试验证、架构守护验证等等,当提交代码不符合要求时,可以不允许入库,这有助于团队尽快发现相关错误。
当然,要让流水线发挥最佳的效果,关键还需要团队能够关注流水线的运行状态,并且及时响应。下面我给你分享2个在实践过程中需要遵循的流水线纪律。
第一个是流水线如果构建失败了,不允许提交代码,尽快修复。当流水线构建失败时,我们应该立马排查失败的原因,尽快修复,避免影响出版本。如果短时间内无法修复,应该及时回滚代码,不要影响团队其他人的代码合入。
第二个是每天下班前一定要保证最后一次流水线构建是成功的。这个纪律我们叫CI红不过夜,通常我们要保证至少每天都有一个可用于测试的版本,这样才不会影响第二天的测试及相关人员拿到版本作验证。
对于组件化的架构来说,由于抽离了多个组件,通过自动化来管理组件的发布及集成作用就更加明显了。如果不能通过自动化来转移这部分复杂度,那么对于团队来说,拆分组件也许是另外一种负担,需要频繁地手工管理组件的构建发布,组件的集成发布等问题。
因此,我们需要设计组件化架构的流水线,实现组件管理的自动化。接下来,我们就结合Sharing来看看如何设计流水线。
首先,我们需要给流水线做个分级,拆分为组件流水线以及基座流水线。
组件流水线的设计分主要分为4个操作步骤,也就是质量门禁检查、代码检视、组件制品管理及发布通知。
具体步骤是后面这样。
1.组件开发人员提交代码后自动触发流水线质量门禁检查,质量门禁包含代码扫描、编译及自动化测试等环节。 2.团队内架构师进行检视后代码合入。 3.发布最新的组件版本到maven制品库。 4.将构建结果及制品通知到相关干系人。
基座流水线的设计主要分为5个操作步骤,分别是提交组件集成清单、进行质量门禁检查、代码检视、发布制品到版本库及最后确认进行发布。
具体步骤是后面这样。
1.产品团队根据版本规划,提交组件集成版本清单。 2.质量门禁包含代码检查、编译、集成测试、性能测试等。 3.团队内架构师进行检视,产品经理进行确认后代码合入。 4.发布最新的应用版本到制品库。 5.团队确认后选择渠道(内测、灰度、应用市场、全量)进行发布。
在实际过程中,我们可以根据项目情况以及内部的工具完善度,灵活调整流水线的各个步骤。流水线的设计应该和代码一样,可以持续迭代,而不是一成不变。比如通常来说,最后的发布阶段不会是自动化的,还需要进行人工测试确认后再触发流水线进行发布部署。
目前持续集成工具非常多,例如Jenkins、GitLab CI、GitHub Action、AzureDevOps等等,由于Sharing项目目前在GitHub托管,所以我们使用GitHub Action来演示如何搭建流水线。
首先来看看如何基于GitHub Action配置流水线。GitHub Action采用yml配置文件的方式来描述CI的构建状态,在.github/workflows目录下创建对应的yml文件即可。
你可以参考后面的代码,为了方便你理解,我在每个配置上备注了具体的注释。
//配置工作流的名称
name: Sharing CI
//配置当master分支有PR或者代码提交时会触发该流水线
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
//配置运行的任务
jobs:
build:
//配置运行的服务器环节,一般使用默认
runs-on: ubuntu-latest
//配置流水线的步骤
steps:
//配置系统的构建环境
- uses: actions/checkout@v3
- name: set up JDK 11
uses: actions/setup-java@v3
with:
java-version: 11
distribution: 'temurin'
cache: gradle
//检查Gradle运行环境
- name: Grant execute permission for gradlew
run: chmod +x gradlew
//执行静态代码扫描,使用lint检查
- name: Code Scan
run: ./gradlew app:lint
//执行代码架构检查
- name: Arch Test
run: ./gradlew app:testDUT --tests com.jkb.junbin.sharing.ArchRuleTest
//执行功能自动化冒烟测试
- name: Smoke Test
run: ./gradlew app:testDUT --tests com.jkb.junbin.sharing.SmokeTesting
//构建版本
- name: build
run: ./gradlew app:assembleRelease
//推送版本到artifact制品库
- name: Upload apk to artifact
uses: actions/upload-artifact@v3
if: always()
with:
name: sharing_app
path: ${{ github.workspace }}/app/build/outputs/apk/release/*.apk
完成了流水线的配置以后,当有代码合入时则会触发流水线的运行,你可以在项目的Action选项中查看对应的构建情况。
另外,除了自动触发以外,你也可以点击进入详情,手动重新触发流水线的执行。
当流水线执行成功以后,则可以在详情中查看到构建的制品。
这里特别推荐采用配置文件的方式来定义流水线,这样一方面可以将流水线的变更和代码也一同纳入到版本管理中,另外由于配置文件和代码在一起管理,也可以让开发的同事更关注流水线的设计。
目前主流的持续集成工具都支持使用配置文件定义流水线的方式,只是不同的工具配置的语法稍有差异,整体的流水线设计是通用的。
今天我们一起学习了持续集成流水线的相关实践。持续流水线是一种软件开发的实践,目的是通过自动化为软件的发布创造一个稳定且可重复的过程。
流水线带来的效果是显而易见的,从效率上帮助我们减少低价值的重复工作,例如本地编译打包,另外也能减少团队成员间不必要的沟通。
从质量上看,统一了构建发布环境,整个环境会更可靠,减少了人工操作带来的意外风险。另外,结合流水线增加质量门禁,可以在版本发布前检查代码质量,避免不符合规范及要求的代码合入代码仓库中。
当然要让流水线发挥最佳的作用,还得依靠团队成员共同来遵循流水线的纪律,保障流水线红不过夜,当运行失败时能及时修复。
对于组件化架构的流水线设计,可以采用分层分级的方式。具体就是通过设计组件的流水线来管理组件的构建和版本管理,另外再设计集成的流水线来管理组件的集成和发布。
最后,我还通过GitHub Action给你演示了如何搭建一个持续集成流水线,GitHub Action采用yml配置的方式来定义流水线,这种方式对于开发人员来说更加友好,只需要按照特定的语法格式描述步骤即可,同时这个配置文件也能和代码一同进行版本管理。在实践过程中推荐使用这种配置文件定义流水线的方式。
下节课是持续交付篇的最后一节课,我们将学习研发过程中的度量,看看如何通过度量来帮助我们发现问题以及持续改进,敬请期待。
感谢你学完了今天的内容,今天的思考题是这样的:你的项目有持续集成流水线吗?在使用过程中你有遇到哪些问题呢?
文章来源:极客时间《大型 Android 系统重构实战》