先說結論:GitHub Actions 跑 iOS CI 會慢,通常不是你專案太重,而是你把最吃機器、最吃快取的 Xcode 鏈路交給了「每次都像新機」的環境。這篇會用白話拆給你看:為什麼 Mac 上的 iOS CI 常常卡在 pod install、xcodebuild archive,以及為什麼 self-hosted runner 才是實際能把速度穩下來的做法。延伸閱讀可先看 Runner 節點 TCO、Flutter iOS CI 快取、Xcode 卡頓解法,以及 算力基建趨勢。
1)為什麼 GitHub Actions 跑 iOS CI 會慢?
iOS CI 的瓶頸不在 YAML,而是在 macOS build chain:簽名、Pods、DerivedData、Xcode toolchain 全都要在 Mac 上完成。GitHub 託管 runner 每次重建環境,快取命中率有限,流程自然回到「每次都接近冷啟動」。你會看到同一條 pipeline 今天 14 分鐘、明天 28 分鐘,抖動很大。
2)時間到底花在哪?(iOS CI 慢點分解)
| 階段 | 常見耗時 | 為何在託管環境特別慢 |
|---|---|---|
pod install | 4–12 分 | Pods/Specs 快取不連續,常重新抓依賴 |
xcodebuild archive | 8–20 分 | DerivedData 不持久,編譯優勢難累積 |
| Signing / Export | 2–8 分 | 鑰匙圈與憑證初始化重複做 |
| Upload TestFlight | 2–10 分 | 網路路由與佇列不固定,時間波動大 |
3)實務可用架構:Linux 測試 + Mac 自管 Runner
GitHub Actions iOS CI 建議拓樸
lint · unit test · Android
Runner 上要保留的三層快取
- CocoaPods cache +
Pods DerivedData- Ruby/Bundler 與工具鏈
4)self-hosted runner 要怎麼設才會快?
- 固定機器與固定路徑:不要每次換工作目錄,讓快取真的命中。
- 鎖版本:Xcode、CocoaPods、Ruby 版本固定,減少「今天過、明天炸」的漂移。
- 限制並發:同一台 Mac 建議 1–2 條 iOS job,避免互搶 I/O。
- 把重更新動作抽離:例如
pod repo update改成每日/每週維護 job,不要每次 release 都跑。 - 監控磁碟與快取大小:DerivedData 爆掉時,速度會直接崩盤。
官方文件可搭配看:GitHub 的 self-hosted runner 說明 與 Apple 的 Xcode release notes。
5)Before / After:自管 Runner 的速度差異
| 環境 | 首次建置(冷) | 第二次(熱快取) | 穩定度 |
|---|---|---|---|
| GitHub 託管 macOS | 16–24 分 | 14–20 分 | 中,波動較大 |
| 自管 Mac mini M4 Runner | 14–22 分 | 5–9 分 | 高,可預期 |
6)Workflow 範例(可直接改成你專案)
name: release-ios
on:
push:
branches: [main]
tags: ['v*']
jobs:
ios:
runs-on: [self-hosted, macos, ios]
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
bundle install
cd ios && pod install && cd ..
- name: Build archive
run: xcodebuild -workspace App.xcworkspace -scheme App -configuration Release archive
- name: Export IPA
run: xcodebuild -exportArchive -archivePath build/App.xcarchive -exportOptionsPlist ios/ExportOptions.plist -exportPath build
另外,若你是 Flutter 團隊,思路完全一樣:用 Linux 跑測試,把 iOS 打包交給 Mac runner。細節可對照 這篇 Flutter 實作。
7)落地清單:速度之外,還要顧穩定
- 簽名資產:憑證與描述檔集中管理,避免人手機器差異。
- 權限分離:Runner 用專用系統帳號,減少手動操作污染。
- 重試策略:只重試網路步驟,不要整條 pipeline 無腦重跑。
- TCO 盤點:硬體、託管、維運時間一起算,別只看月費。
若你正在算成本,可直接接著看 TCO 拆解 和 Mac mini 定價。
8)什麼時候該從託管改成 self-hosted?
| 團隊情境 | 建議 | 理由 |
|---|---|---|
| 每月 iOS 發版 ≤ 1 次 | 先用託管 | 導入成本不一定回本 |
| 每月 2–4 次,且常等 CI | 試行 1 台 self-hosted | 可快速驗證速度收益 |
| 每週固定發版 / 多 App | 直接上自管 | 時間與穩定性收益最大 |
| 跨區團隊協作 | 選離主倉最近區域 | 減少下載與上傳延遲 |
如果你最近也在關注算力資源競爭,這個趨勢脈絡可參考 AI 算力基建戰。
9)FAQ(8 題,實務口語版)
Q1:我只有一個 App,真的需要 self-hosted 嗎?
如果你一個月才發一次,先不用急;但只要團隊開始抱怨「每次都在等 iOS CI」,就該評估了。
Q2:最先優化哪一步最有感?
先把 pod install 和 DerivedData 快取做對,通常立刻有感。
Q3:self-hosted 會不會很難維護?
一開始要花半天到一天打底,但之後日常維護其實可控,重點是版本固定。
Q4:跟 Xcode 本地慢有關嗎?
高度相關。你本地慢的點,CI 只會放大。可一起看 Xcode 優化。
Q5:一定要接 Fastlane 嗎?
不一定。即使先不用 Fastlane,只做穩定打包 + 上傳 TestFlight,也能先拿到主要收益。
Q6:Runner 放辦公室還是雲端?
看網路與維運能力。要高可用、遠端協作,雲端通常更省心。
Q7:會不會有資安風險?
會,所以要做權限最小化、專用帳號、Secrets 管理與審計記錄。
Q8:如果我要開始,第一步是什麼?
先挑一條最痛的 iOS release pipeline,做 A/B 對照(託管 vs self-hosted)一週,數據會自己說話。