ci

持续集成(Continuous integration)想必有经验的工程师都不会陌生,它能帮助我们及早发现并去解决 bug,让产品可以快速迭代,同时还能保持高质量。我们网易云课堂 iOS 端从去年开始也做了持续集成相关工作,技术栈如下:

  1. 一台空闲 Mac mini 作为 CI 服务器
  2. Jenkins 作为持续集成工具
  3. fastlane 将持续集成/部署流程自动化
  4. OCLint 作为静态代码检测工具
  5. SonarQube 作为质量监控工具
  6. 泡泡(内部通讯)作为消息通知工具

痛点

虽然上述工具都已经无缝集成到工程中了,但是总有那么一些让人不爽的痛点在刺激着我,诱导我入坑去做些对个人绩效可能没什么作用,但却能提升整个项目组开发体验的事情。

易用性

第一个开发和测试体验都没那么好的事情就是打包后的通知。每次项目打包完成后也不知道到底是构建成功还是失败了,通知到 QA 去内测分发平台下载时却发现根本没有包(内心OS:??你在逗我),然后 QA 又需要通知到开发这边,开发一脸懵逼,只能登陆到 Jenkins 上查看打包失败的原因,代码修改后重新打包也还是无法立即知道结果,只能听天由命。不管是打包成功还是失败最小路径也超过4条,因为即使是成功后 QA 还是需要登陆到内测分发平台-点击对应项目-点击对应版本-扫码下载。

有没有办法将最小甚至最长路径都缩短到1条呢?我给出了以下的解决方案。打包成功和失败其实无论是 Jenkins 还是 fastlane 都没有对应的返回值可供判断,但我们可以换个思路,fastlane 中的 gym 是打包命令,如果打包成功那肯定会生成一个 ipa 文件,所以我们可以通过判断该文件是否存在来判断打包是否成功。第二步我们会将 ipa 文件上传到内测分发平台,如果上传失败也意味着构建失败,之前上传是用 curl 来做的,获取返回值还要调 shell ,所以我索性用 Python 重写了一下上传过程,如果成功则会返回一个 qr_url 的二维码字段,如果该字段有值则说明整个构建过程成功了!

优化前 优化后(为保证隐私,二维码不可扫)

这样一来打包成功则 QA 直接扫码下载,打包失败开发也可以第一时间知道并直接进入 Jenkins 查看,成功将最长路径缩短为1条。

复用性

我们教育产品部移动端一共有四个产品,如果在 Jenkins 上细分的话更是有数十个 job。我们之前的做法是分别在每个 job 配置相关的构建前/构建中/构建后命令,但我仔细看了一下,每个项目的配置都大同小异,如果每个项目都这样写很难维护和添加新功能,其实完全可以结合 fastlane 可以调用脚本和设置环境变量的功能,抽出一个通用的文件来进行配置。因此我写了一个教育产品部移动端可以共用的 ci.py 的 Python 文件,主要提供了 prepost 两个钩子(hook),可以直接在 fastlane 中进行调用:

1
2
3
4
5
6
7
8
9
10
11
before_all do
sh './ci.py pre'
end
after_all do |lane|
sh './ci.py post'
end
error do |lane, exception|
sh './ci.py post'
end

这样一来 Jenkins 里终于干净了,构建过程只需要写上一句命令:fastlane qa_release

动态性

我一开始觉得上述方案已经可以做到很好的复用了,但 ci.py 总不能直接在每个项目里拷一份。一开始我想的是通过 git submodule 引入,这样就能保证一共只有一份代码,但是我的老大强哥给了我一个更好的建议,直接通过 Jenkins 动态下载脚本代码然后执行。这样的话一来不会侵入现有代码,二来也不需要通知到每个产品开发来引入 CI 脚本,三来又可以保证每次都可以执行到ci.py 最新版本的代码。我觉得这种动态注入的方案非常好,有时间会继续完善一下。

Tips

CI 虽好,但用在 iOS 平台真是蛋疼。每次打包 fastlane 底层需要调用 xcodebuild 命令。而一旦出现同时打包的情况总是会导致前一个 job 失败。我们这边调研过后,发现可以通过设置 fastlane 环境变量来避免这个问题:

1
2
export FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT=120
export FASTLANE_XCODEBUILD_SETTINGS_RETRIES=5

后续规划

  1. 完善静态代码检查报告
  2. 提高单测覆盖率
  3. Android 支持(因为 Android 的开发同事说目前仍是手动代码混淆/加壳/打包,如果有时间可以一起来把这些自动化工作给做起来)


阅读