iOS开发必备 - 搭建自动化构建服务
本篇文章讲述了如何使用Jenkins搭建iOS的自动化构建环境。
iOS自动化构建官方有篇文章介绍Xcode持续集成, 是基于Xcode Server以及Xcode Bots去实现, 但是系统依赖和版本依赖过于严重, 并且需要在Xcode中配置Target的Shared属性, 耦合性太强, 因此不建议使用。
为什么客户端需要自动化构建
个人觉得最主要的原因是需要提高编码效率, 其次是方便测试与集成
-
<font size=4>场景: 开发过程中, 不断的有产品或者测试介入, 需要你不定时的打包给他们提供体验与测试。</font>
对于小工程来说, build一下可能就是几秒钟的事情, 但是对于大工程来说, 一次打包可能耗费5分钟乃至10分钟的开发时间。开发者一天中的有效开发时间是有限的, 在不断被打断的情况下, 开发者的产能实际上十分低效的。 -
<font size=4>场景: 联调过程中, 需要有第三方的SDK接入联调, 需要不断的为对方更改SDK, 然后重新编译打包。</font>
这种场景一般发生在多个公司协作的时候, 本人在开发某二手车批发软件的时候, 就曾遇到与第三方支付平台接入联调的问题, 一天中80%时间为对方提供打包服务, 生产效能过低。 -
<font size=4>场景: 用户预览&客户演示。</font>
这个场景比较类似第一个场景, 不过是面向不专业的人事或者客户, 需要不定时更新版本然后提供企业证书打包的预览版给老板和客户。严格意义上来说, 与第一个场景是同样一个场景。 -
<font size=4>场景: 测试集成。</font>
自动化构建和持续集成本来就是一码事, 如果你是个喜欢写集成测试的开发者, 那么在每一次打包编译的时候都要运行一次集成测试是很浪费时间的(PS: 集成测试不通过这个场景除外哦`)。将测试集成在构建步骤里面, 可以让自动化集成的所有行为在另外一台机器上工作, 工程师该干嘛还是干嘛去。
还有其他很多应用场景, 不过我个人暂时为了上述目的而搭建CI环境。最主要的目的是解放打包时间, 防止开发工作被打断。
准备工作
硬件环境
- <font size=4>搭载Macintosh系统的一台硬件设备或虚拟机。</font>
既然是编译iOS应用, 那自然少不了苹果一系列的机器啦, 一台搭载Macintosh OS操作系统的电脑还是必须的, 目前本人采用的硬件方案是Mac mini。
需要Macintosh是因为Xcode运行环境在该系统上, 因此如果你想利用脚本编译iOS应用, 你就需要Xcode, 如果你需要Xcode, 那你又必须处于Macintosh OS环境(哎~ 苹果的生态链)。
鉴于目前国内的云服务器均为windows或者Linux, 您如果想用Geek的方式去搭建iOS的自动化构建方式, 可以尝试在云服务器上安装虚拟机然后运行Mac OS环境的方案。 - 在准备做CI的机器上安装Java SDK环境 & Xcode。
Xcode可以用过App Store直接安装, Xcode体积非常大, 但是因为iOS编译脚本xcodebuile命令依赖于Xcode,因此Xcode是必须安装的。
Java运行环境是Jenkins需要, 因为Jenkins是基于Java实现的一套构建系统, Java SDK可以通过Oracle的官网去下载最新的适用版本, 在本文书写的时候, 最新的Java SDK是JDK8, 下载地址为 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
安装Jenkins
- 在Mac OS环境下, 建议采用Homebrew来安装, 获取Homebrew建议采用官网的方式。通过Homebrew官网获取homebrew后执行如下命令:
$ brew install jenkins
- 利用homebrew安装完毕后, 可以在终端执行
which jenkins
查看是否已经安装成功。 - 在终端执行
jenkins
命令启动Jenkins(PS: 默认占用8080端口会导致启动失败), 此处有可能会出现Jenkins ssl证书过期的问题, 可以通过修改os系统时间, 调整为ssl支持的时间来解决, 否则安装Jenkins插件可能会失败。(jenkins命令依赖Java开发环境, 旧版本的Jenkins需要自己去配置bash alias) - 打开浏览器, 访问http://localhost:8080, 查看Jenkins是否正常启动(默认端口8080)。
配置工作
Jenkins插件配置
Jenkins项目Jenkins是个生态比较完善的自动化构建系统, 提供了各种的可扩展插件给用户下载, 同时又开放接口, 让大家自主开发对应的插件。插件管理路径是系统管理
->管理插件
插件索引默认并不是最新的, 需要大家手动更新, 在管理插件
->可选插件
的右下角有个立即获取
按钮, 点击即可获取最新的插件索引。
PS: <font color='red'>有时候官方索引的地址ssl证书过期导致索引会更新失败, 大家可以通过浏览器调试获取证书到期时间, 然后将本机时间调整为过期以前的时间即可解决该问题。</font>(apache有时候也不靠谱啊。。)
每个开发者根据不同的业务场景需要用到不同的插件, 本文就不在这里赘述, 大家自由发挥吧。
Jenkins账户权限配置
Jenkins用户权限配置和本文主题无关, 主要是方便大家对Jenkins用户进行访问控制。可以参考文章"Jenkins安全配置/访问控制/审计"或文章"Jenkins使用经验谈5(用户的登录与权限设定等)"进行配置。
我个人是比较喜欢采用安全域里的Jenkins专有用户数据库
进行配置, 因为自带的比较方便, 而且在不复杂的企业环境里面已经够用, 负责的企业环境就另当别论了。大致流程如下:
- 在Jenkins页面上注册一个新用户(当然可以导入)
- 在
系统管理
->Configure Global Security
里的授权策略进行定向配置, 个人偏好采用项目矩阵授权策略
进行管理。
项目&脚本配置
本文重点是如何自动化编译iOS项目, 这个其实大家应该很熟悉了, 就是利用xcodebuild
命令和xcrun
组合进行脚本编译和签名。编译脚本的编写本质上不缺分环境(Jenkins服务器环境or本机环境), 因此我们需要确保本机环境的脚本能够正常的工作, 个人偏好使用bash环境(因为不用安装其它的依赖), 然后使用下述的模板进行填空。
#!/bin/bash
projAbbr='iOS_Bash_Build_Demo'
function autoBuild()
{
prepareBuildEnv
updatePodsDeps
clearProjs
buildProjs
signProjs
uploadAppFile
echo ${projAbbr} '- ALL Action Completed!'
}
function prepareBuildEnv()
{
# TODO - 执行编译器准备工作, 可以调整项目配置等执行任务
# eg: mkdir xxx
}
function updatePodsDeps()
{
# TODO - 更新Pod依赖
# eg: pod install
}
function clearProjs()
{
# TODO - clean项目
# eg: xcodebuild -workspace "demo.xcworkspace" -scheme "targetName" -configuration 'Release Adhoc' clean
}
function buildProjs()
{
# TODO - 编译项目, 生产带证书签名的App文件
# eg: xcodebuild -workspace "demo.xcworkspace" -sdk iphoneos -scheme "targetName" -configuration 'Release Adhoc' SYMROOT='$(PWD)'
# eg: xcodebuild -workspace "demo.xcworkspace" -sdk iphoneos -scheme "targetName" -configuration 'Release Adhoc' CODE_SIGN_IDENTITY="keychain中证书代号名称" SYMROOT='$(PWD)'
}
function signProjs()
{
# TODO - 填充签名脚本, 用于签名编译到的App, 打包成ipa文件
# eg: xcrun -sdk iphoneos PackageApplication -v "demo.app" -o "demo.ipa" > $logPath
}
function uploadAppFile()
{
# TODO - 上传App文件到发布平台
# 蒲公英、Fir.im都是不错的选择哦
}
# 如果使用第三方平台, 一般不需要createPlist, 因为第三方平台都已经集成
function createPlist()
{
# TODO - 创建Plist文件, 用于企业证书下载
}
autoBuild
上述脚本将编译步骤拆分为准备环境
、更新Cocoapods
、clean项目
、build项目
、签名项目
和上传ipa文件
六大步骤。脚本的每一个步骤其实都可以在Jenkins需找对应的插件进行图形化控制, 但是我本人喜欢编写脚本来轻量化Jenkins的依赖, 除非需要本地上传一些依赖配置的文件。
上述脚本的执行任务也可以通过组合CocoaPods Jenkins Integration
插件、Xcode integration
插件以及其他上传插件进行组合。但是我并不太喜欢过渡依赖Jenkins的图形界面, 因此并没有深度整合这两个插件。
<font style='bold' color='black'>脚本的核心代码注释在代码里的eg, xcodebuild命令进行clean以及build, 然后通过xcrun进行签名app生成ipa文件</font>
clean清理脚本示例:
xcodebuild -workspace "demo.xcworkspace" -scheme "targetName" -configuration 'Release Adhoc' clean
build编译脚本示例:
xcodebuild -workspace "demo.xcworkspace" -sdk iphoneos -scheme "targetName" -configuration 'Release Adhoc' CODE_SIGN_IDENTITY="keychain中证书代号名称" SYMROOT='$(PWD)
xcodebuild是Xcode Command Tool工具集里的一个命令, 可以通过-workspace
指定固定的xcode工作空间或者通过-project
指定固定的xcode项目。详细的参数可以参考官方文档。
sign签名脚本示例:
xcrun -sdk iphoneos PackageApplication -v "demo.app" -o "demo.ipa"
xcrun命令可以将app文件打包成ipa文件, 主要依赖xcodebuild命令中指定的证书, 如果没有有效证书的话, 只能在越狱的环境中安装ipa。
大家可以尝试填充这个脚本, 来进行本地编译, 如果本地编译不通过, 那放在Jenkins上是肯定通过不了的。
具体的脚本内容每个人各不相同, Github上有个叫BashShell的项目里包含一个脚本示例build_install.sh, 该项目作者编写的脚本非常详细, 大家可以参考编写。
- xcodebuild命令可以指定对应的签名文件, 可以通过预先上传签名文件到编译服务器的keychains中或者将证书存放于代码托管库中方便指定。
- 通过Jenkins执行的脚本会默认注入一些全局环境变量, 例如
${PWD}
、${CHANGES}
等等。通过这些全局参数可以在执行脚本或者邮件等任务中打印动态数据。 - Jenkins默认将返回值-1作为失败条件, 因此如果需要主动在脚本终止任务,
return -1
就可以触发Jenkins任务失败了。
好了, 准备工作已经差不多了, 可以先将自己的脚本本地测试下了哦~ 编译通过了就可以开始配置Jenkins任务啦~
开始自动化之旅
新建任务
编译脚本、编译环境都准备好, Jenkins也正常运行了, 接下来就是创建Jenkins任务了。
-
通过
新建
构建一个自由风格的软件项目。(同一个分支不同的任务也需要建立多个不同的Job, 每一个Job在服务器Home目录下的.jenkins文件夹下面会创建一个独立的job和workspace) -
在配置界面配置对应的
源码管理
, 现在国内大部分开发者的开发项目应该依托于gitlab
的吧。(gitlab需要制定对应的版本号) -
设定自动执行循环时间: 在
构建触发器
勾选Poll SCM
, 可以通过特定的表达式进行触发设定。点击输入框后方问号会提示触发表达式的规则与书写方式。- 例如12小时检查一次源码库更新可以表达如下:
H H/12 * * *
- 例如30分钟检查一次源码库更新可以表达如下:
H/30 * * * *
-
在
构建
栏点击增加构建步骤
, 选择Execute Shell
, 在Command栏里面执行预先保存的脚本文件。假如默认保存在项目根目录下的脚本文件名为jenkins.sh, 则输入如下代码:sh jenkins.sh
- PS: 此处如果使用图形插件的同学们, 可以在这里添加对应的插件。
-
构建后任务可以添加邮件提醒以及上传FTP等执行动作。
自动发送邮件
- Jenkins默认提供了编译错误时候的自动邮件发送功能。 简易邮件功能需要在
系统管理
中的系统设置
中的邮件通知
栏进行设置。设置了邮件通知全局配置后, 在Jenkins Job配置的增加构建后步骤
中可以配置简易的邮件通知, 简易版本的邮件通知仅仅支持在系统不稳定的时候通知相关的责任人与固定的邮件接收者。 - Email Extension Plugin: Jenkins一个很强大的邮件系统, 可以配置各种各样的邮件内容, 也可以定制不同的触发条件以及接受者。配置使用可以参考其他博文, 例如这篇《Jenkins中配置ESB构建后自动发邮件》
自动化测试
自动化测试可以参考构建脚本的使用方式, 单独书写shell任务去执行对应的测试任务, 也可以在构建脚本中提前穿插测试任务。本文中自动化测试并不是重点, 再这里不讨论, 后期更新相关内容。
自动上传
自动化的最后一步就是提供方便的下载地址给需要安装App的测试体验人员。需要一个平台提供一个方便苹果app下载的平台, 目前市面上以fir.im以及蒲公英使用率最广。
-
第三方平台: 以
fir.im
为例子, fir.im平台集成了ipa文件下载所需要plist文件以及一张优美的下载页。ipa文件第一次需要自己上传产生一个对应url地址。因为该平台本身依赖于七牛云存储, 因此使用该平台的上传脚本可以使用七牛的qrsync
脚本实现, 具体怎么上传请参考fir.im的官方指南文档。 -
自建平台: 自建平台比较麻烦, 需要产生对应的plist文件以及下载页面, 但是在很多高保密的企业下是不允许使用第三方平台的, 个人建议为公司搭建一个简易版本的fir.im, 方便以后使用。
- 构建平台主要是承担的工作是企业证书下载环境的搭建以及历史版本的维护。企业证书下载环境最关键的部分是plist描述文件的生产以及https服务的支持(iOS7以后的items-service服务需要ssl且不接受伪造证书)。
- Bryce Zhange的博文里面有一篇教大家使用Apache和PHP搭建一个服务器的博文《iOS自动化编译》, 大家可以参考。该博文包含了如何使用Bruce(heyuan110)的编译脚本以及如何采用
cat
&EOF
&>>
去动态生成plist文件。
PS: 个人不建议采用Apache搭建服务器, 可以试试Ngnix~ 其实最好是不要自己搭建了~
总结
本文主要是总结了我个人喜爱的方式, 使用Jenkins+Bash脚本在Mac OS X环境下搭建一个自动化构建环境。通过自动化构建环境来将自己从体验人员和测试人员不断要求安装包的麻烦中解放出来专心编写工程代码。Bash主要在于xcodebuild
和xcrun
命令的使用, 以及提供了本人比较爱好的一套模板方便大家快速编写。Jenkins主要用于定时轮询代码和邮件通知。
文章的作用是给我本人备忘用的哈~ 水平有限, 有错误之处请大家及时指出哈~