SwiftPM 私有依賴:2026 年遠端 Mac 配置指南

獲勝者是「專用倉庫憑據+固定 Package.resolved+實際構建使用者的 SSH 設定」;只要遠端 Mac 會執行 xcodebuild、定時任務或自動歸檔,就應先單獨驗證依賴解析,再接入完整構建,不能把個人 Xcode 的登入狀態當成 CI 設定。
這篇文章適合三類讀者:正把本地 Mac 遷移到遠端構建環境、專案含有私有 Swift 包的獨立開發者;使用命令列或定時任務執行 iOS 構建的人員;以及需要管理多個私有倉庫、重視權限隔離與密鑰輪換的小團隊。
先分清楚:開發者憑據,還是構建憑據
常見失敗案例是:Xcode 圖形介面可以正常解析並構建,但同一台遠端 Mac 的背景任務卻無法拉取私有包。原因通常不在 Swift 程式碼,而在兩個會話使用了不同的 macOS 使用者、SSH 設定或 Keychain 狀態。
遷移前先把專案依賴畫出來:
- 列出專案直接引用的私有 Swift 包。
- 展開傳遞依賴,確認每個私有倉庫的實際來源。
- 記錄目前可成功構建的
Package.resolved與提交版本。 - 確認每個倉庫只需要讀取權限,還是確實需要寫入權限。
- 將個人開發帳戶與遠端構建專用帳戶分開。
Apple 的 Swift Package 持續整合官方說明 明確把憑據提供與依賴鎖定視為 CI 環節的一部分。這代表「在本地可以拉到」並不等於「在背景任務可以拉到」。
| 判斷維度 | 個人開發憑據 | 遠端構建專用憑據 |
|---|---|---|
| 使用者 | 開發者本人 | 實際執行構建的 macOS 使用者 |
| 權限 | 可能包含多個專案與寫入權限 | 只讀取指定私有倉庫 |
| 使用位置 | 本地 Xcode、互動式終端機 | 遠端 Mac 的命令列與自動任務 |
| 撤銷方式 | 撤銷可能影響日常開發 | 可單獨停用,不牽連個人工作 |
| 風險 | 容易被複製到腳本或共享主機 | 範圍較小,便於輪換與稽核 |
私人包的宣告仍應以專案的 Package.swift 和 Apple 的 Package.Dependency 官方文件 為準。這一步不是 SwiftPM 入門,而是為後續授權測試建立清單。
兩條配置路徑:圖形登入不等於 SSH 可用
Swift Package 私有倉庫如何配置認證,關鍵不在於先開啟 Xcode,而在於先確認「誰」會執行讀取動作。假設自動任務由構建使用者執行,SSH 私鑰、主機公鑰和 known_hosts 都必須在該使用者可讀取的環境中完成配置。
| 配置項目 | 建議做法 | 不建議做法 |
|---|---|---|
| 私鑰 | 為遠端構建建立專用 SSH 密鑰 | 直接複製開發者日常私鑰 |
| 公鑰 | 加到指定私有倉庫的唯讀授權位置 | 授予整個帳戶的管理權限 |
| 主機校驗 | 預先建立並審查 known_hosts |
以關閉主機校驗掩蓋錯誤 |
| 執行使用者 | 使用後台任務實際使用的 macOS 帳戶 | 只在管理員或個人帳戶測試 |
| 密鑰保存 | 由 SSH agent 或受控 Keychain 管理 | 寫入程式碼、腳本或終端機歷史 |
若倉庫使用 SSH Git URL,先依照 GitHub 官方 SSH 密鑰與 agent 說明 建立專用密鑰,再由倉庫端加入公鑰。文件中的 ssh-keygen 與 ssh-add 是認證配置的兩個關鍵參數,私鑰內容本身不應出現在文章、腳本或共享筆記中。
接著使用同一個構建使用者測試倉庫連線。可參考 官方 SSH 連線測試方法;若測試主機名稱、帳戶或指紋不符,應在這裡停止,不要直接進入 Xcode 構建。
首次解析與 Package.resolved:固定版本,先於編譯
Package.resolved 是否應該提交到倉庫?對需要可重現 CI 的專案,應將它納入版本控制,並在每次有意更新私有包時一併審查。Apple 的 SwiftPM 持續整合文件 將提交解析檔列為 CI 的重要做法;遠端環境不應在沒有審查的情況下自行選擇新版本。
第一輪不要直接執行完整 Archive。建議按照以下時間線操作:
- 建立乾淨工作目錄:使用與後續任務相同的專案路徑和構建使用者,避免把個人快取當成成功條件。
- 確認檔案位置:檢查
Package.resolved是否位於專案實際使用的位置,並確認檔案已提交。 - 單獨解析依賴:先使用
xcodebuild -resolvePackageDependencies。這個參數只針對依賴解析,能把認證、主機校驗和版本衝突分開。 - 觀察解析結果:確認私有包來源、版本與提交資訊符合基線。不要只看命令是否結束。
- 解析失敗即停:認證失敗先回到 SSH;主機校驗失敗先檢查
known_hosts;版本失敗才檢查Package.resolved與依賴宣告。
xcodebuild -resolvePackageDependencies 的用途與命令列構建邏輯,可對照 Apple TN2339 命令列構建技術說明。若依賴解析階段已經失敗,繼續編譯只會製造更難分辨的錯誤訊息。
| 解析結果 | 可觀察證據 | 下一步 |
|---|---|---|
| 認證成功 | 私有來源可讀取,解析檔未被意外改寫 | 進入編譯測試 |
| 主機校驗失敗 | 出現未知或不匹配的主機指紋 | 停止,審查 known_hosts |
| 版本解析失敗 | 解析檔與依賴宣告不一致 | 審查提交與版本規則 |
| 找不到私有包 | URL、權限或 SSH 設定不符 | 回到倉庫授權測試 |
| 只在 Xcode 成功 | 圖形會話帶有額外登入狀態 | 改用構建使用者重測 |
xcodebuild 為什麼無法拉取私有依賴
xcodebuild 無法拉取私有依賴,通常是因為它執行在另一個使用者、另一個環境變數集合,或另一個工作目錄。圖形介面曾經成功登入,只能證明那個互動式會話具備存取條件。
實際命令應與後續自動任務一致。不要在終端機中臨時載入密鑰後,卻用另一套定時任務設定作結論。最低限度要核對:
- 構建使用者是否與 SSH 測試使用者相同。
- SSH Git URL 是否與專案依賴來源一致。
known_hosts是否由該使用者可讀取。Package.resolved是否已提交且沒有被解析過程意外更新。- 是否使用了不同的
HOME、工作目錄或 SSH agent。 - 代理、URL 映射或系統 Git 設定是否只存在於互動式 Shell。
若專案確實需要系統 Git 的代理、URL 映射或進階 SSH 設定,再評估 xcodebuild 的 SCM 選項;不要先加入一長串參數。Apple 的 xcodebuild 源程式碼管理說明 應作為參數行為的核對來源;若頁面路徑或適用版本有所變化,應以寫作時的 Apple 官方文件為準。
實務上,失敗日誌至少要能回答三件事:使用哪個 macOS 使用者、在哪個工作目錄執行、失敗發生於認證還是版本解析。日誌可以保留錯誤類型與命令返回結果,但不得輸出私鑰、存取令牌或完整敏感環境變數。
從解析到 Archive:以同一個會話完成驗收
完成解析後,才進入第一次命令列構建。此時的目標不是追求最快,而是證明圖形介面以外的環境也能完成整條路徑。
建議按以下順序驗收:
- 先構建指定 Scheme:使用與後續自動任務相同的 Scheme、目的地和工作目錄。
- 核對私有包目標:確認私有包被解析、編譯,且其產品確實被主程式連結。
- 產生 Archive:使用
xcodebuild archive與明確的-archivePath,避免只得到散落的構建產物。 - 檢查歸檔內容:確認 Archive 包含預期 App、版本資訊和簽名所需內容。
- 再處理匯出:若流程需要 IPA 或其他發佈產物,使用
-exportArchive與受控的-exportOptionsPlist。 - 保留失敗脈絡:記錄命令、使用者、工作目錄、解析結果與歸檔結果,讓下一次能重現。
-archivePath、-exportArchive 和 -exportOptionsPlist 都是命令列歸檔流程中的具體參數,使用方式可對照 Apple 官方歸檔與匯出文件。這些參數不是固定填充值;路徑、Scheme 和匯出設定必須取自實際專案,不能照抄範例中的假路徑。
私有包讀取憑據也不應自動擁有發佈權限。程式碼簽名憑據、App Store Connect 存取權與倉庫 SSH 密鑰應分開管理。需要進一步處理構建憑據的讀者,可先參考 ProxyMac 的使用說明,再按專案權限模型配置。
無人值守構建:SSH agent、Keychain 與環境變數分開測
SwiftPM 私有依賴如何用於無人值守構建,答案不是「讓 VNC 視窗一直開著」,而是驗證 SSH agent、Keychain 和環境變數在沒有圖形會話時仍然成立。
交給定時任務前,先完成三種會話測試:
- SSH 連線保持期間執行一次解析。
- 斷開 SSH 連線後,由背景任務重新執行解析。
- 不開啟 VNC 或 Xcode 登入狀態,直接執行構建與 Archive。
若 SSH 密鑰帶有口令,應確認 agent 的生命週期與載入方式。可參考 GitHub 官方 SSH 口令與 agent 文件。不能把口令硬編碼進腳本,也不能為了讓任務「成功」而永久關閉主機校驗。
失敗時要有明確停止條件:
- 密鑰過期或被撤銷:停止解析,通知輪換。
known_hosts不匹配:停止連線,人工核對主機身分。- 私有倉庫被移除:停止構建,不自動改用公開鏡像。
- 構建使用者變更:重新配置並完成完整驗收。
- Xcode 或 SwiftPM 更新:重新執行乾淨解析與 Archive。
這種隔離也能避免倉庫讀取權限意外變成發布權限。對小團隊而言,構建任務的成功不應以犧牲密鑰邊界換取。
重啟與依賴更新後的回歸清單
遠端 Mac 重啟後 SSH 密鑰失效怎麼辦?先判斷是密鑰沒有載入、agent 沒有在背景會話存活,還是構建使用者讀不到 Keychain。不要直接重新產生密鑰,更不要把私鑰複製到另一個帳戶。
重啟後應從乾淨工作目錄重跑:
- 以構建使用者執行 SSH 倉庫存取測試。
- 單獨執行依賴解析,核對
Package.resolved。 - 執行實際 Scheme 的命令列構建。
- 產生 Archive,檢查歸檔產物。
- 以無 VNC、無互動式登入的方式重做一次。
- 將結果與重啟前的日誌比較。
私有包升級也要遵循同一邏輯:先在可審查的分支更新解析結果,確認 Package.resolved 的變更,再讓遠端 Mac 執行構建。不要讓背景任務自行漂移到新版本。
若專案之後要接入長期 iOS 打包流程,建議先在 ProxyMac 的遠端使用支援頁面確認連線與帳戶操作,再安排冷啟動驗收。需要比較不同租用週期時,可查看 ProxyMac 的方案頁面;選擇前仍應以實際構建時數、是否需要常駐和團隊權限要求為準。
本地臨時工作站若把個人 Xcode 登入、私有倉庫密鑰和簽名憑據混在一起,常見缺點是權限難以收回、重啟後狀態不透明,還會讓背景任務依賴某個人的互動式會話。改用一般雲端編譯環境,則可能受限於自訂 SSH、Keychain 或完整 macOS 工具鏈的控制範圍。對需要真實 macOS、完整 root 權限與可自行驗收的團隊,租用 ProxyMac 的遠端 Mac 會比臨時拼接這些環節更容易維持一致;但若工作負載長期滿載或必須直接連接實體裝置,自購 Mac 仍可能更合適。
較穩妥的做法,是先用短週期遠端 Mac 讓真實專案完成冷啟動解析、Archive 與重啟復驗。只有在脫離圖形會話後仍能穩定讀取私有依賴,才把該主機接入長期無人值守的 iOS 打包任務。