天天看点

iOS中 为 iOS 建立 Travis CI 韩俊强的博客

新浪微博!

也开始支持 iOS 和 Mac 平台。

我最喜欢 Travis 的一点就是它与 GitHub 的 Web UI 集成的非常好。例如 pull 请求。Travis 会为每次请求都执行编译操作。如果一切正常,pull 请求在 GitHub 上看起来就像这样:

万一编译不成功,GitHub 页面会修改相应的颜色,给予提醒:

现在只需要打开这个开关就可以为你的项目添加 Travis 服务。之后你会看到 Travis 会和 GitHub 项目设置相关联。下一步就是告诉 Travis, 当它收到项目改动通知之后该做什么。

Travis CI 需要项目的一些基本信息。在项目的根目录创建一个名叫 <code>.travis.yml</code> 的文件,文件中的内容如下:

预装的编译脚本会分析你的 Xcode 项目,并对每个 target 进行编译。如果所有文件都没有编译错误,并且测试也没有被打断,那么项目就编译成功了。现在可以将相关改动 Push 到 GitHub 中看看能否成功编译。

打开终端并输入:

使用 <code>iphonesimulator</code> SDK 是为了避免签名错误。直到我们稍后引入证书之前,这一步是必须的。通过设置 <code>ONLY_ACTIVE_ARCH=NO</code> 我们可以确保利用模拟器架构编译工程。你也可以设置额外的属性,例如 <code>configuration</code>,输入<code>man xcodebuild</code> 查看相关文档。

对于使用 <code>CocoaPods</code> 的项目,需要用下面的命令来指定 <code>workspace</code> 和 <code>scheme</code>:

schemes 是由 Xcode 自动生成的,但这在服务器上不会发生。确保所有的 scheme 都被设为 <code>shared</code> 并加入到仓库中。否则它只会在本地工作而不会被 Travis CI 识别。

我们示例项目下的 <code>.travis.yml</code> 文件现在看起来应该像这样:

对于测试来说,通常使用如下这个命令 (注意 <code>test</code> 属性):

xctool 用法非常简单,它使用的参数跟 <code>xcodebuild</code> 相同:

一旦相关命令在本地能正常工作,那么就是时候把它们添加到 <code>.travis.yml</code> 中了:

到此为止,介绍的内容对于使用 Travis 的 library 工程来说,已经足够了。我们可以确保项目正常编译并测试通过。但对于 iOS 应用来说,我们希望能在真实的物理设备上进行测试。也就是说我们需要将应用部署到我们的所有测试设备上。当然,我们希望 Travis 能自动完成这项任务。首先,我们需要给程序签名。

为了在 Travis 中能给程序签名,我们需要准备好所有必须的证书和配置文件。就像每个 iOS 开发人员知道的那样,这可能是最困难的一步。后面,我将写一些脚本在服务器上给应用程序签名。

1. 苹果全球开发者关系认证

2. iPhone 发布证书 + 私钥

<code>Add</code> &gt; <code>App Store and Ad Hoc</code>)。然后下载并安装证书。之后,可以在钥匙串中找到它。打开 Mac 中的<code>钥匙串</code> 应用程序:

右键单击证书,选择 <code>Export...</code> 将证书导出至 <code>scripts/certs/dist.cer</code>。然后导出私钥并保存至<code>scripts/certs/dist.p12</code>。记得输入私钥的密码。

上面的命令会安装一个叫做 <code>KEY_PASSWORD</code> 的加密环境变量到 <code>.travis.yml</code> 配置文件中。这样就可以在被 Travis CI 执行的脚本中使用这个变量。

3. iOS 配置文件 (发布)

<code>Ad Hoc</code> or <code>In House</code>)。然后将其下载保存至 <code>scripts/profile/</code> 目录。

由于 Travis 需要访问这个配置文件,所以我们需要将这个文件的名字存储为一个全局环境变量。并将其添加至 <code>.travis.yml</code> 文件的全局环境变量 section 中。例如,如果配置文件的名字是<code>TravisExample_Ad_Hoc.mobileprovision</code>,那么按照如下进行添加:

上面还声明了两个环境变量。第三行中的 <code>APP_NAME</code> 通常为项目默认 target 的名字。第四行的 <code>DEVELOPER_NAME</code> 是 Xcode 中,默认 target 里面<code>Build Settings</code> 的

<code>Code Signing Identity</code> &gt; <code>Release</code> 对应的名字。然后搜索程序的<code>Ad Hoc</code> 或

<code>In House</code> 配置文件,将其中黑体文字取出。根据设置的不同,括弧中可能不会有任何信息。

如果你的 GitHub 仓库是公开的,你可能希望对证书和配置文件 (里面包含了敏感数据) 进行加密。如果你用的是私有仓库,可以跳至下一节。

首先,我们需要一个密码来对所有的文件进行加密。在我们的示例中,密码为 “foo”,记住在你的工程中设置的密码应该更加复杂。在命令行中,我们使用 <code>openssl</code> 加密所有的敏感文件:

现在,我们的文件已经被加密了,接下来需要告诉 Travis 对文件进行解密。解密过程,需要用到密码。具体使用方法跟之前创建的 <code>KEY_PASSWORD</code> 变量一样:

最后,我们需要告诉 Travis 哪些文件需要进行解密。将下面的命令添加到 <code>.travis.yml</code> 文件中的 <code>before-script</code> 部分:

就这样,在 GitHub 上面的文件就安全了,并且 Travis 依旧能读取并使用这些加密后的文件。但是有一个安全问题你需要知道:在 Travis 的编译日志中可能会显示出解密环境变量。不过对 pull 请求来说不会出现。

现在我们需要确保证书都导入至 Travis CI 的钥匙串中。为此,我们需要在 <code>scripts</code> 文件夹中添加一个名为 <code>add-key.sh</code> 的文件:

通过上面的命令创建了一个名为 <code>ios-build</code> 的临时钥匙串,里面包含了所有证书。注意,这里我们使用了 <code>$KEY_PASSWORD</code> 来导入私钥。最后一步是将配置文件拷贝至<code>Library</code> 文件夹。

创建好文件之后,确保给其授予了可执行的权限:在命令行输入:<code>chmod a+x scripts/add-key.sh</code> 即可。为了正常使用脚本,必须要这样处理一下。

至此,已经导入了所有的证书和配置文件,我们可以开始给应用程序签名了。注意,在给程序签名之前必须对程序进行编译。由于我们需要知道编译结果存储在磁盘的具体位置,我建议在编译命令中使用<code>OBJROOT</code> 和

<code>SYMROOT</code> 来指定输出目录。另外,为了创建 release 版本,还需要把 SDK 设置为<code>iphoneos</code>,以及将 configuration 修改为

<code>Release</code>:

如果运行了上面的命令,那么编译完成之后,可以在 <code>build/Release-iphoneos</code> 目录找到应用程序的二进制文件。接下来,就可以对其签名,并创建<code>IPA</code> 文件了。为此,我们创建一个新的脚本:

第十四行,才是真正的签名操作。这个命令会在 <code>build/Release-iphoneos</code> 目录生成 2 个文件:<code>TravisExample.ipa</code> 和<code>TravisExample.app.dsym</code>。第一个文件包含了分发至手机上的应用程序。<code>dsym</code> 文件包含了二进制文件的调试信息。这个文件对于记录设备上的 crash 信息非常重要。之后当我们部署应用程序的时候,会用到这两个文件。

最后一个脚本是移除之前创建的临时钥匙串,并删除配置文件。虽然这不是必须的,不过这有助于进行本地测试。

最后一步,我们必须告诉 Travis 什么时候执行这三个脚本。在应用程序编译、签名和清除等之前,需要先添加私钥。在 <code>.travis.yml</code> 文件中添加如下内容:

完成上面的所有操作之后,我们就可以将所有内容 push 到 GitHub 上,等待 Travis 对应用程序进行签名。我们可以在工程页面下的 Travis 控制台验证是否一切正常。如果一切正常的话,下面来看看如何将签好名的应用程序部署给测试人员。

首先我们对 <code>sign-and-build.sh</code> 脚本做一个扩充 -- 在里面添加一些 release 记录:

注意这里使用了一个 Travis 的全局变量 <code>TRAVIS_BUILD_NUMBER</code>。

现在我们可以调用相应的 API 了。并将下面的内容添加到 <code>sign-and-build.sh</code>:

千万不要使用 verbose 标记 (<code>-v</code>) -- 这会暴露加密 tokens。

对 App ID 和 token 进行加密:

然后在 <code>sign-and-build.sh</code> 文件中调用相关的 API:

使用 Travis 一个月以来,并不总是那么顺畅。知道如何不通过直接访问编译环境就能找出问题是非常重要的。

在写本文的时候,还没有可以下载的虚拟机映像 (VM images) 。如果 Travis 不能正常编译,首先试着在本地重现问题。在本地执行跟 Travis 相同的编译命令:

为了调试 shell 脚本,首先需要定义环境变量。我的做法是创建一个新的 shell 脚本来设置所有的环境变量。记得将这个脚本添加到 <code>.gitignore</code> 文件中 -- 因为我们并不希望将该文件公开暴露出去。针对示例工程来说,<code>config.sh</code> 脚本文件看起来是这样的:

为了暴露出所有的环境变量,执行如下命令(确保 <code>config.sh</code> 是可执行的):

然后试着运行 <code>echo $APP_NAME</code>,以此检查脚本是否正确。如果正确的话,那么现在我们不用做任何修改,就能在本地运行所有的 shell 脚本了。

在本地安装好与服务器完全相同的软件之后,再重新编译项目。

如果获取到的编译信息仍然不一样,试着将项目 check out 到一个新的目录。并确保所有的缓存都已清空。每次编译程序时,Travis 都会创建一个全新的虚拟机,所以不存在缓存的问题,但在你的本地机器上可能会出现。

一旦在本地重现出和服务器上相同的错误,就可以开始调查具体问题了。当然导致问题的原因取决于具体问题。一般来说,通过 Google 都能找到引起问题的根源。

如果一个问题影响到了 Travis 上其它的项目,那么可能是 Travis 环境配置的原因。我曾经遇到过几次这样的问题 (特别是刚开始时)。如果发生这样的情况试着联系 Travis,取得支持,以我的经验来说,他们的响应非常迅速。

在一定程度上,你会依赖于 Travis 所提供的配置。比如你只能使用 Travis 内置的 Xcode 版本进行编译。如果你本地使用的 Xcode 版本较新,你的项目在服务器上可能无法编译通过。如果 Travis 能够为不同的 Xcode 版本都分别设置一个对应虚拟机会就好了。

iOS中 为 iOS 建立 Travis CI 韩俊强的博客

对于商业项目,Travis 专业版也能为私有仓库提供快捷、简便的持续集成支持。

如果你还没有用过 Travis,赶紧去试试吧,它棒极了!

<a target="_blank" href="https://github.com/objcio/issue-6-travis-ci">示例工程</a>

<a target="_blank" href="http://www.travis-ci.com/">Travis CI</a>

<a target="_blank" href="https://magnum.travis-ci.com/">Travis CI 专业版</a>

<a target="_blank" href="https://github.com/facebook/xctool">Xctool</a>

<a target="_blank" href="http://hockeyapp.net/">HockeyApp</a>

<a target="_blank" href="https://testflightapp.com/">TestFlight</a>

iOS中 为 iOS 建立 Travis CI 韩俊强的博客