2026 年 5 月 29 日,週四,15:03。一支十二人的 iOS 團隊,有人在 Slack #ios-ci 貼了 Run 連結,配文「本機 Xcode 十二分鐘過了,PR 還在 Queued。」半小時沒人回——這類訊息太常見。48 分鐘後同一人補了一句:「Queued 47m,run 12m。今晚別指望 merge 了。」一位 release engineer 從會議室出來看了一眼手機:「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 後再合併。四條硬規則寫進了 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 人,日合併 | 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。