某次构建用了相近的总时长,真正的慢点却可能完全不同:一次卡在依赖安装,另一次耗在设备测试矩阵。Xcode Cloud 构建太慢时,不要先迁移:本周先用构建报告拆分队列、环境准备、编译、测试和归档,再按持续出现的瓶颈决定优化、迁移远程 Mac,或采用双轨 CI。

01

谁适合看这篇文章

如果你在使用 Xcode Cloud,构建时间不断增加,却还不知道慢在哪里,这篇文章适合你。

如果你的 CI 需要安装复杂依赖、保留缓存、访问内网或运行常驻服务,也可以用本文的判断条件评估远程 Mac。

研发负责人则可以据此决定:继续消耗 Xcode Cloud 使用量、迁移部分任务,还是让两套环境分别承担不同职责。

02

先把“慢”拆成五段,而不是看一个总时长

Xcode Cloud 的一次工作流可能包含克隆代码、解析依赖、运行自定义脚本、执行 xcodebuild、测试、归档和上传产物。Apple 文档明确说明,工作流动作会在临时构建环境中完成,而且不同动作可能分别创建环境、解析依赖并执行脚本。(Apple 工作流动作文档)

所以你要先建立一份同一提交的耗时记录:

  1. 队列等待:构建提交后多久才开始执行。
  2. 环境准备:克隆仓库、解析依赖和安装工具花了多久。
  3. 实际编译:build、archive 或 build-for-testing 的执行时间。
  4. 测试阶段:单元测试、UI 测试和不同设备组合分别耗时多少。
  5. 产物处理:签名、归档、上传和通知是否形成尾部等待。

建议选择同一个提交、同一个 Scheme 和同一组测试范围,分别保存一条正常样本和一条异常样本。不要把不同提交、不同设备矩阵、不同工作流的总时长放在一起比较。

App Store Connect 可以查看构建数量、构建时长和使用趋势,并支持导出使用数据;但使用时间和用户看到的构建完成时间并不完全相同,因为 Xcode Cloud 会并行处理部分任务。(Apple 使用数据文档)

⚠️ 经验判断:如果你只有流水线总时长,没有动作日志和使用数据,就还不能证明 Xcode Cloud 本身变慢。先补证据,再改工作流。

03

第一步:处理反复出现的依赖准备

每次都重新安装依赖,通常不是“编译器性能不足”,而是工作流把环境准备成本重复支付了。

Xcode Cloud 使用临时构建环境。项目需要的第三方工具、私有依赖和脚本资源,必须在工作流开始时重新获得或安装。Swift Package 依赖应提交 Package.resolved,否则依赖解析可能偏离你本地验证过的版本。(Apple 依赖管理文档)

你可以按下面顺序检查:

  • Package.resolved 是否已提交,且没有被忽略。
  • 私有 Git 仓库和内部框架是否已经授权。
  • ci_post_clone.sh 是否每次都安装相同工具。
  • 脚本是否把下载、解压、生成代码和依赖解析混在一起。
  • 是否在每个动作中重复执行同一套初始化命令。
  • 依赖下载失败后,脚本是否进行了无上限重试。

自定义脚本可以放在 ci_scripts 目录中,常见阶段包括克隆后、xcodebuild 前和 xcodebuild 后。Apple 也要求脚本正确返回非零退出码,否则失败可能被推迟到后续步骤才暴露。(Apple 自定义脚本文档)

优化动作与停止条件

先把脚本改成可观察的形式。每个重要步骤输出开始和结束标记,记录依赖解析、工具安装、代码生成和上传的耗时。不要把令牌、密钥或完整认证信息写入日志;被标记为 secret 的环境变量会在构建日志中隐藏具体值。

然后做两次对照:

  • 一次使用完整初始化流程。
  • 一次只执行必要的构建准备。

如果删除非必要安装后,构建恢复稳定,继续留在 Xcode Cloud 更合理。如果每次都必须启动本机服务、访问内网、保留大体积生成物,或者需要管理员权限,迁移相关任务到远程 Mac 的理由就开始成立。Apple 文档说明,自定义脚本不能通过 sudo 获得管理员权限。(Apple 依赖管理文档)

04

第二步:分清缓存收益与环境残留

“缓存没生效”不是一个足够精确的结论。

Derived Data、Swift Package 依赖、代码生成目录、测试产物和自定义脚本生成文件,属于不同类型的状态。即使某部分缓存被复用,也不代表整个动作可以跳过依赖解析或重新生成步骤。

你需要分别观察三类结果:

  • 全新构建:用于确认项目能否从干净环境复现。
  • 依赖复用后的构建:用于判断锁文件和依赖获取是否稳定。
  • 小范围代码变更后的增量构建:用于判断日常反馈是否真的变快。

不建议为了追求一次更短的时间,保留无法解释的环境残留。缓存如果不能被验证、清理和重建,后续失败会变得更难定位。

对专业团队来说,缓存的价值不只是“少下载一次”。你还要记录它是否导致以下问题:

  • 依赖版本与锁文件不一致。
  • 生成文件来自旧提交。
  • 测试偶尔读取错误的资源。
  • 清理后构建结果无法复现。
  • 新成员或新分支无法在相同环境完成构建。

如果你需要的是可复现发布,干净构建仍应保留在某个工作流中。Xcode Cloud 支持配置不同工作流和动作,你可以把日常快速验证与完整验证拆开,而不是让每次提交都执行最重的流程。

05

第三步:测试矩阵慢时,先拆职责再减设备

Xcode Cloud 的测试动作不只是“运行测试”。测试阶段还可能包含测试构建、多个平台、多个 Scheme 和多组设备配置。Apple 建议将快速、短时测试与更全面、长时间测试放到不同工作流中。(Apple 工作流动作文档)

判断顺序建议如下:

  1. 拉取请求工作流只保留能快速发现编译错误和核心回归的测试。
  2. 主分支工作流增加完整单元测试和关键 UI 测试。
  3. 定时工作流再执行更广泛的设备和系统组合。
  4. 发布工作流负责归档、签名和分发,不要混入所有实验性测试。
  5. 对无效旧提交启用自动取消,避免过时构建继续占用执行资源。

Xcode Cloud 的 Auto-cancel Builds 可用于同一工作流的新构建排队场景。开启后,旧提交可能在新提交到来时被取消,从而减少“每个提交都排队等完整测试”的浪费。(Apple Xcode Cloud 工作流参考)

设备减少还是工作流拆分

如果所有测试都在同一动作中串联,先拆工作流。这样你可以保留发布前的完整矩阵,同时让日常提交只运行小范围验证。

如果工作流已经拆开,但快速验证仍然被设备组合拖慢,再减少快速工作流中的设备类型。不要一开始就删除边缘系统测试,因为那会把问题从“反馈慢”变成“覆盖不足”。

以下情况更适合设计远程 Mac 测试池:

  • 测试需要固定的模拟器状态。
  • 多个任务要共享预置测试资产。
  • 测试脚本需要访问内网服务。
  • 团队需要自行控制节点并发。
  • 测试任务之间存在长期复用的环境状态。

这不是把所有工作流搬走,而是让 Xcode Cloud 负责标准验证,让远程 Mac 承担需要主机控制权的测试任务。

06

第四步:检查脚本和网络依赖造成的长尾等待

Xcode Cloud 提供预定义环境变量,包括当前工作流、Scheme、构建动作和构建标识。临时环境还会提供标准的 HTTP_PROXY 和 HTTPS_PROXY 变量,网络工具是否正确读取这些变量,会直接影响下载和上传行为。(Apple 环境变量文档)

对自定义脚本重点检查:

  • 下载命令是否设置合理的失败处理。
  • 重试是否只针对可恢复的网络错误。
  • 上传前是否先检查文件是否存在。
  • 外部 API 调用是否有明确退出码。
  • 日志是否能区分 DNS、认证、下载和服务器响应。
  • 失败后是否会继续执行无意义的后续步骤。

一个常见问题是脚本没有失败,但也没有真正完成任务。例如下载命令等待网络,外部服务没有响应,脚本继续进入下一步,最后才在归档或上传阶段失败。这样的日志看起来像“Xcode 构建太慢”,实际慢点在构建之外。

如果任务需要长期后台进程、内网白名单、固定 IP、物理设备或跨构建保留文件,临时环境通常不是理想载体。此时远程 Mac 的价值不在于宣传中的某个峰值性能,而在于你能控制主机、进程、文件系统和恢复流程。

07

常见问题:从日志到迁移判断

依赖为什么会在不同构建中反复安装?

因为 Xcode Cloud 的构建环境是临时的,不能直接等同于你本地长期运行的开发机。你需要检查锁文件、私有依赖权限和 post-clone 脚本,确认哪些工具真的必须每次安装。若初始化步骤无法压缩,且任务还依赖持久缓存或本机服务,再考虑迁移。

在哪里查看一次构建的详细耗时?

先在 App Store Connect 打开对应 App 的 Xcode Cloud 页面,查看构建报告、动作日志和使用数据。使用数据可以帮助你观察构建数量、构建时长和趋势,但不要用它替代动作级日志。总时长必须拆成队列、准备、编译、测试和归档后再分析。

测试矩阵变慢时,应该先减设备还是拆工作流?

先拆分职责,再减少设备。拉取请求适合快速验证,主分支适合回归,定时任务适合完整矩阵。如果不同测试之间共享环境、并发冲突明显,继续减少设备未必解决问题,应该拆成独立工作流或交给可控的远程 Mac 测试节点。

哪些信号说明任务适合迁到远程 Mac?

当持续瓶颈来自重复初始化、内网连接、常驻服务、持久缓存或主机权限不足时,才进入迁移评估。先迁移最慢的一类任务,使用同一提交和同一 Scheme 对照运行,确认构建复现、日志完整、重启恢复和维护投入,再决定是否扩大范围。

08

用同一项目做三种方案判断

不要把“继续优化”和“迁移远程 Mac”理解成二选一。对大多数团队,更可靠的方式是先按任务拆分,再决定哪些动作留在 Xcode Cloud。

方案 更适合的任务 主要优点 主要限制 判断信号
继续使用 Xcode Cloud 标准编译、基础测试、TestFlight 发布验证 工作流集成度高,配置入口集中,适合标准化任务 临时环境不适合长期保留复杂状态 依赖可锁定,脚本短,测试范围可分层
迁移到远程 Mac 常驻服务、复杂初始化、内网测试、专用脚本 可控制主机、文件、进程和恢复流程 需要自行维护 macOS、权限、网络和 CI 节点 日志显示准备和环境限制长期占主要时间
双轨运行 标准构建加复杂测试或专用发布流程 保留 Xcode Cloud 的标准验证,同时获得主机控制权 两套环境需要统一版本和结果对照 发布顺畅,但某类测试或构建任务持续受限

迁移时不要先复制整条流水线。优先选择一个边界清晰的任务,例如完整 UI 测试、依赖初始化复杂的归档,或必须访问内网的验证步骤。等它在远程 Mac 上完成稳定复现,再决定是否扩大范围。

如果你需要评估远程节点的登录、权限、Xcode 安装、重启和构建恢复,可以先参考 远程 Mac 租赁开发环境验收。若重点是长期 CI 并发,再结合 Mac CI/CD 节点方案 做节点数量和任务拆分。

检查项 继续优化 Xcode Cloud 迁移远程 Mac 双轨 CI
依赖是否能锁定 ✅ 能 ✅ 能 ✅ 两边都能
是否需要常驻服务 ❌ 不适合 ✅ 适合 ✅ 复杂任务放远程 Mac
是否需要内网访问 ⚠️ 先验证代理与权限 ✅ 可按网络方案设计 ✅ 标准任务留在 Xcode Cloud
是否需要主机级权限 ⚠️ 受临时环境限制 ✅ 可控范围更大 ✅ 按任务拆分
是否需要完整测试矩阵 ✅ 可用不同工作流分层 ✅ 可自建测试池 ✅ 快速测试与复杂测试分开
是否要降低维护量 ✅ 更省节点维护 ❌ 需要维护主机 ⚠️ 维护两套环境
最适合的下一步 优化日志、缓存和触发条件 先做短周期对照 先迁移单一瓶颈任务
09

本周建议的 5 步执行顺序

  1. 选定一个提交和一个 Scheme,保存一次正常构建与一次异常构建。
  2. 从构建报告提取阶段耗时,至少区分准备、编译、测试和归档。
  3. 检查依赖与脚本,确认是否重复安装工具、重复解析依赖或等待外部服务。
  4. 重做触发策略,把快速验证、主分支回归和完整测试拆开,并检查自动取消设置。
  5. 只迁移一个瓶颈任务,在远程 Mac 上验证构建复现、日志完整、重启恢复和维护成本。

不要设置一个未经官方确认的“超过多少分钟就必须迁移”的硬阈值。Apple 官方资料确认了临时环境、工作流动作、构建报告、缓存、自定义脚本和自动取消等机制,但没有给所有项目统一适用的“过慢”标准。

如果你现在的方案只是继续增加 Xcode Cloud 工作流,却没有减少重复初始化、无效测试触发和长尾网络等待,计算用量会继续被低价值步骤消耗。反过来,直接把全部任务搬到自建节点,也会增加 macOS 更新、权限、签名、网络和故障恢复的维护负担。

当日志已经证明主要时间花在临时环境重复准备、持久服务缺失或主机控制权不足时,短周期租赁一台远程 Mac 做对照,通常比立刻全面迁移更稳妥。VpsMesh 提供真实 Mac 主机的远程访问和 root 权限,适合先验证复杂任务是否能稳定复现,再决定局部迁移还是长期双轨;你可以从 VpsMesh 的远程 Mac 方案开始核对适合的节点与周期。