2026 年 5 月 29 日,周四,15:03。一支十二人的 iOS 团队,有人在 Slack #ios-ci 贴了 Run 链接,配文「本地 Xcode 十二分钟过了,PR 还在 Queued。」半小时没人回——这类消息太常见。48 分钟后同一人补了一句:「Queued 47m,run 12m。今晚别指望 merge 了。」另一位负责 Release 的同事从会议室出来看了一眼手机:「ios-release 那条 Archive 还在排队,PR 线又被堵死了。」
下文截图来自我们协助排障时客户提供的材料,已打码;数字未做修饰。你可对照 GitHub 官方文档中的 Job 执行时间与 queue_wait_seconds 自行复现统计。
这支团队两个 App 共仓,CI 全走 GitHub 托管 macOS Runner。2025 年还能忍;到了 2026 年春,反馈回路被拉长到让人失去耐心。GitHub Actions 并没有「坏掉」,但对 iOS 团队来说,越来越像高峰必堵的共享班车。
那个周四:打开 Job 页,黄条比绿条长四倍
他们把 Run #18472910356 的 Job 页截图发过来——不是口述,是带时间轴的实录。黄条(Queued)47 分 12 秒,绿条(Run)12 分 4 秒,界面底部写着 queue_wait_seconds: 2832、run duration: 724。公式简单得刺眼:wait time >> run time。
ios-pr.yml · macos-latest · 2026-05-29 15:04 CST当时团队还在开会讨论要不要加 -jobs 并行、要不要换 DerivedData 缓存 key——却没人拉过最近十条 macOS job 的 queued 与 run 两列数。Release 线那边更憋屈:ios-release.yml 把 xcodebuild archive、notarization、上传 TestFlight 和 PR 单测塞在同一条线、同一个 macos-latest 标签上。Archive 一次二十五到四十分钟,占着稀缺并发槽,等 PR 红灯的快反馈 job 一起被拖进队列。
若你此刻只想先排 Queued,可先读站内 macOS runner 排队诊断 Runbook。他们的转折,是从承认「问题在供给」、并且用数字而不是体感说服会议室之后才开始的。
证据:十条 failed job 抄录表
5 月 30 日站会前,有人从 Actions 运行列表手动抄了十个 failed macOS job(见下图)。没有花哨 BI——就是一张表,两列核心数字:queued_s 与 run_s。中位数 queued 2650 秒、run 724 秒;比值最高到 4.5×。那一刻会议室安静了几秒:没人再提「是不是 Xcode 17.4 的锅」。
| 常被误判为 | 图 3 里的信号 | 更可能原因 |
|---|---|---|
| GitHub 故障 | 10/10 条 queued_s > run_s | macOS 并发 cap、重任务占槽 |
| 项目变大 | run_s 稳定在 690–811s | 编译并非主因,等待才是 |
| 同事乱 push | 同一 PR 多 run(见图 2) | Agent / bot 循环 |
| 证书过期 | 签名失败分散在不同 run | 钥匙串冷启动、并行争用 |
图 2:同一 PR 被触发了六次
排队还只是前半场。三月里团队接了一个编码 Agent:CI 红 → 读日志 → 自动 commit → 再触发 Actions。PR #847 的 Checks 页截得很清楚——六个 workflow run,其中四个 actor 是 bot,两个是人。名义上仍是 CI;行为上已是 retry loop。详见 CI 被循环化之后会发生什么。
ios-pr.yml 触发(4× bot · 2× 人)· 2026-05-29团队里后来有人开玩笑:「CI 没死,是我们把它跑成了永动机。」坏的不是自动化,是每一次 bot 修复都在抢公共队列,且没有专用 Runner 池。
转折点:一台云 Mac,和 5 月 31 日拆线的那个晚上
抄录表之后,团队先把日志按 job 类型拆开:大约七成 PR 校验与集成编译,两成 Simulator 单测,一成 Archive 与上传——和 另一支团队每天五百次 iOS CI 的实测构成 惊人相似。有人提议买八台 Mac mini;有人提议迁 Xcode Cloud。最终落地两件事:
5 月 31 日,租一台 Apple Silicon 云 Mac,注册自托管 Runner(标签 self-hosted, macOS, cloud-mac)。对照实验很朴素:同一 commit 在托管 macos-latest 与云 Mac 上各 build 五十次——queued 方差从「有时 40 分钟有时 5 分钟」收成「基本个位数秒」。
当晚拆线。 把 Archive、export、上传从 ios-pr.yml 挪到 ios-release.yml,PR 线只保留 build 加单测。暖机 cron 也加上了:每天凌晨在云 Mac 上跑一次 xcodebuild build,DerivedData 留在磁盘上,比每次从 actions/cache 冷拉更稳。
图 6 · 同一团队 wall time 中位数(PR build,两周各 50 次)
数据来源:客户对照实验日志(2026-05-31 — 06-08),与图 1、图 4 单次 run 可交叉验证。
6 月 9 日:图 4 那张终于「像话」的 Job 页
拆线两周后的周一早晨,Slack 里有人贴了 Run #18510488201 的截图:queued 41 秒,run 9 分 17 秒,runner 已是 self-hosted, cloud-mac。频道里没人点赞,有人回了一个表情——在 iOS 团队里,那就是最高的褒奖。
self-hosted, cloud-mac · 2026-06-09 09:11 CST给 Agent 围栅栏:四条写进 Wiki 的规则
拆线解决 Archive 堵 PR;bot 循环还要另治。团队给 bot 单独分支和低优先级 Runner 池,人 review 后再 merge。四条硬规则写进了 Wiki:
- PR workflow 禁止 Archive / 上传商店
- macOS job 必须设
concurrency,避免同一分支叠跑 - 自托管 Runner 必须标签化,禁止与实验 workflow 混池
- Agent 自动 push 必须走独立分支,禁止直推保护分支
queue_wait_seconds 与 run duration(如图 3);统计单 PR workflow 次数(如图 2);确认 Archive 是否还在 PR workflow 里。若中位数 queued > run,先谈 Runner 供给,别先调编译参数。
新解法是什么:不是抛弃 GitHub,是换供给方式
这支团队的经历,浓缩成 2026 年 iOS 团队最常落地的四件事:云 Mac 自托管 Runner 池、PR / Release 拆线、暖机与磁盘缓存、Agent 护栏。Xcode Cloud 能替代吗?适合 Apple 生态闭环;与 GitHub PR 检查、混合 Android 线组合时,多数团队仍保留 Actions,只把 Archive 迁到专用 macOS 算力。
| 若你的团队像…… | 常见落地建议 |
|---|---|
| 1–3 人,周发版 | 托管 macOS + 强缓存;Release 手动 Archive |
| 5–15 人,日 merge | 1–2 台云 Mac 自托管 + PR/Release 拆线 |
| 15+ 人,多 App | Runner 池按 App 分标签 + 暖机 cron |
| 重度 Agent 修复 | 独立 bot 池 + 禁止 Archive 进 PR workflow |
Apple 官方 xcodebuild 文档 强调 scheme 与 destination 一致;CI 若环境每次冷启动,「本地能过」会失去意义。客户原话大概是:比每天对着 47 分钟黄条,证书轮换和 Xcode 大版本升级反而更好忍受。
从第一台云 Mac 自托管 Runner 说起
若你的 Job 页也像图 1——黄条压过绿条——下一步最划算的投资通常是一台专属 Apple Silicon 云 Mac:注册为 GitHub 自托管 Runner,让 PR 编译从「排队抽奖」变成图 4 那样可预期的九分钟。
先跑两周对照实验:同一分支在托管 macOS 与云 Mac 自托管各 build 五十次,抄一张像图 3 的表。数据会告诉你,究竟是 Xcode 慢,还是供给在拖慢 iOS 开发。
从一台可预期的云 Mac 开始,把 Archive 请回 Release 线。 查看 VPSSpark 云 Mac 套餐, 注册你的第一台 iOS 自托管 Runner。