使用Travis CI连续部署iOS

在Telestax,我们相信持续集成与部署是我们生产的所有软件的组成部分。 随着我们的移动开源WebRTC SDK变得更加成熟,我们开始意识到,越来越多的时间花在了测试和部署的手动步骤上,而不是实际的开发上,现在是时候该死了,并引入CI / CD设施。 在本期中,我们将介绍如何使用Travis CI解决Restcomm Olympus应用程序的持续部署问题,该应用程序是我们在GitHub上托管的开源WebRTC Restcomm iOS SDK的一部分。 更具体地说,我们将:

  • 构建并导出已签名的restcomm-olympus.ipa,并准备进行企业部署。
  • 将restcomm-olympus.ipa上传到TestFairy Beta测试平台,以便我们的Beta测试人员和社区可以立即使用。

这说起来容易做起来难,所以让我们深入了解一下细节。

iOS签名简介

为了能够部署到任何物理设备,而不必每次我们要在新设备上进行测试时都必须更新配置文件,我们将使用企业分发。 这样就可以在Apple App Store外部自由分发App。 要构建以这种方式可分发的.ipa,构建过程分为两个步骤:

  1. 生成存档的存档构建步骤。 为此,您需要在CI服务器中安装“开发身份和开发配置文件”
  2. 导出步骤,用于对存档进行签名并生成可用于实际分发的.ipa。 为此,您需要在CI服务器中安装分发标识和分发配置文件

但首先..

关于自动签名

从Xcode 8开始,随着自动签名的引入,很多地方在Signing区域中进行了更改,从而解决了过去的许多问题-请记住,如果以前使用的生成脚本可能会向后兼容,可以与Xcode 7一起使用。有关更多信息,我敦促查看有关该主题的Apple最新的WWDC会议。 另外,对于更技术性的方法,这是一个很好的阅读材料,它首先概述了Apple的代码签名机制,然后描述了Xcode 8中的更改。

我们将使用自动签名,我敦促您也这样做,因此,如果您还没有这样做,现在是升级到Xcode 8的好时机。对于新的Xcode 8项目,默认为自动签名,并且要求您执行以下操作。在创建项目时选择您的团队ID ,并自动为您设置签名身份和配置文件。 您可以在会员中心的“ Apple帐户会员”页面中找到您的团队ID:

如果您已经使用早期版本的Xcode创建了应用程序,则应导航至主要目标的“常规设置”,然后勾选“ 自动管理签名”:

再次,您将被要求输入团队ID,所有内容应自动为您处理。

在这一点上,需要指出的是, Apple并不真正支持在您无法访问UI和Xcode IDE的无头机器上进行构建 ,例如Travis CI的情况(不幸的是,这是我们从Apple那里得到的回应当我们针对某些问题提出技术支持事件时,我们就遇到了麻烦。 主要问题如下:

  • 无法提供自动解决方案来解决有关身份和配置文件的问题(即只能从Xcode IDE中获得解决方案)。
  • 您无法解决UI中显示的任何安全提示,例如在codesign步骤中授予访问权限。

但是不要害怕! 如果您确保在构建之前已安装了正确的身份和配置文件,并且已在脚本中采取了一些其他步骤来授权codesign在没有这些UI提示的情况下工作,那么一切都会正常进行。 我们将稍等一下。

请记住,这里的总体思路是,您首先必须在本地OS X机器上为您的项目提供一个功能强大的构建系统,该系统可以通过XCode IDE进行归档和导出,以进行Enterprise Distribution。 这样您便知道在本地正确设置了所有内容。 然后,我们想将其复制到Travis CI,即使没有Xcode IDE来自动处理任何事情,也不需要这样做,因为我们自己动手做。

Travis CI中使用哪些身份和个人资料?

我们说您需要先在Travis CI中安装正确的身份和配置文件,然后才能进行任何构建,但是我们没有讨论如何进行。 让我们弄清楚。

随着时间的流逝,您可能会发现自己在系统中安装了多个身份和多个配置文件。 有时很难说出要在Travis CI机器中使用哪些机器。 对我们而言,最好的方法是检查Xcode IDE将哪些用于本地构建,然后使用它们。

A.开发身份和配置文件

为了弄清开发身份和配置文件,事情很简单。 您可以转到Xcode 8目标设置,然后单击“签名”部分中的“信息”按钮:

对于屏幕快照顶部的Development Provisioning Profile ,您将看到其名称,因此您需要找到具有该名称的Provisioning Profile。 导航至〜/ Library / MobileDevice / Provisioning Profiles ,该位置将保留Xcode管理的所有Provisioning Profiles。 您需要使用文本编辑器查看其内容,并找到一个名称 (xml标记 )与上面的屏幕快照相同的名称。 找到该文件后,将其复制到〜/ Desktop / security /等临时位置( 不在您的仓库内 ),然后将其重命名为development-provisioning-profile.mobileprovision 。 我们将需要一点。

提示1:您应用的开发配置文件通常是非通配符文件,例如:com.telestax.olympus

提示2:我最近意识到Xcode还允许您从上面的屏幕快照中的带有齿轮的小文档图标中拖动,并在需要时将其拖放到文件系统中,该文件系统会复制使用的Development Provisioning Profile,以便您可以查看哪个这很容易。

现在,对于开发身份,您可以从上面的屏幕快照的“证书”部分中找出它。 在那里,您应该看到一个名称以及一个代码。 您应该使用Keychain Access OS X应用程序,并将该身份的私钥部分导出为development-key.p12 (通过提供强密码),并将证书部分导出development-cert.cer 。 将它们也移到您的临时位置。

B.发行身份和配置文件

我们通常知道“分发身份和配置文件”,因为我们通常在Apple Member Center中手动创建它们。 因此,您需要再次使用Keychain Access OS X应用程序,并将该身份的私钥部分导出为distribution-key.p12 (通过提供强密码),并将证书部分导出distribution-cert.cer 。 将它们也移到您的临时位置。 对于Distribution Provisioning Profile,您需要将它从〜/ Library / MobileDevice / Provisioning Profiles复制到您的临时位置,并将其重命名为distribution-provisioning-profile.mobileprovision

如果您在确定应该使用哪个分发身份和配置配置文件时遇到问题,请参阅附录A。

现在,您知道需要在Travis CI上安装哪些身份和供应配置文件才能进行构建。 可是等等…

安全第一

现在我们已经将所有证书,密钥和配置文件存储在temp目录中,我们需要确保将它们的内容复制给我们的仓库并提交之前(确保我们的GitHub存储库处于打开状态),以确保它们的内容对公众不可读。对于所有人,与Travis CI构建输出相同)。 为此,我们需要对所有文件进行对称加密,并将加密文件(以.enc结尾)放入我们的存储库中:

  $ openssl aes-256-cbc -in〜/ Desktop / security / development-key.p12 -out $ REPO / scripts / certs / development-key.p12.enc -a 
$ openssl aes-256-cbc-输入〜/ Desktop / security / development-cert.cer -out $ REPO / scripts / certs / development-cert.cer.enc -a
$ openssl aes-256-cbc -in〜/ Desktop / security / profile-development-olympus.mobileprovision -out $ REPO / scripts / provisioning-profile / profile-development-olympus.mobileprovision.enc -a
$ openssl aes-256-cbc -in〜/ Desktop / security / distribution-key.p12 -out $ REPO / scripts / certs / distribution-key.p12.enc -a
$ openssl aes-256-cbc -in〜/ Desktop / security / distribution-cert.cer -out $ REPO / scripts / certs / distribution-cert.cer.enc -a
$ openssl aes-256-cbc -in〜/ Desktop / security / profile-distribution-olympus.mobileprovision -out $ REPO / scripts / provisioning-profile / profile-distribution-olympus.mobileprovision.enc -a

完成后,我们需要将它们提交到GitHub并推送,以便Travis CI可以使用它们。

请记住,证书,密钥和配置文件的未加密版本绝对不能提交到存储库

苹果的CA证书

除了我们的身份和配置文件,我们还需要在Travis CI中安装Apple的证书,以便可以验证对我们证书的信任。 为此,您需要从此处下载它,并将其与AppleWWDRCA.cer的证书一起复制到脚本/证书中的回购文件中。

启用特拉维斯

到目前为止,我们一直在本地进行工作以找出所有配置,并通过将所有必需的资源提交到我们的存储库中来使它们可供Travis使用。 但是在实际为我们构建Travis之前,我们需要为回购启用它。 为此,您需要使用Travis CI进行注册,然后启用它:

您可能已经注意到在上面的设置中启用它还不够,所以您还需要在仓库的根目录中包含.travis.yml。 因此,我们使用以下内容创建它:

  osx_image:xcode8.1 
语言:Objective-C
脚本:
-./scripts/ci-script.bash

使用xcode8.1作为osx_image非常重要,因为这也决定了要使用的OS X映像(在本例中为OS X Sierra),并且所有脚本均已在此处进行了验证。 它们很有可能会在早期版本中中断。

可以想象,我们所有的脚本逻辑都将放在scripts / ci-script.bash中 。 我们将稍作讨论,但现在让我们创建一个空文件scripts / ci-script.bash,使其可执行,提交并将其推送到我们的仓库中。

特拉维斯隐藏环境

我们已经达到了加密所有必需的证书,密钥和配置文件并将其提交到存储库,从而可用于Travis CI的地步,并且还启用了Travis构建。 在使用Travis的那些文件之前,我们还需要提供密码,以便Travis可以解密它们,但又不将其公开。 这样做的方法是使用Travis隐藏的环境变量。 因此,我们转到Travis帐户设置页面,并添加用于加密所有文件的密码作为隐藏环境变量:

在这里,我们将此密码作为环境变量SECURITY_PASSWORD公开。 您还可以看到稍后将使用的其他变量。 还要注意,可以将不敏感的变量设置为明文,以便在构建中更好地进行日志记录/故障排除

编写脚本的时间

现在我们已经奠定了所有基础,我们开始将需要的代码片段添加到要在Travis CI上运行的ci-script.bash脚本中,开始进行整合。

1.解密

要解密所有身份(即证书和私钥)和供应配置文件,我们需要发出以下命令(注意,我们使用Travis CI变量作为密码):

  #发展 
openssl aes-256-cbc -k“ $ SECURITY_PASSWORD”-在脚本/证书/development-cert.cer.enc中-d -a -out脚本/证书/development-cert.cer
  openssl aes-256-cbc -k“ $ SECURITY_PASSWORD”-在脚本/证书/development-key.p12.enc -d -a -out脚本/证书/development-key.p12 
  openssl aes-256-cbc -k“ $ SECURITY_PASSWORD” -in脚本/provisioning-profile/profile-development-olympus.mobileprovision.enc -d -a -out脚本/provisioning-profile/profile-development-olympus.mobileprovision 
  #分布 
openssl aes-256-cbc -k“ $ SECURITY_PASSWORD” -in脚本/证书/distribution-cert.cer.enc -d -a -out脚本/证书/distribution-cert.cer
  openssl aes-256-cbc -k“ $ SECURITY_PASSWORD”-输入脚本/证书/发行密钥.p12.enc -d -a-输出脚本/证书/发行密钥.p12 
  openssl aes-256-cbc -k“ $ SECURITY_PASSWORD” -in脚本/provisioning-profile/profile-distribution-olympus.mobileprovision.enc -d -a -out脚本/provisioning-profile/profile-distribution-olympus.mobileprovision 

2.创建钥匙串并导入证书和钥匙

为了使证书和密钥在我们的版本中实际可用,我们需要将证书和密钥导入默认的钥匙串。 为了避免混淆Travis CI的默认钥匙串,我们将创建一个新的并将其设置为默认钥匙串:

  #创建自定义钥匙串 
安全创建钥匙串-p $ CUSTOM_KEYCHAIN_PASSWORD ios-build.keychain
  #将ios-build.keychain设置为默认值,以便xcodebuild使用它 
安全默认钥匙串-s ios-build.keychain
  #解锁钥匙扣 
安全解锁钥匙串-p $ CUSTOM_KEYCHAIN_PASSWORD ios-build.keychain
  #对于长构建,将钥匙串超时设置为1小时 
# 看这里
安全性设置-keychain-settings -t 3600 -l〜/ Library / Keychains / ios-build.keychain

注意还使用了另一个travis环境变量:CUSTOM_KEYCHAIN_PASSWORD

接下来,我们需要导入该新钥匙串中的所有证书和钥匙。 请注意,密钥也需要密码,这是从本地计算机上的Keychain OS X应用程序导出密钥时使用的密码。 在此示例中,我们使用与对称加密文件所使用的密码相同的密码,以简化操作。 显然,您应该在此处使用单独的安全密码:

 安全导入./scripts/certs/AppleWWDRCA.cer -k ios-build.keychain -A 
安全导入./scripts/certs/development-cert.cer -k ios-build.keychain -A
安全导入./scripts/certs/development-key.p12 -k ios-build.keychain -P $ SECURITY_PASSWORD -A
安全导入./scripts/certs/distribution-cert.cer -k ios-build.keychain -A
安全导入./scripts/certs/distribution-key.p12 -k ios-build.keychain -P $ SECURITY_PASSWORD -A
  #修复挂在codesign步骤中的OS X Sierra 
安全设置键分区列表-S苹果工具:,苹果:-s -k $ SECURITY_PASSWORD ios-build.keychain> / dev / null

提示1 :要允许任何应用程序在不发出警告的情况下访问导入的密钥,则需要在导入命令中使用-A标志

提示2 :为避免出现代码签名构建步骤在OS X Sierra中永久挂起的问题,您需要在上面添加最后一个命令:security set-key-partition-list…有关更多信息,请检查我相关的SO问题

3.安装配置文件

为了使您的构建实际使用未加密的配置文件,您需要将它们放置在将其拾取的位置。 因此,您需要将它们从我们的仓库中复制到该位置:

  mkdir -p〜/ Library / MobileDevice / Provisioning \配置文件 
  cp“ ./scripts/provisioning-profile/development-provisioning-profile.mobileprovision”〜/ Library / MobileDevice / Provisioning \ Profiles / 
  cp“ ./scripts/provisioning-profile/distribution-provisioning-profile.mobileprovision”〜/ Library / MobileDevice / Provisioning \ Profiles / 

4.存档版本

现在已经完成了身份和配置文件的配置,是时候进行实际的归档构建了。 请注意,我们正在显式地在存储库中输出归档文件,以便我们可以在下一步中轻松地将其提取,并在其中导出文件:

  xcodebuild archive -workspace示例/restcomm-olympus/restcomm-olympus.xcworkspace -scheme restcomm-olympus -configuration版本-derivedDataPath ./build -archivePath ./build/Products/restcomm-olympus.xcarchive 

一旦一切就绪,就可以将此命令通过管道传递给“ xcpretty”,以便获得美化的构建输出

5.出口分销

在导出存档并生成实际的.ipa之前,我们需要提供一个包含多个用于导出的配置选项的导出选项plist文件。 让我们在脚本中的脚本/exportOptions-Enterprise.plist中创建以下内容:

   
   


compileBitcode

方法
企业
teamID
您的团队ID
uploadBitcode

uploadSymbols


请注意,我们禁用了位码,因为某些二进制依赖项不是用它来构建的,因此整个应用程序都无法构建(如果您没有此问题,则应该能够启用它)。 另外,我们正在使用企业方法来告诉构建系统我们要创建一个用于企业分发的应用程序。

现在我们已经准备好plist了,我们可以进行实际的构建了:

  xcodebuild -exportArchive -archivePath ./build/Products/restcomm-olympus.xcarchive -exportOptionsPlist ./scripts/exportOptions-Enterprise.plist -exportPath ./build/Products/IPA 

提示:如果此构建命令失败并显示“找不到适用的设备”,则需要更新ruby配置以使用系统ruby。 为此,请遵循此SO回答中概述的逻辑,您应该获得成功的构建。 您还可以查看我在帖子末尾所指向的完整脚本中如何解决该问题-为清晰起见,我避免将其全部放在此处

现在,您应该在build / Products / IPA中拥有您的App .ipa,您可以将其免费分发给用户! 一步走…

6.上传到测试仙子

最后,您现在可以使用curl在Test Fairy上上传.ipa:

  curl -v -s http://app.testfairy.com/api/upload \ 
-F api_key = $ TESTFAIRY_API_KEY \
-F文件= @ build /产品/IPA/restcomm-olympus.ipa \
-F video = wifi \
-F max-duration = 10m \
-F comment =“ TestFairy中显示的.ipa注释” \
-F testers-groups = \
-F auto-update =关闭\
-F notify = off \
-F instrumentation = off \
-A“ TestFairy iOS命令行上传器2.1”

注1:对于api_key,我们使用另一个隐藏的Travis CI环境变量:TESTFAIRY_API_KEY

注意2:您可以在Test Fairy GitHub命令行上载存储库中找到现成的上载脚本。 同样,在全文的结尾处(我们在帖子末尾指向),我们使用的是脚本本身-为了清楚起见,我将其保留。

7.用完成的脚本更新GitHub存储库

为了测试整个过程,我们需要提交更改并推送到GitHub。 这将触发Travis CI构建,然后构建我们的App并将其上传到TestFairy。 例如,下面是成功的Travis构建日志,并将生成的.ipa成功上传到TestFairy:

请注意,TestFairy下载页面中显示的版本最后包含Travis内部版本号(即内部版本118)。 这样,我们可以将此应用程序的问题与Travis构建相关联,然后再与错误的GitHub提交相关联。

最后说明

使用类似的逻辑,您应该能够使用Travis CI将CI / CD设施添加到任何GitHub iOS存储库中。 要获得更全面的了解,您可以通过访问WebRTC Restcomm iOS SDK存储库自己检查这些设施,以下代码以.travis.yml开头。 可以在此处找到ci-script.bash的更完整版本,我们用它来构建和部署Restcomm Olympus。 有关最新的Restcomm iOS SDK版本的更多信息,请查看我们的公告。

最后但同样重要的是,要感谢Mattes Groeger在iOS版Travis CI上的帖子。 本职位试图确定自Mattes职位3年前发表以来出现的挑战,并希望成功解决这些挑战;)。

附录A:推断分发身份和配置文件

如果您在确定应该与Travis CI一起使用的是哪个分发身份和供应配置文件时遇到问题,或者在Travis CI上遇到构建问题,说明您使用的是错误的,则可以通过本地构建并检查使用了哪些。 首先构建您的应用程序的存档,然后在终端中导出该存档,该存档将向您显示所使用的身份和配置文件,然后您可以如前所述导出/复制,加密并移至您的存储库。

原因是我没有使用Xcode IDE进行构建/导出,但是终端是我还没有找到获取导出步骤的构建细节的方法:(。似乎被埋在某个地方。

要进行存档构建,请按照本文第4部分的存档构建中的说明进行操作。 之后,请按照第5节中的说明进行操作。

在这一点上,我们对生成的实际.ipa并不感兴趣,但是系统所使用的是分发身份和配置概要文件,因此我们知道需要在Travis CI中使用哪些。 要弄清楚所有这些,您需要注意export命令的输出:

  $ xcodebuild -exportArchive -archivePath ./build/Products/restcomm-olympus.xcarchive -exportOptionsPlist ./scripts/exportOptions-Enterprise.plist -exportPath ./build/Products/IPA 
  2016–12–08 13:26:58.971 xcodebuild [26052:5606768] [MT] IDEDistribution:-[IDEDistributionLogging _createLoggingBundleAtPath:]:在路径' / var / folders / hn / 3nnkp2mn3b971z549c6klmr40000gn / T / restcomm-olympus_中创建了包08_13–26–58.970.xcdistributionlogs '。 
 导出的restcomm-olympus.x归档到:/ Users / antonis / Documents / telestax / code / restcomm-ios-sdk / build / Products / IPA 
  **出口成功** 

使用Finder导航到已创建的包(即/var/folders/hn/3nnkp2mn3b971z549c6klmr40000gn/T/restcomm-olympus_2016–12–08_13–26–58.970.xcdistributionlogs),并在其中打开文件IDECodesignResolver.log 。 在那里,您应该看到xcodebuild采取的所有步骤,以确定用于分发的Identity和Provisioning Profile。

对于用于导出的身份,您应该找到这样的行(哈希等是虚构的):

  2016-12-08 11:26:59 +0000 [MT]仅限签名身份:{( 
<DVTSigningCertificate:0x7ff031ddb7b0; 名称=“ iPhone发行版:Telestax,Inc. '',哈希='19284627f9A798A9D878F8D9A8789A8D7F90F6A7',serialNumber ='8A5D9F54C8A8D6FF',certificateKind ='1.2.840.113635.100.6.1.4,issueDate ='2015-10-12 09:04:10 ''>
)}

瞧,粗体显示您需要的证书名称。 现在,对于Provisioning Profile,在IDECodesignResolver.log文件的底部,您应该看到如下一行(UUID和Team ID是虚构的):

  2016–12–08 11:26:59 +0000 [MT]在排序数组中选择顶部条目:( 
“ <DTDKProvisioningProfile 0x7ff031df76f0:UUID: 53a8d3d0–2092–1829-8121-ddf211b923f1 ,名称: XC iOS:com.telestax.olympus ,团队: G6RG91NSAE (Telestax,Inc.),平台:iOS>”,
“ <DTDKProvisioningProfile 0x7ff036c049b0:UUID: a7d8a870–1038–2829-1947-d1f2113913f1 ,名称: TeleStax Enterprise Distribution Profile ,团队: G6RG91NSAE (Telestax,Inc.),平台:iOS>”

在这种情况下,我们有两个可能有效的分发配置文件可用于分发,第一个是XCode管理的,第二个是手动创建的,xcodebuild选择第一个。 您需要查看您的情况下选择了哪一个,然后使用它。