窥探 Xcode Cloud

452次阅读  |  发布于3年以前

作为 WWDC 21 的一个重点产品 Xcode Cloud,我们有必要了解和熟悉它。Xcode Cloud 是专为苹果开发者设计的一款内置于 Xcode 中的 CI/CD (持续集成和交付服务)。那 Xcode Cloud 到底能干嘛,我们该怎样使用它呢?最好的办法是亲自使用,可是我的 Xcode Cloud Beta 申请还没有下来,只能通过下面视频来一窥究竟了。

请注意,这篇文章不是这些视频的直接翻译,而是我看完这些视频后对 Xcode Cloud 的一些理解和想法。老司机技术周报会有专门的文章详细地讲述这些视频的内容,你可以订阅哦。

如果你也想试用 Xcode Cloud,可以登录 App Store Connect 进行申请。如下图所示,在申请下来之前,我们是没有办法使用 Xcode Cloud 的。

Xcode Cloud Workflow

CI 已经成为项目开发中必不可少的一个工程环节,同时也是推动团队工程师文化的重要手段。为了方便苹果开发者快速搭建 CI 系统,苹果公司推出了 Xcode Cloud 服务。Xcode Cloud 服务属于全服务 CI。一般我们可以把 CI 分成三大类,下面是我在《持续集成:如何实现无需人手的快速交付?》文章中对这些 CI 分类的描述:

目前流行的 CI 构建中介主要分成三大类。

  1. 全手工维护 CI。该类 CI 从物理主机、操作系统、Ruby 环境以及 Xcode 版本都由开发团队维护。这类型的 CI 维护成本比较高,例如,需要管理物理主机和网络联通性,需要维护操作系统的安全更新等,但同时也给了我们很大管控性和灵活性。
  2. 云端虚拟机 CI。该类 CI 是通过租用云 Mac 虚拟机来进行搭建,目前比较流行的 Mac 虚拟机服务提供商有亚马逊的 AWS 和 MacStadium。有了这些云服务提供商,我们就不用再进行安全补丁等常规维护了,只需选择特定的 Mac OS 版本来启动虚拟机,然后在虚拟机上执行脚本来搭建 Ruby 和 Xcode 环境即可,就如 Moments App 项目里执行 setup.sh 脚本就能完成项目搭建。
  3. 全服务 CI。该类 CI 不仅为我们提供了虚拟机,而且还提供了 Ruby 和 Xcode 等环境,我们只需要提供一个 CI 管道配置文件就能完成这个 App 的自动构建。

没有一类 CI 是完美的,它们都有各自的优缺点。这三类 CI 从上往下看,维护成本越来越低,但从长远来看,运行成本却越来越高。从便利性来看,下面的类型会比上面的更加易用,但同时也牺牲了灵活性。

我建议你根据团队的自身情况来选择。假如你所在的团队没有专门的人员来维护 CI,可以从全服务 CI 开始。随着项目和团队的发展,慢慢地升级为云端虚拟机 CI 和全手工维护 CI。作为一个只有一个开发者的开源项目,Moments App 也选择了全服务 CI。

接下来我们就从下图中的构建流程看看 Xcode Cloud 是如何运作的。

在 Xcode Cloud 里面,一个构建流程称作为 Workflow,而其他 CI 常常通过 Pipeline 来定义流程。Xcode Cloud 的 Workflow 主要由有三大部分所组成。第一部分是触发构建,这一部分一般由 Git 分支上的变化所触发。第二部分是构建服务,通常由构建,测试,打包等动作所组成。第三部分是构建后的任务,例如发送构建状态更新通知,上传 App 到 App Store Connect 等等。下面就看一下这三部分分别做了些什么事情。

触发构建

要触发自动化构建,首先需要配置代码托管服务,在视频的演示里,使用了 GitHub 作为代码托管服务。如下图所示:

为了访问 GitHub,我们需要在 Xcode 上手工配置对 GitHub 的连接。在真实项目中,我们一般使用 GitHub API Key 来访问 GitHub,这样能帮助我们方便管理访问的权限。

成功连接 GitHub 以后,我们就可以指定 Repo 的地址以及要构建的 Workspace 名字。

接着就可以配置启动条件了。

启动条件通常由 Repo 分支上的变化所触发,例如 push 了新 commit 到 main 分支上,或者提交了新的 Pull Request 等等。我们可以根据团队的需求来添加启动条件。在配置里面有一个 “Auto-cancel Builds” 的选项。当选中该选项时,一旦分支有新的更新,Xcode Cloud 会自动取消原有的构建任务来节省花费。这个选项非常实用,我们通常会在 CI 上打开该选项。

配置完好启动条件后,我们可以在 Environment 下配置运行环境。

例如在上图中,我们指定了 Xcode 和 macOS 的版本,我们可以为不同的 Workflow 指定不同的运行环境。例如指定 beta 版本的 Xcode 来测试代码的兼容性。

构建服务

构建服务 是 Xcode Cloud 的核心组成部分。Xcode Cloud 为我们提供了构建所需的云服务基础设施,我们只需要配置构建动作就能完成构建任务。

默认情况下,Xcode Cloud 为我们添加了 Archive 动作,我们还可以按需添加 Build,Test 以及 Analyze 等动作。如下图所示:

我们先看一下 Test 动作。

我们可以选择平台,Scheme,测试的设备类型及系统版本来执行测试。在 Requirement 选项中,我们可以决定当测试失败时是否继续构建过程。在 CI 配置中,我们通常打开这一开关,这样能使得 App 在发布到 App Store 之前必须通过所有测试,从而减少 App 的 bug 和崩溃率。

现在回来看看 Archive 动作。

如上图所示,在打包的过程中,我们可以把 App 打包成内部测试的 Ad hoc 版本,或者打成 App Store 版本。Xcode Cloud 的一个亮点是为它能为我们自动管理证书和 Provisioning Profile 等文件,大大减轻管理私钥和证书等大量的手工操作。在其他 CI 里,我们往往使用 fastlane match 来管理签名信息。

构建后任务

Xcode Cloud 提供了构建后任务让我们发送状态通知以及把 App 上传到 App Store Connect 上。其他 CI 往往会把这些任务作为 Pipeline 的一部分,也就是类似构建服务里面的 Build,Test 等普通的任务。

先看看状态更新通知, Xcode Cloud 允许我们发送两类通知消息,如下图所示:

第一类是成功消息:我们可以选择发送所有的成功消息;或者只发送修复后的成功状态,或者不发送任何成功消息。第二类是失败消息:我们可以选择发送所有的失败消息;或者发送曾经成功但后来失败的构建任务;或者不发送失败消息。

配置构建后任务也很简单,只需要在 Post-Actions 菜单点击添加即可。

目前支持的通知方式有 Slack 和邮件。

第二种构建后任务是把打包后的 App 上传到 TestFlight。

上图是苹果推荐的配置信息。这种做法与我们平常配置 CI 的做法基本一致,我们常常把 Pull Request 构建并发布到内部测试组,而把 release 分支发布到 App Store Connect 进行审核。

当上传内部测试版本的时候,我们还可以选择测试组。如下图所示:

自定义脚本

为了提高可扩展性,Xcode Cloud 允许我们通过自定义脚本来执行一些 Xcode Cloud 没有提供的操作。如下图所示:

根据运行时机的不同,我们可以创造三种不同的脚本:post-clone,pre-xodebuild 和 post-xcodebuild。我们可以使用他们来完成不同任务,例如可以在 post-clone 脚本里安装 CocoaPods 等第三方依赖库。

在视频中演示了 pre-xodebuild 脚本的例子。你可以看到,这个脚本使用 CI_PULL_REQUEST_NUMBERCI_XCODEBUILD_ACTION 等环境变量。这些环境变量都是由 Xcode Cloud 所提供的。方便我们执行脚本时可以进一步定制构建的流程。在上述的脚本里,我们可以为 App 替换不同的图标。

当使用自定义脚本时,需要注意苹果给我们的建议。

其中最重要的是第三点,要注意脚本时的返回值,Xcode Cloud 会根据返回值来决定是否继续当前的构造流程。

配置 Workflow 时的一些技巧

下面我们看看配置 Workflow 时的一些技巧,这些技巧能帮我们进一步按照团队的需要来自定义构建流程。

New Build System

谢谢 红纸[5] 指出参考链接[6],要在已有项目上使用 Xcode Cloud,必须符合下面的要求。

特别注意的是 Build system。我们需要点击菜单 File -> Project Settings (或者 File -> Workspace Settings) 打开下面的配置框。

然后在 Build System 里选择 New Build System 选项。

"Restrict Editing" 选项

我们可以通过选中 "Restrict Editing" 选项来控制 Workflow 配置的访问权限,这样能保证其他团队成员不会因为不小心而做不必要的改动。

定时任务

除了监听 Git 分支的变化以外,我们还可以通过 On schedule 的方式来启动构建任务。这是常见的构建方式,例如,我们会在每天晚上执行大量测试来保证 main 分支上代码能很好地在各种设备上正常运行。下图演示了如何配置一个 On Schedule 的构建任务。

恢复 derived data

我们可以在 Environment 选项中不选中 “Clean” 选项来加快构建的速度,如下图所示。

需要注意的是,如果要构建 App Store 版本的 App,我们需要选中 “Clean” 选项来保证 App 在构建的过程中不会受到任何缓存的影响。

环境变量

为了可以构建出功能不一样的 App,例如为测试版本的 App 连接内部的 API 服务器,我们可以为 Workflow 配置不同环境变量。如果环境变量保护私密信息时,我们可以选中 Secret 选项来保证该环境变量的值不再明文显示了。下图是环境变量的配置。

依赖库管理

当前, Xcode Cloud 只支持 Swift Package Collections 来管理依赖库。如下图所示:

如果我们添加的依赖库来自于私有的 Repo,在第一次构建时会失败。我们需要在 App Store Connect 上修复这一错误,例如给 GitHub 访问配置权限等等。

Webhook

Xcode Cloud 提供了 Webhook 来帮助我们与外部系统进行交互。我们可以为不同构建状态配置各种Webhook,下图演示了在创建 Build,启动 Build 和完成 Build 时调用不同 Webhook 的情况。

视频中还给了一个如何使用 AWS Lambda 来处理 Webhook 请求的例子。

这个 Lambda 能帮我们把构建成功的信息发送到推特上。

一些想法

CI 已经成为一个团队的标配,苹果公司也意识到这一点,尝试提供官方的解决方案 Xcode Cloud,这是一个很好的开始。但苹果收购 BuddyBuild 也有三年多了,三年的时间只做出视频中产品,我感觉还远远不能满足一个成熟团队的需求,主要原因如下:

  1. Xcode Cloud 还不支持 code as configuration (代码即配置),目前 Xcode Cloud 上所有的配置都必须通过手工点击来完成。这样做容易出错,不方便重用,而且无法查看历史记录。很难应用到大型项目中。
  2. 作为全服务 CI,Xcode Cloud 方便我们快速搭建 CI 系统,但是,Xcode Cloud 的构建系统只能完成 Build,Test,Archive 等几个默认操作,不方便扩展。尽管提供了自定义脚本,Webhook 等方式,但还是停留在三年前 BuddyBuild 的做法,可扩展性不强,基本上无法满足大型项目的要求。在大型项目中,我们可以使用 fastlane 等工具来自定义几乎任何的动作和流程。
  3. 也由于 Xcode Cloud 是全服务 CI,无法进行本地调试,每次操作都需要在 Xcode Cloud 进行验证。
  4. 由于不支持 code as configuration,很难从全服务 CI 升级到其他 CI,例如云端虚拟机 CI 或者全手工维护 CI。
  5. Xcode Cloud 只支持苹果生态,不利于公司范围内多系统的重用。当我们搭建一个 CI 系统的时候,除了构建部分以外还需要连通监控系统和工单系统等。如果使用一个通用的 CI,我们就可以为 iOS,Android 以及后台系统配置统一的监控系统,方便统一管理所有的指标和流程。使用 Xcode Cloud 反而增加了额外的工作来完成对其他系统的整合。
  6. 在视频里面说,Xcode Cloud 可以支持多种 Git 托管服务,也就是可以支持 GitLab 等服务。但是从我们以前使用 BuddyBuild 的经验来看,BuddyBuild 不能做到精确限制 GitHub 的访问权限。经过后来做了一些 SSH 的支持,但功能上远比不上其他 CI。希望 Xcode Cloud 在这方面做得更好。

那 Xcode Cloud 是不是一无是处呢?当然不是,这还是第一个版本,我相信苹果还会根据用户反馈不断优化,而且苹果公司有硬件设备的成本优势,加上完善的云服务基础设施,如果能不断优化,也许能成为主流的 CI 平台。而且如果 Xcode Cloud 价格优惠,也适合小型团队快速搭建 CI 系统。

有几点我特别感兴趣的,假如运行成本很低,我们可以使用 Xcode Cloud 来定时执行大量的测试。如果能支持 Device farm 就更好了。还有,如果 Xcode Cloud 能执行开发中的代码和并行执行测试,就能很方便解决本地机器的性能问题。

不管怎么说,我觉得苹果开了个好头,大家拭目以待,多提点建议,让他们越做越好吧。

如何搭建 CI

在文章的最后,我想分享一下如何搭建一个常用的 CI。在 《iOS开发进阶》 课程里面,我详细讲述过如何从头搭建一个 CI。这里我总结一下,这只是我理解的一个 CI 搭建流程,各个公司根据具体情况可能有所不同。

首先,我们会把构建过程中的所有的步骤都开发成自动化脚本,常用的办法是使用 fastlane,也有公司直接使用 xcodebuild 或者直接调用 LLVM。原则是把常用的操作,例如构建、测试、打包、签名、上传、发送通知消息等等动作都封装成可执行的命令。这样做的好处是可以在本地开发和测试这些脚本。而且脚本可以使用到任何的 CI 系统上。目前 Xcode Cloud 无法做到这一点,它为我们提供了 Build,Test,Archive 等动作,但由于这些动作都是有苹果公司预先定义,很难进一步的优化。我举个例子,如果我们自己开发自动化脚本,就可以在构建过程中做一些优化,例如合并 Frameworks,根据 LVVM 来优化编译,减少包的大小等等。目前 Xcode Cloud 没有办法让我们进行这些优化。

然后,根据团队需要选择其中一种 CI,例如全服务 CI,云端虚拟机 CI 或全手工维护 CI。不管选择那种 CI,原则基本是,这些 CI 可以调用上一步定义好的脚本,然后通过 YAML 文件来配置构建的 Pipeline,例如主分支构建内部版本,发布分支构建 App Store 版本。还可以定义定时任务,例如晚上 12 点在多种设备上执行测试等等。有了可配置的 YAML 文件,可以方便我们快速地升级 CI,例如从全服务 CI 升级为云端虚拟机 CI。尽管 Xcode Cloud 是全服务 CI,但由于 Xcode Cloud 目前不支持 YAML 配置,所有的配置都需要手工点击,所以很难支持升级。

除了上述的 Pipeline 配置以外,根据不同的 CI 类型,额外的工作有所不同。例如,当我们选择全服务 CI 时,主要的任务是在 YAML 文件上配置 macOS 和 Xcode 的版本。

当我们选择云端虚拟机 CI 时,需要租用云 Mac 虚拟机来进行搭建,目前比较流行的 Mac 虚拟机服务提供商有亚马逊的 AWS 和 MacStadium。要使用这类型的 CI,我们需要有一个构建 CI 的 CI。这个母 CI 可以根据她自己的 YAML 文件来快速地构建出一个用于构建 App 的 CI 的 VM (虚拟机),这样能帮我们把构建环境的 VM 快速地复制到各个云 Mac 虚拟机上。当我们需要支持新的 macOS 或者 Xcode 版本时,需要更新母 CI,然后测试 App CI 的构建情况。

当我们选择全手工维护 CI 时,需要有专门的团队管理物理主机,网络联通性,安全性等等。但不代表我们把 CI 直接运行在 Host OS(宿主操作系统)上。其做法与云端虚拟机 CI 一致。也是使用一个母 CI 来构建 VM,然后把 VM 部署到我们自己维护的 Host OS 上,而不是云 Mac 虚拟机上。除非所在的公司有强大的 IT 支撑团队,否则我不建议自己维护各个物理 Mac 主机。

尽管不同的 CI 搭建的步骤有点差异,但你可能已经看到,在构建 CI 时,我们基本上是遵循组建化和容器化的原则,尽量保持所有模块都可以快速重用。同时也遵循自动化的原则,不管哪一类型的 CI,都可以做到无人手自动构建,连 VM 也是由母 CI 自动构建出了的。所以 CI 是推动工程师文化建设的重要手段,规范我们的开发流程,让我们从繁重的重复劳动中解放出来。

参考资料

[1]iOS开发进阶: https://t2.lagounews.com/lR59RGRBct5E3

[2]10267 - Meet Xcode Cloud: https://developer.apple.com/videos/play/wwdc2021/10267/

[3]10268 - Explore Xcode Cloud workflows: https://developer.apple.com/videos/play/wwdc2021/10268

[4]10269 - Customize your advanced Xcode Cloud workflows: https://developer.apple.com/videos/play/wwdc2021/10269

[5]红纸: https://github.com/nianran

[6]参考链接: https://developer.apple.com/documentation/xcode/requirements-for-using-xcode-cloud

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8