根据项目需求,现要在团队内部搭建一个统一的打包平台,实现对 iOS 和 Android 项目的打包。而且为了方便团队内部的测试包分发,希望在打包完成后能生成一个二维码,体验用户(产品、运营、测试等人员)通过手机扫描二维码后就能直接安装测试包。
该需求具有一定的普遍性,基本上所有开发 APP 的团队都可能会用到,因此我将整个需求实现的过程整理后形成此文,并且真正地做到了零基础上手,到手即飞、开箱即用
,希望能对大家有所帮助。
首先,先给大家展示下平台建设完成后的整体效果:
该平台主要实现的功能有 3 点:
.ipa
、.app
、.apk
、info.plist
等文件),供有需要的用户进行下载。接下来,本文就开始对平台建设的完整实现过程进行详细介绍。
Jenkins 依赖于 Java 运行环境,因此需要首先安装Java。
安装 Jenkins 的方式有多种,可以运行对应系统类型的安装包,可以通过 docker 获取镜像,也可以直接运行war
包。
我个人倾向于直接运行war
包的形式,只需下载jenkins.war
后,运行如下命令即可启动 Jenkins。
$ nohup java -jar jenkins_located_path/jenkins.war --httpPort=88 &
如果不指定httpPort
,Jenkins 的默认端口为 8080。
Jenkins 有非常多的插件,可以实现各种功能的扩展。
针对搭建的 iOS/Android 持续集成打包平台,我使用到了如下几个插件。
安装方式也比较简单,直接在 Jenkins 的插件管理页面搜索上述插件,点击安装即可。
在 Jenkins 中,构建项目以 Job 的形式存在,因此需要针对每个项目创建一个 Job。有时候,一个项目中可能有多个分支同时在进行开发,为了分别进行构建,也可以针对每个分支创建一个 Job。
创建 Job 的方式有多种,本次只需要创建Freestyle project
类型的即可。
Main page
->New Item
->Freestyle project
对于一个持续集成打包平台,每次打包都由 4 步组成:触发构建、拉取代码、执行构建、构建后处理。对应的,在每个 Job 中也对应了这几项的配置。
要对项目进行构建,配置项目的代码仓库是必不可少的。由于当前我们的项目托管在 GitHub 私有仓库中,因此在此需要对Git
进行配置。
在【Source Code Management】
配置栏目下,如果之前GIT plugin
安装成功,则会出现Git
选项。
配置 Git 代码仓库时,有三项是必须配置的:仓库 URL 地址(Repository URL
)、仓库权限校验方式(Credentials
),以及当前 Job 需要构建的代码分支(Branches to build
)。
在配置Repository URL
时,选择HTTPS URL
或SSH URL
均可。不过需要注意的是,Credentials
要和Repository URL
对应,也就是说:
Repository URL
是HTTPS URL
形式的,那么Credentials
就要采用 GitHub 用户名密码的校验方式;而且,如果在 GitHub 中开启了2FA(two-factor authentication)
,那么还需要在 GitHub 中创建一个Personal access token
,输入密码时将这个Personal access token
作为密码进行输入。Repository URL
是SSH URL
形式的,那么就需要先在 Jenkins 所在的服务器上创建一个SSH
秘钥对,并将公钥添加到 GitHub 的SSH keys
中,然后在填写Credentials
时,选择SSH Username with private key
的校验方式,填入 GitHub Username、SSH 私钥、以及创建SSH
秘钥对时设置的Passphrase
。如果对 Git 权限校验的概念还比较模糊,可以参考《深入浅出 Git 权限校验》。
在配置Branches to build
时,可以采用多种形式,包括分支名称(branchName
)、tagName
、commitId
等。其中分支名称的形式用的最多,例如,若是构建master
分支,则填写refs/heads/master
,若是构建develop
分支,则填写refs/heads/develop
。
除了以上关于 Git 的必填配置项,有时根据项目的实际情况,可能还需要对 Jenkins 的默认配置项进行修改。
比较常见的一种情况就是对clone
的配置进行修改。
在 Jenkins 的默认配置中,clone
代码时会拉取所有历史版本的代码,而且默认的超时时限只有 10 分钟。这就造成在某些项目中,由于代码量本身就比较大,历史版本也比较多,再加上网络环境不是特别好,Jenkins 根本没法在 10 分钟之内拉取完所有代码,超时后任务就会被自动终止了(错误状态码 143)。
这种问题的解决方式也很简单,无非就是两种思路,要么少拉取点代码(不获取历史版本),要么提高超时时限。对应的配置在Advanced clone behaviours
中:
Shallow clone
:勾选后不获取历史版本;Timeout (in minutes) for clone and fetch operation
:配置后覆盖默认的超时时限。代码仓库配置好了,意味着 Jenkins 具有了访问 GitHub 代码仓库的权限,可以成功地拉取代码。
那 Jenkins 什么时候执行构建呢?
这就需要配置构建触发策略,即构建触发器,配置项位于【Build Triggers】
栏目。
触发器支持多种类型,常用的有:
构建触发器的选择为复合选项,若选择多种类型,则任一类型满足构建条件时就会执行构建工作。如果所有类型都不选择,则该Jenkins Job
不执行自动构建,但可通过手动点击【Build Now】
触发构建。
关于定时器(Schedule)的格式,简述如下:
MINUTE HOUR DOM MONTH DOW
通常情况下需要指定多个值,这时可以采用如下 operator(优先级从上到下):
*
适配所有有效的值,若不指定某一项,则以*
占位;M-N
适配值域范围,例如 7-9 代表 7/8/9 均满足;M-N/X
或*/X
:以 X 作为间隔;A,B,C
:枚举多个值。另外,为了避免多个任务在同一时刻同时触发构建,在指定时间段时可以配合使用H
字符。添加H
字符后,Jenkins 会在指定时间段内随机选择一个时间点作为起始时刻,然后加上设定的时间间隔,计算得到后续的时间点。直到下一个周期时,Jenkins 又会重新随机选择一个时间点作为起始时刻,依次类推。
为了便于理解,列举几个示例:
H/15 * * * *
:代表每隔 15 分钟,并且开始时间不确定,这个小时可能是:07,:22,:37,:52
,下一个小时就可能是:03,:18,:33,:48
;H(0-29)/10 * * * *
:代表前半小时内每隔 10 分钟,并且开始时间不确定,这个小时可能是:04,:14,:24
,下一个小时就可能是:09,:19,:29
;H 23 * * 1-5
:工作日每晚 23:00 至 23:59 之间的某一时刻;触发策略配置好之后,Jenkins 就会按照设定的策略自动执行构建。但如何执行构建操作,这还需要我们通过配置构建方式来进行设定。
常用的构建方式是根据构建对象的具体类型,安装对应的插件,然后采用相应的构建方式。例如,若是构建Android
应用,安装Gradle plugin
之后,就可以选择Invoke Gradle script
,然后采用Gradle
进行构建;若是构建iOS
应用,安装Xcode integration
插件之后,就可以选择Xcode
,然后选择Xcode
进行构建。
该种方式的优势是操作简单,UI 可视化,在场景不复杂的情况下可以快速满足需求。不过缺点就是依赖于插件已有的功能,如果场景较复杂时可能单个插件还无法满足需求,需要再安装其它插件。而且,有些插件可能还存在一些问题,例如对某些操作系统版本或 XCode 版本兼容不佳,出现问题时我们就会比较被动。
我个人更倾向于另外一种方式,就是自己编写打包脚本,在脚本中自定义实现所有的构建功能,然后在Execute Shell
中执行。这种方式的灵活度更高,各种场景的构建需求都能满足,出现问题后也能自行快速修复。
另外,对于 iOS 应用的构建,还有一个需要额外关注的点,就是开发者证书的配置。
如果是采用Xcode integration
插件进行构建,配置会比较复杂,需要在 Jenkins 中导入开发证书,并填写多个配置项。不过,如果是采用打包脚本进行构建的话,情况就会简单许多。只要在 Jenkins 所运行的计算机中安装好开发者证书,打包命令在 Shell 中能正常工作,那么在 Jenkins 中执行打包脚本也不会有什么问题。
完成构建后,生成的编译成果物(ipa/apk)会位于指定的目录中。但是,如果要直接在手机中安装ipa/apk
文件还比较麻烦,不仅在分发测试包时需要将好几十兆的安装包进行传送,体验用户在安装时也还需要通过数据线将手机与计算机进行连接,然后再使用 PP 助手或豌豆荚等工具进行安装。
当前比较优雅的一种方式是借助蒲公英(pgyer)
或fir.im
等平台,将ipa/apk
文件上传至平台后由平台生成二维码,然后只需要对二维码链接进行分发,体验用户通过手机扫描二维码后即可实现快速安装,效率得到了极大的提升。
不管是蒲公英
还是fir.im
,都有对应的 Jenkins 插件,安装插件后可以在Post-build
中实现对安装包的上传。
除了使用 Jenkins 插件,fir.im
还支持命令上传的方式,蒲公英
还支持HTTP Post
接口上传的方式。
我个人推荐采用命令或接口上传的方法,并在构建脚本中进行调用。灵活是一方面,更大的好处是如果上传失败后还能进行重试,这在网络环境不是很稳定的情况下极其必要。
Jenkins 成功完成安装包上传后,pgyer/fir.im
平台会生成一个二维码图片,并在响应中将图片的 URL 链接地址进行返回。
二维码图片的 URL 链接有了,那要怎样才能将二维码图片展示在 Jenkins 项目的历史构建列表中呢?
这里需要用到另外一个插件,description setter plugin
。安装该插件后,在【Post-build Actions】
栏目中会多出description setter
功能,可以实现构建完成后设置当次 build 的描述信息。这个描述信息不仅会显示在 build 页面中,同时也会显示在历史构建列表中。
有了这个前提,要将二维码图片展示在历史构建列表中貌似就可以实现了,能直观想到的方式就是采用HTML
的img
标签,将<img url='qr_code_url'>
写入到 build 描述信息中。
这个方法的思路是正确的,不过这么做以后并不会实现我们预期的效果。
这是因为 Jenkins 出于安全的考虑,所有描述信息的Markup Formatter
默认都是采用Plain text
模式,在这种模式下是不会对 build 描述信息中的 HTML 编码进行解析的。
要改变也很容易,Manage Jenkins
-> Configure Global Security
,将Markup Formatter
的设置更改为Safe HTML
即可。
更改配置后,我们就可以在 build 描述信息中采用HTML
的img
标签插入图片了。
另外还需要补充一个点。如果是使用蒲公英(pyger)
平台,会发现每次上传安装包后返回的二维码图片是一个短链接,神奇的是这个短连接居然是固定的(对同一个账号而言)。这个短连接总是指向最近生成的二维码图片,但是对于二维码图片的唯一 URL 地址,平台并没有在响应中进行返回。在这种情况下,我们每次构建完成后保存二维码图片的 URL 链接就没有意义了。
应对的做法是,每次上传完安装包后,通过返回的二维码图片短链接将二维码图片下载并保存到本地,然后在 build 描述信息中引用该图片的 Jenkins 地址即可。
每次完成构建后,编译生成的文件较多,但是并不是所有的文件都是我们需要的。
通常情况下,我们可能只需要其中的部分文件,例如.ipa/.app/.plist/.apk
等,这时我们可以将这部分文件单独收集起来,并在构建页面中展示出来,以便在需要时进行下载。
要实现这样一个功能,需要在【Post-build Actions】
栏目中新增Archive the artifacts
,然后在Files to archive
中通过正则表达式指定成果物文件的路径。
设置完毕后,每次构建完成后,Jenkins 会在Console Output
中采用设定的正则表达式进行搜索匹配,如果能成功匹配到文件,则会将文件收集起来。
本文主要是对如何使用 Jenkins 搭建 iOS/Android 持续集成打包平台的基础概念和实施流程进行了介绍。对于其中涉及到的执行命令、构建脚本(build.py),以及 Jenkins 的详细配置,出于篇幅长度和阅读体验的考虑,并没有在文中进行详细展开。
为了实现真正的开箱即用
,我将 Jenkins 的配置文件和构建脚本抽离出来形成一套模板,只需要导入到 Jenkins 中,然后针对具体的项目修改少量配置信息,即可将这一套持续集成打包平台运行起来,实现和文章开头插图中完全相同的功能效果。
详细内容请阅读《关于持续集成打包平台的 Jenkins 配置和构建脚本实现细节》。
公众号:DebugTalk 原文链接:http://debugtalk.com/post/iOS-Android-Packing-with-Jenkins GitHub 地址:https://github.com/debugtalk/JenkinsTemplateForApp