Jenkins + Xcode9 持续集成环境搭建
最近搭建了一套 Jenkins + Xcode9 持续集成环境,记录一下。
安装 Jenkins
推荐通过 homebrew 安装,也可以通过下载 pkg 包安装(Jenkins OSX Installer),不过还是 brew 方便些。
1 | brew install jenkins |
如果没安装 Java,需要先装一下:
1 | brew cask install caskroom/versions/java8 |
初始化配置
这里提一下关键的点,就不一一截图细说了。
浏览器中打开 http://localhost:8080
第一次打开会提示去取 initialAdminPassword
(一行红字标明路径,很明显)里的密码。cat path/to/initialAdminPassword
拿到后贴到输入框中 next。
→ 安装推荐的插件 → 创建用户。完成后重新登录 Jenkins。
接下来在 Manage Jenkins -> Manage Plugins -> Available 安装一些需要的插件:
Xcode integration
Keychains and Provisioning Profiles Management可能一些教程推荐装这个插件,然后配置一下 login.keychain 填一些证书信息什么的就很方便导出
.ipa
了。but,很遗憾 Xcode9 无法读取 login.keychain 的信息,现在导出需要提供一份ExportOptions.plist
,后面再具体说怎么操作。GIT plugin
Git Parameter
局域网内访问 Jenkins
使用 brew 安装 Jenkins 默认将 httpListenAddress
设置为 127.0.0.1
,本机可以通过 localhost:8080
访问,但局域网内无法通过 本机 ip:8080
访问。
1 | ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist |
将两个 plist 文件中 httpListenAddress
改为 0.0.0.0 重启 Jenkins 即可。
重启 Jenkins 可以执行:
1 | brew services restart jenkins |
或者在浏览器里:
1 | http://localhost:8080/restart |
创建/配置项目
Net Item → 输入 item name → Freestyle project → OK
Source Code Management 选择 Git,并填入相应的信息。
切换到 Build 栏,按顺序添加 build step。
由于我们使用 Cocoapods,首先安装 pod 的第三方库。
点击 Add build step → Execute shell
切换的 Podfile 文件目录下,执行 pod install 命令
1 | !/bin/bash -l |
接下来添加另一个 build step,这次选择的是 Xcode。
使用 Cocoapods,Target 这栏不用填,点击右侧的 Settings,按截图设置下。
Code signing & OS X keychain options 可以不设置。
这里有个注意的地方,在 Xcode9 以前,可以勾选底下的 Pack application, build and sign .ipa?,并设置些相关的信息就能导出 .ipa。但是使用 Xcode9 会报这样的错误:
1 | "Error Domain=IDEProvisioningErrorDomain Code=9 \"\"MyApp.app\" requires a provisioning profile.\" UserInfo={NSLocalizedDescription=\"MyApp.app\" requires a provisioning profile., NSLocalizedRecoverySuggestion=Add a profile to the \"provisioningProfiles\" dictionary in your Export Options property list.}" |
大概意思就是需要指定 ExportOptions.plist,这个文件内容大概是这样的:
1 |
|
可以拷贝然后修改一下相应的 key value。不过一个简单获取这个文件的方式是:手动使用 Xcode9 打包,在导出 .ipa
的文件夹里应该有 4 个文件,其中一个就是 ExportOptions.plist
。可以把它拷贝到 workspace 目录下。
☕️ 补充:如果 Signing 勾选了 Automatically manage signing,就简单多了,它的 exportOptons.plist
是这样的:
1 |
|
在 Xcode step 下面再添加一个 Execute shell
1 | xcodebuild -exportArchive -archivePath ${WORKSPACE}/build/${JOB_NAME}.xcarchive -exportPath ${JENKINS_HOME}/jobs/${JOB_NAME}/builds/${BUILD_NUMBER}/archive -exportOptionsPlist ${WORKSPACE}/ExportOptions.plist |
-exportOptionsPlist ${WORKSPACE}/ExportOptions.plist
这个选项就是指定刚刚拷贝到 workspace 的 plist。
到这里配置就完成了。回到 item 页面,点击 Build Now。
左侧就能看到 building 的项目了,点击 Console Output 可以看到 log 记录,如无意外最后输出 Finished: SUCCESS
。
可以在两个目录找到 .xcarchive
和 .ipa
:
1 | /Users/yourname/.jenkins/workspace/jobName/build/jobName.xcarchive |
配置 Git Parameter
有时候想指定打某个分支的包,用 Git Parameter 就很方便了。
回到配置页最顶部,勾选 This project is parameterized,选择 Git Parameter

Name 后面需要用到,Type 这里就选 Branch 了。
再到 Source Code Management,修改一下设置。
红框部分修改为刚刚设置的 Name,以 $
开头。
这时 Build Now 就变成 Build with Parameters 了。右侧选择分支,然后开 build。
这个插件适合手动构建,如果设置了轮询自动构建,会因为找不到分支而构建失败,有什么好的方案告诉我啊。
点击右侧的 Advanced,有一栏「Default Value」可以指定默认的分支。这样轮询构建的时候就会自动选择这个分支。
获取 Bundle ID 和 Provisioning Profile 生成 ExportOptions.plist
☕️ 补充:Signing 勾选了 Automatically manage signing 可以不操作这步。
现在可以指定任意的分支打包了,但可能每个分支的 Bundle ID 或者 Provisioning Profile 跟之前准备的 exportOptions 不一致,这样就得准备多一份 ExportOptions.plist。就写个脚本来获取吧。
方案是这样的,先准备一份 exportOptions 模版,通过 xcodebuild -showBuildSettings
获取 Bundle ID 和 Provisioning Profile,替换模版生成一个新的 .plist
,export 命令指定这个新的 .plist
就好了。
在 xcodebuild -exportArchive
前 Add execute shell
这里就玩玩 Swift 脚本了,在文本开头加上 !/usr/bin/env swift
1 | #!/usr/bin/env swift |
后面的 -exportOptionsPlist
指定 directoryPath/ExportOptions.plist
。
单元测试
采用 xcodebuild test + xcpretty 方案。
xcpretty 可以将 xcodebuild test 输出内容格式化,并且可以导出 xml、json 或者 html。
格式化的内容大概是这样子的:
1 | ✓ testExample (0.001 seconds) |
首先安装 xcpretty :
1 | gem install xcpretty |
Add execute shell:
1 | xcodebuild test \ |
&& exit ${PIPESTATUS[0]}
使用 exit 参数帮助 Jenkins 确定是否失败。
可以把这段 shell 放在 pod install 之后,测试失败后 build 失败而不继续 archive。
上传 ipa 到测试平台
你可以找到平台提供的集成文档,例如 fir.im Jenkins 插件使用方法 或者 蒲公英 - 使用 Jenkins 实现持续集成 (iOS)
这里演示使用 ios-ipa-server 搭建「本地自签名 https 服务器,快速安装 ipa」。
安装 ios-ipa-server
1 | npm install -g ios-ipa-server |
开启 ios-ipa-server
1 | cd /path/of/ipa |
将导出的 ipa 文件复制到 ios-ipa-server 目录
add Execute Shell:
1 | !/bin/bash -l |
关于证书问题,iOS 10.3 (不确定版本了) 以上除了需要安装描述文件外,还需要到「关于本机」- 「证书信任设置」将 ios-ipa-server 开启。
Build Triggers
可以设置一些触发构建条件。
Trigger builds remotely
在远程终端就可以通过以下指令来触发构建:
1 | curl http://username:password@ipAddr:8080/job/jobName/build\?token\=tokenName |
或者 /buildWithParameters?
,指定 Git Parameter 的分支 &Branches=master
1 | curl http://username:password@ipAddr:8080/job/jobName/buildWithParameters\?token\=tokenName\&Branches\=master |
Poll SCM
Poll SCM 可以设置一个轮询周期,当 Git 上有代码更新才会触发构建:
可能出现的错误和解决
pod: command not found
Execute shell 第一行加上 !/bin/bash -l
1 | !/bin/bash -l |
export 语句将控制台语言环境设置为 UTF-8
xcode-select: error: tool ‘xcodebuild’ requires Xcode
xcode 路径错误,在终端输入以下:
1 | sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/ |
error: exportArchive: “xx.app” requires a provisioning profile Jenkins
正文已解决,参考链接。
最后,备份 Jenkins 配置
理论上,把 Jenkins Home
,也就是用户目录下 .jenkins
文件夹,打个 .zip
保存起来就可以了。thinBackup
就能做到这些,可以完全备份或者差异备份。
不过这里用的是 GitHub 上的一个脚本(Backup Jenkins home periodicallly with git.),将 *.xml
配置文件保存到 git。
1 | !/bin/bash |

新建一个 Item,设置一个周期性 Build Trigger。H 18 * * 5
表示每周五 18 点…
Add Execute shell,将上面脚本填进去就行了。