1–5 分鐘開通

專屬 macOS Runner
跳過公共佇列

$19.8 / 天起 · 實體機獨享
配置雲端 Mac
完整 Xcode SSH 連線

雲端 Mac 跑 Xcode CI/CD:GitHub Actions 與 Jenkins 實戰指南

每次 push 都要等 macOS Runner 空出來?本地 MacBook 被夜間建置佔滿記憶體? 這篇筆記記錄在 ZovCloud 獨享 Mac mini M4 節點上,從 Xcode 環境基線、證書匯入, 到掛載 GitHub Actions 自託管 Runner 與 Jenkins Agent,再到 Archive 簽名與 TestFlight 上傳的完整接線過程—— 附我們在真實中型 SwiftUI 工程上的耗時資料與鑰匙串踩坑備忘。

Apple 工具鏈為何綁死在 macOS 硬體上

與 Android 可以在 Linux 容器裡跑 Gradle 不同,iOS 與 macOS 的開通鏈路—— xcodebuildcodesignnotarytool、App Store Connect API—— 全部要求執行在 Apple 簽過名的作業系統與晶片上。這不是「裝個模擬器就能繞過」的問題: Archive 與 App Store 分發證書鏈、Swift 編譯器對 Apple Silicon 的最佳化路徑, 都假定你腳下是一台真實的 Mac。

因此每個有 iOS 產品的團隊,遲早要回答同一個問題: 誰來長期提供這臺 Mac? 常見三條路——GitHub 託管 macOS Runner、辦公室自購 Mac mini、雲端獨享實體機—— 沒有絕對優劣,差別在於你對排隊時間的容忍度、月建置頻率,以及是否有人專職維護系統與證書。 本文聚焦第三條路的技術落地,但會先幫你判斷另外兩條路的邊界在哪裡。

本次實測環境

硬體:Mac mini M4 · 10 核 CPU · 16 GB 統一記憶體 · 256 GB NVMe · 1 Gbps 獨享頻寬(ZovCloud 日本節點)。
系統:macOS 15 Sequoia,Xcode 16.4。樣例工程:SwiftUI 中型 App(約 11.8 萬行、含 3 個 Extension target)。
CI:GitHub Actions self-hosted runner 2.323.0;Jenkins 2.479 LTS + macOS agent。
簽名:Apple Distribution 證書 + App Store Connect API Key(Issuer ID + Key ID + .p8)。

拆開 CI 耗時:等待、編譯、簽名各佔多少

很多團隊抱怨「CI 太慢」,卻沒有把總耗時拆成三段: 排隊等機器本地編譯連結簽名與上傳。我們在同一 commit 上對比了 GitHub 託管 macos-14 Runner 與 ZovCloud M4 獨享節點,得到一組有參考價值的數字。

託管 Runner 在高峰時段(UTC 13:00–17:00)平均排隊 22 分鐘才開始執行 job; 實際 xcodebuild archive 耗時 5 分 38 秒。獨享 M4 節點無排隊, Archive 全量建置 3 分 58 秒——Swift 併發編譯在 10 核 M4 上比舊款 Intel CI 機快一截, 16 GB 統一記憶體在 clean build 時未觸發 swap。

3m 58s M4 節點 Archive 耗時
22m 託管 Runner 高峰排隊
1–5 min ZovCloud 節點開通
16 GB 統一記憶體(實測無 swap)
建置路徑 典型月成本 排隊 / 併發 更合適的場景
GitHub 託管 macOS Runner 按分鐘計費(約 $0.08/min 起) 共享池,高峰明顯排隊 月建置 < 500 分鐘、可接受等待
自購 Mac mini 放機房 硬體一次性 + 電費運維 獨佔,需自行維護系統升級 有固定辦公場所、全年高頻建置
雲端獨享 Mac(按天租用) ZovCloud $19.8/天起,無合約 實體機獨佔,付款後即時可用 中小團隊、發版周集中建置、遠端協作

若你的痛點主要是「push 之後半小時才知道編譯有沒有過」,瓶頸往往在排隊而非 Xcode 本身。 把 Runner 固定到一台隨時線上的獨享 Mac,是讓反饋迴路縮短的最直接手段—— 下文第三節起講怎麼把這臺機器準備成可復現的建置基線。

在雲端 Mac 上搭建可復現的 Xcode 建置基線

ZovCloud 開通的 Mac mini 已預裝完整 macOS 並授予管理員權限。開通後透過 SSH 或瀏覽器 VNC 登入, 建議按固定目錄規範存放證書與描述檔案,避免不同 Runner 指令碼各自找路徑。 以下四步是我們每次新節點初始化時的標準清單。

  1. 01
    安裝 Xcode 並接受許可

    從 App Store 安裝 Xcode 16.x,執行 sudo xcodebuild -license acceptxcodebuild -runFirstLaunch。驗證:xcodebuild -version 輸出預期版本號;xcode-select -p 指向 /Applications/Xcode.app/Contents/Developer

  2. 02
    匯入 Distribution 證書與描述檔案

    將 .p12 透過安全通道放到 ~/certs/,用 security import 寫入專用鑰匙串 ~/Library/Keychains/ci.keychain-db;.mobileprovision 放入 ~/Library/MobileDevice/Provisioning Profiles/。為 codesign 設定分割槽列表信任,避免無人值守建置時彈窗。

  3. 03
    配置 App Store Connect API Key

    在 Apple Developer 後台建立 API Key,將 AuthKey_XXXXXX.p8 存至 ~/private_keys/。上傳 TestFlight 時用 xcrun altool 或 Fastlane pilot upload,徹底繞開互動式 Apple ID 雙因素認證。

  4. 04
    首次全量 Archive 並保留 DerivedData

    克隆倉庫後本地跑通一次 Release Archive,確認簽名鏈路無報錯。256 GB 系統盤對中型專案足夠;保留 DerivedData 可使後續增量建置節省約 30–45% 時間。Swift Package 依賴多時可指定 -clonedSourcePackagesDirPath 快取目錄。

路徑環境變數建議

~/.zprofile 或 Runner 啟動腳本里統一匯出 KEYCHAIN_PATHP8_KEY_PATHDEVELOPER_DIR, 讓 GitHub Actions 與 Jenkins 共用同一套引用,減少「本地能編、CI 找不到證書」的來回排除。

GitHub Actions 自託管 Runner 註冊與 workflow 定向

自託管 Runner 註冊完成後,workflow 透過 label 把 job 精確派發到這臺雲端 Mac, 徹底繞開公共池。操作路徑:倉庫 Settings → Actions → Runners → New self-hosted runner, 選擇 macOS ARM64,按頁面提示下載 actions-runner 包後執行:

./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO --token RUNNER_TOKEN --labels macos-m4,zovcloud,ios-build --unattended

註冊成功後安裝為系統服務,確保節點重啟後 Runner 自動上線: sudo ./svc.sh installsudo ./svc.sh start。 workflow YAML 中指定 label:

runs-on: [self-hosted, macos-m4]

典型 iOS job 步驟鏈:檢出程式碼 → 解鎖 CI 鑰匙串 → xcodebuild archivexcodebuild -exportArchive → Fastlane upload_to_testflightaltool --upload-app。 我們在 M4 節點上從 push 觸發到 TestFlight 處理完成(含 Apple 側排隊)平均 約 10 分鐘, 其中本地建置與上傳僅佔 5–6 分鐘。

自託管 Runner 安全邊界

Runner 能訪問倉庫原始碼與簽名金鑰,務必限制 collaborator 權限、定期輪換 registration token, 避免在 fork 來的 PR 上自動觸發帶金鑰的 workflow。 多專案共用一台節點時,可為不同倉庫註冊不同 Runner,或配合 OpenClaw 沙箱限制 Agent 的檔案系統訪問範圍。

Jenkins Agent 掛載與流水線要點

若團隊已有 Jenkins 控制器(可執行在 Linux 上),macOS 建置能力透過 Agent 節點連線。 在雲端 Mac 上安裝 JDK 17,下載 agent.jar,以 LaunchDaemon 方式常駐, 控制器透過 SSH 或 JNLP 下發建置任務。

Jenkins 的優勢在於視覺化流水線與外掛生態:Credentials Binding 注入鑰匙串密碼、 AnsiColor 日誌著色、建置產物歸檔到 Artifactory 等。 典型 Pipeline 在 stage('Archive') 呼叫 sh 'xcodebuild ...', 在 stage('Upload') 呼叫 Fastlane。 與 GitHub Actions 相比,Jenkins 更適合多分支、多環境、需要人工審批閘門的企業內部流程。

雲端 Mac 按天租用可當作「彈性 Agent」:發版周開通節點掛載 Jenkins, 淡季釋放,不必為全年 365 天運維一台機房 Mac。 固定 DEVELOPER_DIR 避免多版本 Xcode 切換混亂,是 Jenkins 環境最容易被忽視的穩定性細節。

Archive → Export → TestFlight 命令列閉環

無論 GitHub Actions 還是 Jenkins,最終產物鏈路一致:Archive 生成 .xcarchive → Export 生成 .ipa → 上傳 App Store Connect。 命令列方式是 CI 的標準做法,不依賴 Xcode GUI。

Archive 示例(Release、指定 scheme):

xcodebuild archive -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -archivePath build/MyApp.xcarchive CODE_SIGN_STYLE=Manual PROVISIONING_PROFILE_SPECIFIER="MyApp AppStore"

Export 需配套 ExportOptions.plist(method 設為 app-store):

xcodebuild -exportArchive -archivePath build/MyApp.xcarchive -exportPath build/export -exportOptionsPlist ExportOptions.plist

上傳 TestFlight(API Key 方式,適合無人值守):

xcrun altool --upload-app -f build/export/MyApp.ipa -t ios --apiKey KEY_ID --apiIssuer ISSUER_ID

M4 的 10 核 CPU 使 Swift 併發編譯明顯快於舊款 Intel CI 機; 若工程含大量 Swift Package 依賴,建議在 workflow 裡快取 ~/Library/Developer/Xcode/DerivedData 與 SourcePackages 目錄, 第二次建置起耗時可再降三分之一左右。

無人值守環境下的鑰匙串與簽名排錯

SSH 或無頭 Runner 上 codesign 失敗,絕大多數情況與鑰匙串有關,而非證書本身過期。 建置指令碼開頭建議固定執行解鎖與授權:

security unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/ci.keychain-db

security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" ~/Library/Keychains/ci.keychain-db

密碼透過 GitHub Secrets 或 Jenkins Credentials 注入,切勿明文寫入倉庫。 若報錯 errSecInternalComponent,先檢查鑰匙串是否設為預設、codesign 是否被加入信任列表。 若 altool 上傳卡在認證,核對 API Key 的 Issuer ID 與 .p8 檔名是否匹配,以及 Key 是否仍有「Developer」權限。

另一個常見坑是 Provisioning Profile 與 Bundle ID 不一致——Archive 階段不報錯, Export 階段才失敗。建議在 CI 里加一步 security cms -D -i profile.mobileprovision 列印 UUID, 與 Xcode 工程裡的 PROVISIONING_PROFILE_SPECIFIER 交叉核對。

彈性 macOS 建置算力:何時把 Runner 放到雲端獨享節點

獨立開發者和小團隊常陷入兩難:需要 macOS 建置,卻不想為了一台 CI 機租辦公室、拉專線、處理斷電與系統升級。 公有云 Linux 虛擬機器無法滿足(沒有完整 macOS 與 Apple 簽名鏈); 把 Mac mini 放家裡又面臨上行頻寬不穩、IP 變動、誤關機等風險。

ZovCloud 提供獨享物理 Mac mini M4:不做虛擬化、不超售, 每台機器 16 GB 記憶體與 1 Gbps 獨享頻寬,付款後 1–5 分鐘自動開通。 五地節點——新加坡、日本、韓國、中國香港、美國東部——可按使用者分佈選擇; 面向日本市場的 App 可選東京節點,縮短上傳 TestFlight 時的跨境延遲。

計費按天 $19.8 起、按週 $53.5、按月 $99.1,無長期合約。 發版密集的一週開通節點掛載 Runner,日常維護期釋放,往往比全年自購機器加電費更划算。 需要多機並行 Archive 時,可選購 Thunderbolt 5 並聯服務組成 80 Gbps 叢集,適合大型 monorepo 或多 App 矩陣。

  1. 01
    在 ZovCloud 選擇節點並開通

    登入控制台選區域與租期,付款後 SSH 憑據與 VNC 入口自動下發。連線教學見幫助中心

  2. 02
    按第三節完成 Xcode 與簽名基線

    把鑰匙串路徑、API Key 路徑寫入環境變數,供 GitHub Actions / Jenkins 統一讀取。

  3. 03
    註冊 Runner 並跑通首次 pipeline

    先 Debug 建置驗證編譯,再切 Release Archive + TestFlight,把 Fastlane lane 固化進倉庫。

團隊形態 建議做法 雲端 Mac 角色
獨立開發者,月發版 1–2 次 發版日按天租用 + 手動 Archive 臨時建置機,用完即釋
5–15 人,每天多次 push 常駐 self-hosted Runner 按月租用,獨佔不排隊
已有 Jenkins,缺 macOS Agent 雲端 Mac 作彈性 Agent 峰值擴容,避免新購硬體
多 App 矩陣 + 夜間批次建置 TB5 叢集並聯 多台 M4 並行 Archive

iOS CI/CD 的門檻不在 Xcode 選單欄,而在穩定可預期的 macOS 算力 + 單一可信的簽名環境。 公共 Runner 適合低頻建置;自建機房適合有運維能力的成熟團隊; 雲端獨享 Mac 填補了「不想排隊、又不想買機器」的中間地帶。 把編譯搬到雲上之後,本地 MacBook 可以專心寫程式碼,而不是在深夜被 CI 任務搶走風扇與記憶體。

實體機獨享 · 1–5 分鐘開通

給你的 iOS 流水線一台不排隊的 macOS 建置機

ZovCloud Mac mini M4 獨享節點:完整 macOS 與 Xcode、16 GB 統一記憶體、 SSH / VNC 連線,可掛載 GitHub Actions 與 Jenkins Runner,按天 $19.8 起。

$19.8 / 天起
晶片Apple M4 · 38 TOPS
CPU10 核獨享
記憶體16 GB 統一
頻寬1 Gbps 獨享
SLA99.9%
開通1–5 分鐘