蒲公英文档中心

Travis CI 持续集成(iOS)

在 iOS 项目中使用 Travis CI 完成证书加密、签名打包,并将 App 自动发布到蒲公英。

本文介绍如何在 iOS 项目中使用 Travis CI 进行持续集成,并将签名后的 App 自动发布到蒲公英。

Travis CI 产品分类

Travis CI 是一款用于构建和测试 GitHub 托管代码的持续集成工具。其产品分为:

  1. Travis CI.org:面向 GitHub 开源项目的免费产品。
  2. Travis CI.com:面向私有项目的付费产品。
本文演示的 Demo 基于 Travis-ci.org。

前置要求

需要在本地安装 Travis CI 命令行工具。

  1. 确保本地 Ruby 版本在 1.9 以上:

    ruby -v
  2. 安装 Travis CI(如使用 macOS,建议先将 Ruby 升级到最新版本):

    gem install travis --no-rdoc --no-ri
  3. 验证是否安装成功:

    travis -v
  4. 使用 GitHub 账号登录 Travis:

    travis login

    登录完成后,可验证账号是否登录成功:

    travis accounts

启用 Travis CI

通过 GitHub 账号登录 Travis 平台。Travis 会自动同步 GitHub 账号下所有的开源项目,在列表中选择需要启用的项目即可。

添加项目

创建 Travis CI 构建

在工程根目录下新建 .travis.yml,用于描述 Travis CI 需要执行的构建。

Travis CI 提供基本的构建能力和语言支持,可通过 .travis.yml 自定义构建操作,同时需遵循 Travis CI 的构建生命周期,详见 官方文档

完成 .travis.yml 后,可通过以下命令验证语法:

travis lint [path to your .travis.yml]

语法正确时会出现如下提示:

验证构建文件

iOS 工程基本模板:

language: objective-c
osx_image: xcode9

若仅需使用 Travis CI 的构建功能,这两行已经足够。将 .travis.yml 推送到 GitHub 后,Travis CI 会自动触发 Build 流程,Build 完成后 GitHub 绑定的邮箱会收到通知。若需要对 App 进行打包、签名并发布到蒲公英,继续阅读后续步骤。

配置项目

使用 Travis CI 需要修改两处项目配置,否则会导致 Build 失败。

  1. 将项目的 scheme 管理方式改为 shared:

  2. 关闭 bitcode 选项:

打包签名并上传

为了对 App 进行签名,需要配置好证书和描述文件。

苹果全球开发者关系认证

苹果官网 下载证书,或从钥匙串中导出,并保存到项目目录 travis/certificates/AppleWWDRCA.cer

导出发布证书和私钥

钥匙串 程序中选中证书,分别导出证书和私钥。

切勿将未加密的证书和描述文件上传到 GitHub 公开仓库,下文会介绍如何加密。

将证书与 p12 文件保存到 travis/certificates/sdk_demo.certravis/certificates/sdk_demo.p12。导出 p12 文件时需要输入密码。

Travis CI 在使用 p12 文件时需要知道密码,而密码不能明文保存,因此需要使用 Travis CI 的 安全环境变量 功能。

打开 终端,定位到 .travis.yml 所在目录,使用以下命令保存 p12 文件密码:

travis encrypt "KEY_PASSWORD=123456" --add

其中 123456 为导出 p12 文件时设置的密码。执行后,.travis.yml 中会新增一行数据:

 - secure: xJz9E1EJdBDAIZcNaz86a7nrJpbdPHS3xiXU5L4Gj4rFR0TcxHsHuu2dcZR/yRJRHg6oum2zuMr0XBsSffMBYFHX5Kw2jb31Ci6uFbOTI/FGBrwdvfhQBL+h/7xe/j3l1bmbmfElYP02fiJvN5VSVyA0    3Iobp7u3vY0TW7yce+po23DmJCTYgnUdfuf4EBO3gpgbOTPdmIxqyhqqw5Ndwmvxpq9BqneqEc3pmNCC1FC6H4RmgjkWnMln5ffWIxNN+nwgPzSDqHDMUnQaYtVUU/CHLQCmNCgQmkrG/OWYvlo4RqpEX6VZv5BUa6gD    7d4lgcfXHONkmLKNbiWBGDRbbBQNNSbubTtGlyGtzCHwEe4KvHoM4n0yDZqtd9edgrxlOSuBgNlQK+/3C6BhZZi2rWNlnqBU7F/ZSmjBONWgRuFZJ2zJByHWLoTTOHvYbFdk4CTmT5qMQPQ7favMu1L9TUBpbX4qBX4D    iXpEKNODtwOvdYjlfiZ+US6i637JeZF8OK9bBtUf4oKjvl1Oz5ly56snTBknF3V+if6VoHlG1Cfroqhy2F7ahS2K3Aq0u4O2gMIVqTRd1juBLo6QkzV/F+go4KvYDwOFpAX05AYrJNOQgAHae5a8Px2YIct1QcRTL++r    Enqx1QzQWXIEXpezm8m1pR8TcB8d2WbLGtwTd/8=

这行数据是通过上述命令注入 .travis.yml 的环境变量 KEY_PASSWORD,后续打包过程中,Travis 可通过引用 KEY_PASSWORD 来使用 p12 文件对应的密码。

描述文件

签名打包还需要用到 .mobileprovision 描述文件,可在苹果开发者网站创建并下载(Provisioning Profiles → Distribution → Add → Ad Hoc or In House),保存到 travis/certificates/sdk_demo.mobileprovision

同时,需要在 .travis.yml 中记录描述文件名、App 名称和开发者名称:

env:
  global:
  - APPNAME="PgySDKDemo"
  - 'DEVELOPER_NAME="iPhone Distribution: your developer name (your developer code)"'
  - PROFILE_NAME="sdk_demo"

加密证书和配置文件

如果使用的是 GitHub 公开仓库,需要对证书、描述文件和私钥等敏感文件进行加密,并告诉 Travis CI 如何解密。

加密

Travis 解密时仅支持单文件,因此加密前需要先将证书和描述文件打包为压缩包:

tar cvf certificates.tar sdk_demo.cer sdk_demo.p12 sdk_demo.mobileprovision

执行后会生成 certificates.tar,将其放到项目根目录下,然后在根目录执行:

travis encrypt-file certificates.tar -a

-a 参数会自动把解密指令添加到 .travis.yml 中。执行完成后会得到一个 certificates.tar.enc 加密文件。此时可以删除未加密的 certificates.tar 以及 travis/certificates 目录下的 sdk_demo.cersdk_demo.p12sdk_demo.mobileprovision 文件。

解密

加密命令执行完成后,.travis.yml 中会新增一行:

- openssl aes-256-cbc -K $encrypted_2838d02a56a6_key -iv $encrypted_2838d02a56a6_iv
  -in certificates.tar.enc -out certificates.tar -d

这一行用于解密证书文件。解密后得到的仍是压缩包,还需要解压。在 before_install 中添加:

- tar xvf certificates.tar -C ./travis/certificates

导入钥匙串

为了进行签名打包,需要将证书导入到 Travis CI 运行环境的钥匙串中。在 scripts 文件夹下新建 add-key.sh

#!/bin/sh
# Create a custom keychain
security create-keychain -p travis ios-build.keychain
security default-keychain -d user -s ios-build.keychain
security unlock-keychain -p travis ios-build.keychain
security set-keychain-settings -t 3600 -l ~/Library/Keychains/ios-build.keychain

security import ./scripts/travis/AppleWWDRCA.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
security import ./scripts/travis/sdk_demo.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
security import ./scripts/certs/sdk_demo.p12 -k ~/Library/Keychains/ios-build.keychain -P $KEY_PASSWORD -A

echo "list keychains: "
security list-keychains
echo " ****** "

echo "find indentities keychains: "
security find-identity -p codesigning  ~/Library/Keychains/ios-build.keychain
echo " ****** "

mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp "./scripts/profile/sdk_demo.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/

add-key.sh 创建了一个名为 ios-build 的临时钥匙串,其中包含所有证书信息,$KEY_PASSWORD 引用 p12 文件的密码。完成签名打包后,这个临时钥匙串还需要被删除。

为保证脚本可以正常运行,还需要赋予可执行权限:

before_install:
 - chmod +x scripts/travis/add-key.sh

打包

Xcode 8.2 之前常用的打包工具 xctool 已无法使用,目前只能通过 xcodebuild 打包,过程分为 Archive 和签名两步:

#!/bin/sh
xcrun xcodebuild -project PgySDKDemo.xcodeproj -scheme PgySDKDemo \
  -archivePath PgySDKDemo.xcarchive archive

xcrun xcodebuild -exportArchive -archivePath PgySDKDemo.xcarchive \
  -exportPath ./build -exportOptionsPlist ExportOptions.plist

-exportOptionsPlist 文件最简单的获取方式是通过 Xcode 导出一个 ipa,在导出 ipa 所在的文件夹中就有 ExportOptions.plist,将其放到项目根目录即可。

运行完打包脚本后,即可在项目的 ./build 目录下找到签名好的 ipa 文件。

上传蒲公英

蒲公英提供了现成的 shell 脚本用于上传 ipa 文件。在 before_install 中下载该脚本并赋予执行权限,然后在打包完成后调用:

- wget -c https://raw.githubusercontent.com/Pgyer/TravisFile/master/pgyer_upload.sh
  -O pgyer_upload.sh
#!/bin/sh
./pgyer_upload.sh "./build/$APPNAME.ipa" $PGYER_APIKEY

上传时需要用到蒲公英的 API_KEY,可在 API 信息页面 查看。API_KEY 不能明文保存,需要使用与保存密码相同的方式写入 .travis.yml。在项目根目录下执行:

travis encrypt "PGYER_APIKEY=YOUR_API_KEY" --add

清理钥匙串

所有环节结束后,还需要删除导入的钥匙串信息。在 /scripts/travis 文件夹下新建 remove-key.sh

#!/bin/sh
security delete-keychain ios-build.keychain
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/$PROFILE_NAME.mobileprovision
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/team.mobileprovision

完成以上配置后,每次向 GitHub 推送新代码都会触发 Travis CI 的持续集成,并将打包签名后的 App 发布到蒲公英。

完整示例

一份完整的 .travis.yml 示例如下:

language: objective-c
osx_image: xcode9.2
env:
  global:
  - APPNAME=PgySDKDemo
  - PROFILE_NAME=sdk_demo
  - 'DEVELOPER_NAME="iPhone Distribution: shengtao lei (DG37YK9PRK)"'
  - secure: iL2KhNdYKzWLTtvaXmmQ3/ci66b0Z5c8VCTmpaoMIotdtwyTOgMnpzH+Vyrof3QBH/nV2oF6puT/b5Y6S6lrCY5b4nZSfhy8xL6FEPDboVzpq2qIAS4gn6qCGUkIkAxpcnKG9sbcxDsI5aLVOqxoevuHKB2Rkw925TWrg6+bfagtWQzVvkByIHT2jm2+7bkmNUx7UiIggNYN+H2ACpfdf/d9g7lC7w2hLb6hO0Mt5pK5eGbzlh8kn/1CnX7jSWw3SyEABEJ3CQOZX5x/yNP4oIJLgdEGW3sy1ErAi2uO+i89VDu1SaRcDLbNuAmlxb95dLVBVU7uWILdWTh6+o6FVL2Eoj1YPrNBJdcRykTvaCSR1eXPs6uCoq9tcWz0zinamSBzemOjdK+G+lOC6IpT9lTimt5Ln0lGQoVYERXnvJMEhR/2rFLvsTIqd+Wt8Agfqr9EeU0ah8agWiTIE6EuA0xdTyC0EyKVYKzXBy4wI6R2fknfn6uHyeFeIN9zpWY8tcwz8pRLDD+xiyoHgSybS1DK+Xr21llOopXkC/aacnoeslciNN28In9QRDwsiDUSlAb6dQ3pRCS/cDoRJBm759xcZ7kdMnwVAcSX7aZ9p8HT0uGvyuJdYNDr4vp0r98RtPI/7JonPXqXuWzsAzZdq5AAzchwNFAbTCrASw6qqHY=
  - secure: KuD0JYZqJEtezCBkJ6Zkfw7I+0VGiv6E7GOa34KUzYsJWpLnzPYB08+NsYZYMh6Yjc8pkC8+MixgXLOHupWm5v3iRthWqD/tdmuF5bdQqIdrWkmQSl0cK9M+rMgc+vaPajguC0/yf+BoPWL6CJXouLLzj3D6IXUwZEVYhmPB4nBv/YHVk/FNwNS4aqP9SSp98VQoYrAGAvUSY3jm5L1qSNwCjPMBcX4QnlbX5mjl0mEf0pEbJ3ZZAz9AzpALfCYR4pPsS7/eSHxVAPNboD5JVQAK6NhaFCTUw0/UlGHW9m9eY3AezMpZo1M8kHXZXsC0BWoP8R4H6WYTybILxz6Aramb8N12R+ntLfAawnFnG5kV4mtUHFY7BqNu+tyBkba4axkclaY90fETekHUcCIqTlVmMjlKq6dzWkq4hGmDtEajajZHqMPBUMy5o8n8lXKww/ZkpvgEhrJjbxbmFcoP4oS2u9Vxi/Q18t+mRoQhGfan2cVQXdpQhk0S8scisVVnUSFHSglt79KjgKwNuVKtIpW89bG8HHQaqIgPFycvIbDRxSM6Xd+I7AIzz25XFdE/ao03ULrnDaIZLbgUa+IGjDknkRJmG9hFQKO2grkZ/JqNXQDSMEIXZrH46CocD1+j39k6wvYler5fdcIStf+wAW4hXmiO0en2hl2+qEFtJds=
  - secure: YecGZLdjxnbJuIyJOywDan2lJ4sToYx2iq8ficMEzODXUxEGHRL6s6XFOPltsm6CnyUZKfT9bzAEWuGWnnES8fcPvMUAKvyNhcRW/D9JtMm/fboIdSjb2Ht8f/dY1IfgB241pMU7SkPifVIL08yxeMYZfwgFxbDQn60aXCiUKWuEcqMgxX2yLX9uHR+T/xuY6E+NX6UEB/6imx3rQ047nVfefMgh9YMGGl0ZH/N9beZgMrwlZn1lAfKMrkbJ3vw1go2RzxnDwa/Noy+6eKfbC8R4RgCvwQ6oXBEXG1IUjHbiNhR5pXajz/5jbum9T64fHUrx8e2aPKrYRWWjooQ5Q7+AER7PnAhTgFymHhDwUm7wEXoMuh5Ltp7jCNZiaKDxUoONw4Hz+oEXKbm4nYJCMFczjW4bQKH0gqNytPhl/y/08u0fuQxTP3wu6Zi0q0qo6e3eqCTGv3Q9YzEm7JWAb6RSO09aRRVvz7HwdZFwbHy07T9//YMUsX/chhw7fziso1wkutWOsVCfBVjMGO6Nve/B8xdCVV4sqiex/Wk3c/cNHB7dTVe9SC6nelrkuyvEqKtxk87/IDBo0sAHxounw9phwtXg+RM8fN+rvBqd9rY4H8hrRaZKdFqikMt3yexoFKcrAqbQuHEDVlz2dxDxZhgjOPyVaejsOHI2jt1bB58=
before_install:
- openssl aes-256-cbc -K $encrypted_2838d02a56a6_key -iv $encrypted_2838d02a56a6_iv
  -in certificates.tar.enc -out certificates.tar -d
- cd $TRAVIS_BUILD_DIR
- wget -c https://raw.githubusercontent.com/Pgyer/TravisFile/master/pgyer_upload.sh
  -O pgyer_upload.sh
- tar xvf certificates.tar -C ./travis/certificates
- chmod +x pgyer_upload.sh
- chmod +x travis/scripts/add-key.sh
- chmod +x travis/scripts/build.sh
- chmod +x travis/scripts/upload.sh
- chmod +x travis/scripts/remove-key.sh
before_script:
- "./travis/scripts/add-key.sh"
script:
- "./travis/scripts/build.sh"
after_success:
- "./travis/scripts/upload.sh"
after_script:
- "./travis/scripts/remove-key.sh"

对应的 Demo 项目可在 GitHub 上找到。

本页目录