症狀:Linux 上的 Bazel 指令可以啟動,但 iOS Archive、Simulator 測試或簽名到了最後一步仍然失敗。
最快解法:Bazel 9 iOS 建置不能讓完整交付鏈路脫離 Mac;本週先把 Apple SDK、Xcode、Simulator、簽名與打包 Action 固定到真實 macOS,平台無關工作留在 Linux,再用混合 CI 驗收。

最後更新於 2026 年 8 月 29 日;版本與相容性資料核實自 Bazel 發布模型Bazel 9 官方公告rules_apple ReleasesApple Xcode 26 Release Notes

這篇適合正在把 Bazel 9 接入 iOS 專案、需要在 Linux 與遠端 Mac 之間分配 CI 工作的建置工程師。
維護 Linux CI 集群、準備擴充 Apple 平台建置能力,或正在比較單一 Mac 節點與混合架構的技術負責人,也能用本文的指標完成評估。

01

先定義工具鏈邊界

Bazel 是建置分析、Action 調度與快取的核心;rules_apple 負責 Apple 平台規則,rules_swift 處理 Swift 建置整合,而 Xcode 提供 Apple SDK、編譯器、連結工具、Simulator 與簽名相關工具。這幾層不是同一件事,Bazel 指令能執行,也不等於 iOS 應用能交付。

Bazel 9、rules_applerules_swift 與 Xcode 26 的實際組合,必須依寫作當日的官方 Releases、README 與 Apple 發布說明逐項確認;不能把某一個規則版本標示「支援 Bazel 9」解讀成所有 Xcode 26 小版本都已通過驗證。Apple 對平台建置要求的變更,也應直接對照官方平台建置要求

Bazel 9 公告已說明其發布與支援方向,舊式 WORKSPACE 依賴入口在升級後需要另外驗證。若專案仍依賴隱含的外部 Repository、未鎖定的規則版本或本機腳本,升級成功可能只是分析階段成功,並不代表乾淨環境可以完成產物。

判斷結果可以分成三種:

  • 只做程式碼分析、產生通用檔案或部分平台無關測試:可先留在 Linux。
  • 需要 Apple SDK、Xcode、Simulator 或 Apple 平台連結:應配置 macOS 節點。
  • 需要 Archive、簽名、Provisioning Profile 或交付產物匯出:必須在具備相符 Apple 工具鏈的 macOS 節點完成。
02

Action 路由與平台證據

「建置腳本」不是可靠的路由單位。相同的 CI 工作可能先在 Linux 產生檔案,再由 macOS Action 完成連結;也可能因工具鏈解析、平台約束或環境變數差異,在錯誤節點上執行。工程師應查看 Bazel 執行日誌,而不是按工作名稱猜測。

可先在非生產工作中保留完整日誌:

bazel build //App:Release \
  --subcommands \
  --verbose_failures \
  --toolchain_resolution_debug

接著從輸出檢查:

Apple toolchain: <XCODE_TOOLCHAIN_PLACEHOLDER>
Execution platform: <MACOS_PLATFORM_PLACEHOLDER>
Action owner: //App:<TARGET_PLACEHOLDER>

上面的值只是應由專案填入的佔位符,不是固定名稱。重點是確認每個關鍵 Action 的 execution platform、工具鏈與實際執行端點。Bazel 的平台與工具鏈參考可用來核對約束解析結果;遠端執行規則文件則應用於確認遠端執行服務如何處理平台與環境要求。

以下工作通常可優先放在 Linux,但仍須以專案依賴為準:

  • Swift 或 Objective-C 原始碼的靜態檢查;
  • 不依賴 Apple SDK 的程式碼產生;
  • 純邏輯單元測試;
  • 依賴圖檢查、格式檢查與一般 CI 品質閘門;
  • 不需要 Apple 平台工具的打包前準備工作。

以下工作不能只因為由 Bazel 啟動,就視為 Linux 可完成:

  • Apple 平台編譯與連結;
  • 使用 Xcode SDK 的建置;
  • iOS Simulator 測試;
  • Archive、程式碼簽名與 Provisioning Profile 驗證;
  • IPA 或其他正式交付產物的匯出。

提醒: 遠端快取、遠端執行與遠端 Mac 登入是三個不同層次。快取只重用已產生的結果,遠端執行是把 Action 交給其他執行器,VNC 或 SSH 登入則只是管理工作階段;啟用其中一項,不能推論另外兩項已經成立。

03

三種 CI 架構的取捨

中型以上團隊通常不應把所有工作都塞進一台 Mac,也不應把完整 iOS 交付誤放進 Linux 集群。較穩妥的做法,是先按 Action 能力分流,再觀察快取、佇列、I/O、重試與節點恢復資料。

架構方案 適合的工作 主要優點 主要風險 建議決策條件
單一遠端 Mac 小團隊、單一 iOS 產品、需要先跑通閉環 工具鏈集中,簽名與測試邊界較容易追蹤 Mac 節點故障或佇列會影響全部 Apple 工作 先以最小專案完成建置、測試、Archive、簽名與重啟恢復
Linux 加遠端 Mac 混合架構 中大型專案、已有 Linux CI、Apple Action 佔比不固定 通用工作沿用 Linux,Apple 工作才使用 Mac 路由、工具鏈標記、快取隔離與權限較複雜 Action 平台已可觀測,且能明確區分 Linux 與 macOS 失敗
Linux 加遠端快取及多個 Mac 執行器 建置量波動、需要擴容或多版本 Xcode 可分離分析、快取與 Apple 執行能力 快取污染、版本不一致、節點容量與安全管理 已記錄命中率、等待時間、失敗重試及版本組合後再擴容

這裡的「快」不能用未經實測的提速百分比代替。每次比較應記錄快取命中與未命中、Mac 佇列等待、分析與連結階段、測試耗時、I/O 錯誤及失敗重試。若 Linux 很快但 Mac 佇列長,瓶頸是 Apple 執行器容量;若 Mac 空閒而連結反覆失敗,問題則更接近工具鏈或輸入未密封。

NodeMini 的遠端 Mac 方案可作為隔離節點的環境入口,但架構是否適合,仍應以專案 Action 與驗收結果決定,而不是只看節點數量。

04

可重現性與依賴密封

Bazel iOS 建置的穩定性,不能只看一次成功的 CI 綠燈。至少要把以下組合寫入可追蹤的建置資訊:

  • MODULE.bazel 中的 Bazel module 與外部依賴;
  • rules_applerules_swift 的精確版本;
  • Bazel 9 的小版本;
  • macOS、Xcode 26 與 SDK 版本;
  • Apple Silicon 或其他執行平台標籤;
  • 簽名身份、Provisioning Profile 與鑰匙圈的引用方式。

rules_apple 應以官方 Releases 頁面核對規則版本;Swift 規則則參考官方 rules_swift 文件。這些來源能協助確認支援範圍,但不能取代團隊對自身專案、Xcode 小版本與第三方依賴的實際驗證。

應特別排查以下非密封依賴:

  • PATH 中只有某台主機才有的工具;
  • 透過絕對路徑讀取的宿主機檔案;
  • 未在規則中宣告的命令列工具;
  • 使用工作站暫存目錄、使用者家目錄或本機腳本;
  • 簽名工具讀取了未被 CI 管理的鑰匙圈;
  • 依賴上一次建置殘留檔案而沒有宣告輸入。

驗證時先做乾淨複製,再清除本地與遠端快取,分別在指定的 Linux 與 macOS 平台執行。兩次結果應比較 Action 圖、警告、產物雜湊、測試結果與失敗位置,而不只是比較終端機最後一行是否顯示成功。

05

簽名、測試與交付證據

Simulator 測試通過,只能證明某一類測試在指定模擬器環境下通過;無簽名編譯成功,也不能證明 Archive、Provisioning Profile 或正式匯出可用。Apple 對簽名身份與 Provisioning Profile 的關係,可參考程式碼簽名技術說明

可將交付驗收拆成以下步驟:

  1. 固定 Bazel 9、規則、macOS、Xcode 26 與 SDK 的版本組合,並把版本輸出存入 CI Artifact。
  2. 以乾淨複製執行分析與建置,使用 Action 日誌確認 Linux 與 macOS 的實際分工。
  3. 在 macOS 節點執行 Simulator 測試,保存測試報告、測試目標與失敗重試資訊。
  4. 在無人值守工作階段產生 Archive,檢查產物內容、建置設定與 Xcode 工具鏈。
  5. 將證書、Provisioning Profile、鑰匙圈與 Bazel 快取分開管理,避免快取攜帶不應重用的簽名資料。
  6. 驗證匯出產物的簽名資訊與 Bundle ID,確認它對應正確的簽名身份,而不是只檢查檔案存在。
  7. 中斷 SSH、重啟節點、切換工具鏈並替換執行器,確認工作可由 CI 自動恢復,不依賴人工登入圖形化桌面。

可用下列形式保存關鍵證據:

xcodebuild -version
xcode-select -p
bazel info release
bazel test //App:<TEST_TARGET_PLACEHOLDER> --test_output=errors

輸出中的版本、路徑與目標名稱應由實際環境產生;不要把某一次主機的結果硬編碼到所有節點。若簽名資料只在互動式使用者工作階段可用,該節點就尚未達到無人值守交付標準。

經驗判斷: 節點上线的證據應包括可重現建置、可檢查測試結果、可驗證 Archive、簽名資訊與失敗恢復記錄。單次成功的 bazel build 不足以支撐生產 CI 架構決策。

06

遠端 Mac 節點驗收與擴容

小團隊可以先用單一遠端 Mac 跑通完整閉環:從乾淨複製開始,經過 Bazel 分析、Apple 平台建置、Simulator 測試、Archive、簽名、匯出與重啟恢復。這樣能先揭露工具鏈缺口,不必在問題尚未定位前建立複雜集群。

已有 Linux CI 集群的團隊,應採用 Linux 加遠端 Mac 的混合架構。路由規則需要把 Apple 平台能力、Xcode 版本與簽名權限視為明確的節點屬性;Linux 不應接收它無法滿足的 Apple Action,Mac 也不應被通用檢查工作長期佔滿。

擴容觸發條件應來自觀察資料,而非預設的節點比例。可在連續一段實際工作負載中記錄:

  • Mac 佇列等待是否成為流水線總等待的主要部分;
  • Apple Action 是否因節點不足而重試或逾時;
  • 遠端快取命中是否因 Xcode 或 SDK 不一致而失效;
  • 重啟或替換節點後,是否仍需人工處理;
  • 失敗主要來自 CPU、I/O、路由、簽名權限或工具鏈差異。

若所有 Apple Action 都能被正確路由,乾淨建置可重現,簽名與重啟恢復也有記錄,才適合增加 Mac 執行器或導入更完整的遠端執行方案。若規則版本與 Xcode 26 小版本尚未形成可驗證組合,應暫緩遷移或先鎖定版本,避免以擴容掩蓋相容性問題。

07

最終架構判斷

Bazel 9 的價值在於統一分析、工具鏈選擇、Action 調度與快取策略;它不能消除 Apple 對 macOS、Xcode、SDK、Simulator 和簽名工具的執行平台要求。對完整 iOS 交付而言,Mac 仍是必要的 Apple 平台節點。

如果目前方案是全 Linux CI,限制在於無法自然承接 Apple SDK、Simulator、Archive 和簽名;如果依賴虛擬化或臨時工作站,限制通常會落在工具鏈漂移、圖形登入依賴、節點恢復與簽名資料管理;如果只有一台本地 Mac,則容易受硬體可用性、佇列與團隊共享造成的排程影響。

因此,較實際的下一步,是先以一台可隔離的 NodeMini 遠端 Mac 執行真實 Bazel iOS 專案,記錄 Action 路由、快取、Simulator、簽名、Archive 與重啟恢復,再依驗收證據決定長期租用規模或 Linux 加 Mac 的節點數量。若團隊還在評估節點所在位置,也可參考香港遠端 Mac 算力方案比較連線與部署條件;但長期穩定、高密度的生產負載,仍應先以實測佇列與恢復資料判斷是否需要自購硬體或建立多節點架構。