2026年獨佔遠端 Mac iOS/macOS CI:SwiftPM 的 Package.resolved 到底該不該提交?——可復現建置、快取預熱、CI 鎖版本漂移與 SSH 流水線決策矩陣 + FAQ
要把「依賴圖釘死」,還是讓解析器在 CI 上當場決定?本文以獨佔遠端 Mac 與 SSH 流水線的真實限制,拆解 Package.resolved、快取預熱與鎖漂移,並整理可寫進 Runbook 的決策矩陣與 FAQ。
1. 先把對象說清楚:你在鎖什麼?
Package.resolved 記錄的是 SwiftPM 解析後得到的精確版本與校驗資訊(含 Git revision 或 registry 版本等),用來復現同一張依賴圖。它與 Package.swift 裡寫的版本範圍不是同一件事:範圍是「允許解在哪一片」,resolved 是「這一次解在哪一點」。
在 Xcode 專案 + SPM 整合依賴的情境裡,常見路徑是專案目錄下的 xcworkspace/xcshareddata/swiftpm/Package.resolved(或等效位置);純 Swift Package 儲存庫則多在根目錄 Package.resolved。CI 腳本若只備份其一,會在「命令列能編過、Xcode 專案卻重新解依賴」這類問題上反覆踩坑。
2. 可復現建置:為什麼「應用類」儲存庫幾乎總是提交?
對可交付的 App/韌體/內部工具,業界共識是:提交 Package.resolved。如此 PR 與主幹、本機與 CI、本週與下週,都在同一張已解析的依賴圖上做 diff;出現「只發生在 CI」的迴歸時,你能先排除「解析器選了另一組相容版本」這一大類變因。
對對外發佈的 Swift Package 函式庫,常見做法是不提交 resolved(或僅把 resolved 當成本機/範例用途),以免把「函式庫的相容面」誤鎖成「某個消費端的一次解析結果」。若你維護的是函式庫,卻在獨佔 Runner 上跑「矩陣:多 Swift 工具鏈 × 多平台」,更應以 Package.swift 的語意與 CI 矩陣為主,而不是以單份 resolved 冒充相容承諾。
3. 快取預熱:提交 resolved 與「省下載」不是同義詞
獨佔遠端 Mac 的一大優勢是持久化磁碟:你可以在機器層級預熱 SourcePackages、Git 鏡像或自建 registry 代理。但快取命中依賴鍵(解析結果、工具鏈版本、registry 設定、甚至網路出口),而不是只靠「有沒有 resolved 檔」。
實務上:提交 resolved 減少的是解析不確定性;快取策略減少的是重複 fetch/checkout。兩者疊加最好——在 SSH 流水線上用固定的 -clonedSourcePackagesDirPath 或團隊約定的 DerivedData/SourcePackages 路徑,並在 Runbook 寫明何時允許清空快取(大版本 Xcode 升級、registry 遷移、資安事件)。若要一併收斂「git 拉取策略與 checkout 失敗分診」,可對照 獨佔遠端 Mac CI:Git blobless/淺克隆與 SSH checkout 的決策矩陣 + FAQ,把儲存庫端與依賴端放在同一套敘事裡。
4. CI「鎖版本漂移」:即使提交了也會動?
會。典型來源包括:Xcode/Swift 工具鏈升級、Package 使用版本範圍且上游打了新 tag(若你在 CI 上執行會改寫 resolved 的指令)、鏡像/registry 中繼資料變更,以及多根專案/多 workspace 下解析入口不一致。獨佔 Runner 若長期不升級工具鏈,會把問題延後成「一次性大爆炸」;託管機則相反,漂移更頻繁但更平均。
緩解策略:把工具鏈版本寫進驗收清單;對應用儲存庫在 CI 上校驗「resolved 是否被意外改寫」;對函式庫儲存庫用矩陣涵蓋解析邊界。並行多個 job 時,也要留意 DerivedData/Simulator 與解析目錄的爭用;可延伸閱讀 Xcode 測試計畫與並行測試在自託管 Runner 上的資源爭用與 SSH 無頭 flaky FAQ,把「鎖依賴」與「鎖執行緒/IO 預算」一起寫進 Runbook。
5. 決策矩陣(獨佔遠端 Mac + SSH 流水線)
| 情境 | Package.resolved | 理由摘要 |
|---|---|---|
| iOS/macOS 應用主儲存庫 | 提交 | 強可復現;便於 PR 審查依賴變更 |
| 可複用 SwiftPM 函式庫(對外) | 通常忽略 | 避免誤代表相容面;以矩陣驗證範圍 |
| 單倉多 App/多 workspace | 每入口各一份並都提交 | 防止「CLI 與 Xcode 各解各的」 |
| Fork/修補依賴 + 頻繁 rebase | 提交 + 嚴格流程 | 衝突在 resolved 上可見;需約定合併策略 |
| 資安稽核要求「可釘死供應鏈」 | 提交 + 校驗 | 與 SBOM/allowlist 聯動;CI 防竄改 |
6. SSH 獨佔流水線上的硬建議
- 顯式解析步驟:在
xcodebuild前呼叫-resolvePackageDependencies(或團隊封裝的等價物),失敗時日誌要落盤。 - 非互動:SSH 工作階段無鑰匙圈彈窗;憑證走環境或預置鑰匙圈,避免首次解析卡住。
- 併發 jobs:同一使用者下並行建置時,避免多個 job 爭用同一 DerivedData/SourcePackages 路徑——這是 flaky 好發區。
- 升級日:Xcode 大版本切換後安排一次「冷啟動」建置驗收,再恢復快取。
若你仍在搭「從筆電 SSH 到第一台遠端 Mac」的最小路徑,請先在 Runbook 固定工作階段、工作目錄、非互動憑證三件事,再把同樣的紀律遷移到 CI(未必安裝同一套軟體,但原則一致)。
7. FAQ
CI 報錯「package resolution failed」優先看什麼?
工具鏈版本、網路/registry、Git 憑證,以及 Package.swift 與 resolved 是否指向同一解析入口(Xcode 專案 vs 套件根目錄)。
resolved 合併衝突怎麼解?
不要手改 JSON:在主幹合併後於本機或 Runner 上執行一次官方解析流程,產生新的 resolved 再提交;衝突本身代表依賴圖同時在變,應審查而不是「選一邊」。
不提交 resolved 會更快嗎?
偶爾省掉合併煩惱,但換來的是難以解釋的 CI 漂移;在獨佔長跑 Runner 上,這種漂移更難用「換一台乾淨機器」自證。
在 Mac mini 上把「鎖依賴」做成預設正確
依賴解析與編譯同樣是 CPU、磁碟與 I/O 的混合負載:Apple Silicon 的統一記憶體與頻寬,讓大型 SPM 圖在解析與增量編譯時較不容易掉速;macOS 作為官方工具鏈的一等公民,也少了跨平台模擬層帶來的額外變因。對需要長期無人值守的獨佔 CI 而言,macOS 的穩定性與極低當機率,意味著更少的「半夜 job 紅燈」。
若你希望把本文的快取策略、SSH 非互動解析與多 job 隔離真正跑穩,一台靜音、待機功耗低且能 7×24 放在機房的 Mac mini M4,往往比臨時拼湊的虛擬機池更省心——總持有成本也更容易算清。若你正在物色這樣一台「解析與建置都可靠」的主力節點,現在就是把它納入團隊基礎建設的好時機:前往 SSHMac 首頁瞭解方案。