結論:WWDC 26 之後要在 CI 裡試 Xcode 26 beta,正確做法不是把 main 的 workflow 改成 macos-latest 然後祈禱——而是在遠端 Mac 上拆兩條 Runner 車道:正式環境繼續釘 Xcode 16 穩定版,beta 走獨立 label 加獨立 DerivedData 目錄。
三條硬規則:① 正式 job 禁止呼叫 sudo xcode-select;② beta 與正式的 -derivedDataPath 必須物理隔離;③ iOS 27 Simulator runtime 只在 beta Runner 預裝一次,別讓每個 job 重複下載。
本文邊界:不談「為什麼 WWDC 後 CI 變慢」(見 WWDC26 iOS CI 變慢文),只給 48 小時可執行的 Runbook。
6 月 10 日那篇我們說過:WWDC 後 iOS CI 的震感來自快取清零、SDK 變胖、託管 Runner 壅塞。一週過去,Slack 裡的新問題變成實操向的——「beta 裝在哪台 Mac?」「一台機器能跑兩個 Xcode 嗎?」「main 紅了誰負責?」這篇就是答案:用遠端 Mac mini 當 self-hosted runner,把 beta 驗證與 App Store 發版徹底分流。若你還沒上 self-hosted,先讀 iOS CI 加速指南;節點與月租對比見 六地 Runner TCO。
1)先排除三個誤讀:一台 Mac 混跑 ≠ 省錢
WWDC 後最常見的錯誤決策,是在唯一一台 CI Mac 上同時裝 Xcode 16 與 26 beta,然後靠 workflow 裡的 xcode-select -s 切換。看起來省了一台月租,實際代價是:
- 競態:兩個 job 並行時,後執行的
xcode-select會改掉全域路徑,正式 Archive 可能意外用到 beta 編譯器。 - 快取污染:共用
~/Library/Developer/Xcode/DerivedData時,Swift module 版本混編,報錯像「幽靈 bug」——本機重現不了,CI 隨機紅。 - 磁碟爭搶:兩套 Xcode 加兩套 Simulator runtime 輕鬆占 80GB 以上;16GB 記憶體機器在 beta 全量編譯時 swap,拖慢所有 job。
2)雙 Runner 架構:label 池與 workflow 分流
推薦的最小可行架構如下——兩台遠端 Mac mini M4(或一台正式環境加一台按日租的 beta 試錯機):
| Runner | GitHub Label | Xcode 版本 | 負責 job |
|---|---|---|---|
| 正式機 A | self-hosted, macos, ios-prod | Xcode 16.4(釘死) | main Archive、TestFlight、發版 tag |
| Beta 機 B | self-hosted, macos, xcode26-beta | Xcode 26 beta | ios-27-* 分支、 nightly 適配、API 探測 |
workflow 側用 runs-on 硬分流,禁止 beta 分支觸發走 ios-prod label:
name: iOS Production CI
on:
push:
branches: [main, release/*]
jobs:
archive:
runs-on: [self-hosted, macos, ios-prod]
env:
DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer
DERIVED_DATA: /var/ci/deriveddata/prod
steps:
- uses: actions/checkout@v4
- name: Build & Archive
run: |
xcodebuild -scheme MyApp -configuration Release \
-derivedDataPath "$DERIVED_DATA" \
-archivePath build/MyApp.xcarchive archive
name: iOS 27 Beta Adapter
on:
push:
branches: [ios-27-*, feature/siri-ai-*]
schedule:
- cron: '0 2 * * *' # nightly,不阻塞 merge
jobs:
beta-build:
runs-on: [self-hosted, macos, xcode26-beta]
continue-on-error: true # beta 紅不擋 main
env:
DEVELOPER_DIR: /Applications/Xcode_26_beta.app/Contents/Developer
DERIVED_DATA: /var/ci/deriveddata/beta
steps:
- uses: actions/checkout@v4
- run: xcodebuild -scheme MyApp -sdk iphonesimulator build \
-derivedDataPath "$DERIVED_DATA"
註冊 Runner 與 label 命名規範見 GitHub self-hosted runner 文件。要點:label 即契約——誰改 label 誰背鍋。
3)遠端 Mac 上安裝 Xcode 26 beta 與多版本共存
在裸金屬遠端 Mac上,你可以把兩個 Xcode 並排裝在 /Applications,用目錄名區分版本,避免 beta 覆蓋穩定版:
- 從 Apple Developer Downloads 拉取 Xcode 26 beta(.xip),SSH 登入遠端 Mac 後解壓到
/Applications/Xcode_26_beta.app。 - 正式機只接受
DEVELOPER_DIR環境變數,workflow 裡寫絕對路徑;禁止 job 內sudo xcode-select -switch。 - 首次安裝後手動跑一次
xcodebuild -runFirstLaunch與 license accept——這些步驟不要放進每次 CI job。 - iOS 27 Simulator runtime 在 beta 機預裝:打開 Xcode → Settings → Platforms 一次裝完;workflow 裡刪除
-downloadPlatform步驟。
若團隊本機開發也要混版本,可參考 告別 Xcode 卡頓 裡的混合工作流:本機寫 Swift,重編譯丟雲端 M4——beta 適配同樣適用,別把 beta 索引和編譯壓在 16GB 筆電上。
4)DerivedData / Pods / SPM 三路快取隔離
WWDC 季快取失效的根因是編譯器與 module 格式變了,不是「快取壞了」。隔離策略如下:
| 快取類型 | 正式路徑 | Beta 路徑 | 備註 |
|---|---|---|---|
| DerivedData | /var/ci/deriveddata/prod | /var/ci/deriveddata/beta | workflow 寫死 -derivedDataPath |
| CocoaPods | /var/ci/cocoapods/prod | /var/ci/cocoapods/beta | CP_HOME_DIR 或 --deployment |
| SPM | /var/ci/spm/prod | /var/ci/spm/beta | clonedSourcePackagesDirPath |
| ModuleCache | 隨 DerivedData 父目錄 | 獨立 beta 樹 | 切勿共用預設 ~/Library |
actions/cache 在 WWDC 季往往不夠:Xcode 大版本切換後 cache key 全廢,上傳數 GB DerivedData 的網路時間常比本機磁碟更虧。self-hosted 的價值正是磁碟跨 job 存活——第二次 beta build 才會從 20 分鐘以上回落到 10 分鐘檔。Flutter 三快取細節見 Flutter iOS CI;簽名與 TestFlight 上傳見 iOS CI 加速指南 的 Signing 章節。
sudo mkdir -p /var/ci/{deriveddata,cocoapods,spm}/{prod,beta}
sudo chown -R $(whoami) /var/ci
# 磁碟監控:DerivedData 膨脹時按 lane 清理,勿 rm -rf 整盤
du -sh /var/ci/deriveddata/*
5)典型踩坑:beta 崩潰、SDK 變胖、鑰匙串串台
我們在客戶現場見過這些高頻事故——每條都有明確預防:
- beta 紅一片阻塞 merge:把 beta workflow 設為
continue-on-error: true,且不要作為 required status check。beta 不穩定是預期,不是工程師失職。 - 正式誤用 beta SDK:Archive 日誌裡檢查
DTXcode與DEVELOPER_DIR;在 prod job 開頭加斷言:test "$DEVELOPER_DIR" = "/Applications/Xcode_16.4.app/Contents/Developer"。 - 鑰匙圈 / Match 證書串台:兩台 Runner 各用獨立 login keychain;Fastlane match 的
git_url可共用,但匯入證書的 keychain 密碼按機隔離。詳見 Apple 的 Xcode 支援矩陣。 - 磁碟滿:beta DerivedData 加雙 Simulator runtime 增長極快;建議 beta 機 512GB SSD,每週 cron 清理
beta樹下超過 14 天未存取的 subfolder。 - 並行 Archive OOM:M4 16GB 同時跑兩個 Archive 會 swap;正式機 concurrency 設為 1,beta 機可 1–2 視工程而定。
6)樣本:拆 Runner 前後同一倉庫 P50(非 SLA)
以下為 Swift/UIKit 加 CocoaPods 中等工程、main 正式 job 在 WWDC 後首週的對比——未拆 Runner(全員升 beta)vs 拆雙 Runner(正式仍 Xcode 16):
| 指標 | 未拆分(main 升 beta) | 雙 Runner 隔離 |
|---|---|---|
| main Archive P50 | ~42 min | ~11 min(與 WWDC 前持平) |
| beta 適配 job P50 | (與 prod 搶同一台) | ~22 min(首週全量,第二週 ~12 min) |
| main 因 beta 誤傷失敗率 | 高(編譯器/快取混用) | 接近 0 |
| 所需 Mac 數量 | 1 台(看似省) | 2 台 M4 或 1 正式 + beta 日租 |
數字因倉庫而異,請用自家 workflow 跑 48 小時 A/B。核心不是「快多少」,而是main 不再為 beta 買單。
7)48 小時落地清單
- Day 0 上午:鎖定正式 Xcode 版本,改 prod workflow 寫死
DEVELOPER_DIR;main 上禁止合併任何「升級 Xcode 26」的 CI 改動。 - Day 0 下午:開一台遠端 Mac mini(或 48h 日租)註冊為
xcode26-betaRunner;裝 Xcode 26 beta 加 iOS 27 runtime。 - Day 1:建立
/var/ci/...快取目錄;跑通 beta workflow 首次全量 build;確認 prod job 未受影響。 - Day 2:把 beta workflow 改為 nightly 加
ios-27-*分支觸發;從 GitHub branch protection 移除 beta check 的 required 標記。 - 驗收:連跑 3 次 main Archive < 15 min(視工程);beta job 失敗不 Slack @channel。
不確定第二台是否值得月租?先用日租 beta 機跑完 iOS 27 適配 spike,再決定是否並席——TCO 模板見 MacBook Pro vs 雲端 Mac 決策。工具鏈為何只能 macOS 獨占,背景見 Xcode 工具鏈獨占文。
8)常見疑問(15 條精選)
1. 一台遠端 Mac 能裝兩個 Xcode 嗎? 能。用不同 .app 目錄名並存;CI 上用 DEVELOPER_DIR 指定,別依賴全域 xcode-select。
2. beta Runner 必須 24GB 嗎? 單 scheme 中等工程 16GB M4 通常夠;多 target 並行 Archive 建議 24GB。
3. 正式能否偶爾跑 beta job? 不建議。即使分時複用,也至少分 DerivedData 目錄;最好物理分機。
4. 託管 macos-latest 能代替 beta Runner 嗎? 不能。ephemeral 磁碟每次冷啟動,WWDC 季 bootstrap 可占 15 分鐘以上,且無持久快取。
5. actions/cache 夠用嗎? 大版本切換後 key 失效;大 DerivedData 上傳下載不如本機磁碟。
6. beta 不穩定導致 CI 紅一片? 預期之內;beta lane 設 optional,勿阻塞 main merge。
7. 如何確認 prod 沒用 beta 編譯器? 查 Archive 日誌 DTXcode 或在 job 開頭斷言 DEVELOPER_DIR。
8. Simulator runtime 每個 job 都要下嗎? 不要。beta 機預裝一次,workflow 刪掉 download 步驟。
9. 兩台 Runner 能不同地區嗎? 可以。正式選離團隊近的節點;beta 可與 prod 同區降低 ops 複雜度。
10. OpenClaw 能管 beta Runner 嗎? 可以 webhook 觸發,但本文不展開 Gateway;CI 註冊見 OpenClaw CI Runner FAQ。
11. 日租 beta 機夠嗎? 適配 spike 夠;若 nightly 長期跑,月租更省。
12. DerivedData 多大要清理? 單 lane 超 40GB 建議按 scheme 清理;正式 lane 謹慎,優先刪 beta。
13. Swift 6 並發檢查更嚴怎麼辦? 在 beta lane 單獨開 strict 標誌,prod 保持現有設定,分階段遷移。
14. 和 WWDC 文什麼關係? 那篇講「為什麼慢」;這篇講「怎麼拆 Runner 落地」。
15. 48h 內必須兩台嗎? 若 main 暫不碰 beta,一台 prod 即可;beta 適配可等日租機到位再開 lane。
Beta 可以折騰,正式環境必須穩
WWDC 後最划算的配置,往往是一台月租正式 Runner 加一台按需 beta 機:正式 Mac mini M4 7×24 保 TestFlight 節奏,beta 用日租試跑 iOS 27 適配,試完再決定是否並席。Nuvcloud 裸金屬獨享磁碟,DerivedData 跨 job 存活;M4 16GB 對絕大多數 iOS Archive 夠用,24GB 留給多 scheme 並行。低功耗、無風扇,適合長期無人值守 CI。
若你正在 WWDC 後規劃Xcode 26 beta CI,又不想拿 main 當試驗場, Nuvcloud 雲端 Mac mini M4 是成本最低的拆分起點—— 立即了解套餐方案 ,48 小時把 beta / 正式 Runner 拆清楚。