個人 Mac 可以完成歸檔,但遠端節點找不到對應私鑰,通常代表簽名資產尚未完成遷移。
最快解法:新專案可直接採用 fastlane match;已有生產簽名則先導入現有身份並保留舊鏈路雙軌驗證,CI 預設使用臨時鑰匙串與 readonly,同時不要把 match nuke 當成預設步驟。
這篇文章適合以下讀者:
- 維護 iOS 或 macOS 自動簽名流水線、希望消除個人 Mac 依賴的 DevOps 工程師。
- 負責證書、私鑰、描述檔與 App Store 發布權限的簽名管理員。
- 準備租用遠端 Mac 作為長期建置或發布節點的研發負責人。
先確認簽名資產邊界
fastlane match 遠端 Mac 的問題,並不是把 .cer 或描述檔下載到節點就算完成。Apple 的代碼簽名身份需要證書及其對應私鑰;Apple 也在團隊簽名憑證說明中將兩者視為必須一起同步的資產。可先閱讀 Apple 關於團隊簽名憑證同步的官方說明。
因此,遷移前應建立資產盤點表:
- 應用程式的 Bundle Identifier:
<APP_BUNDLE_ID>。 - Apple 團隊識別碼:
<TEAM_ID>。 - 證書類型:開發、Ad Hoc、App Store,或 macOS 發布用途。
- 對應私鑰是否仍存在於目前發布用 Mac 的 Keychain。
- 每個 Target 使用的描述檔,包括 Extension、Widget、Watch App 或其他附屬 Target。
- 現有生產發布鏈路、負責人、備份位置與回退入口。
Xcode 只在主應用成功歸檔,不能證明所有附屬 Target 都能簽名。若專案包含 macOS App,還應另外核對 Apple 的 macOS 分發簽名流程;iOS 或 iPadOS 應用則要對照 Apple 的註冊裝置分發流程。
新專案初始化
全新專案沒有需要保護的現有生產身份時,可以直接初始化 match。初始化前,先決定簽名倉庫由誰持有、加密口令由誰管理,以及 Apple 團隊權限由哪一個受控角色操作。不要把倉庫存取憑據、match 解密口令與發布令牌放在同一個秘密變數中。
設定檔只使用占位符,不要把真實帳號資料提交到程式碼庫:
# Appfile
app_identifier("<APP_BUNDLE_ID>")
team_id("<TEAM_ID>")
Appfile 的團隊與應用程式設定方式,可對照 fastlane Appfile 官方文件。
接著依照實際發布用途建立簽名資產:
- 開發建置:供開發裝置或內部測試使用。
- Ad Hoc:供註冊裝置測試使用。
- App Store:供商店發布或相應的分發流程使用。
- macOS 發布:依 macOS Target、分發方式與專案設定獨立核對。
初始化完成後,第一個驗收目標不是「證書檔案已下載」,而是全新的遠端 Mac 能完成最小簽名建置。這個基線至少應包括:同步簽名資產、Xcode 解析正確的 Target、產生歸檔,以及在指定測試流程中驗證安裝或分發結果。
既有簽名遷移
已有生產簽名時,先使用 match import 將現有證書、私鑰與描述檔納入受控流程,並在隔離的簽名倉庫或分支試跑。fastlane 官方文件確認 match 支援匯入既有簽名資產,相關做法可參考 match 的匯入說明。
建議按以下順序執行:
第一步:保留現有發布能力。
先確認目前個人 Mac 或既有 CI 仍能使用同一提交完成歸檔,保存歸檔檔案、簽名檢查結果與發布紀錄。不要在尚未確認遠端節點可用前撤銷證書。
第二步:建立隔離試跑。
使用 <SIGNING_REPO_URL>、<MATCH_BRANCH>、<APP_BUNDLE_ID> 等占位符配置,讓測試分支只服務非生產建置。這樣可以檢查解密口令、倉庫權限、私鑰匯入與描述檔匹配,而不影響正式發布。
第三步:比較同一提交。
讓舊鏈路與遠端 Mac 使用同一個 Git commit,分別檢查歸檔是否成功、簽名身份是否一致、安裝或分發結果是否一致。單次建置成功不足以證明遷移完成,因為它可能只覆蓋主應用,沒有覆蓋 Extension 或其他 Target。
第四步:設計回退入口。
回退入口至少應包含舊鏈路、簽名倉庫的可恢復版本、加密口令保管責任與節點失效時的替代建置位置。只有完成影響評估、備份與回復演練後,才討論是否重置簽名資產。
match nuke、證書撤銷或刪除鑰匙串都可能使仍在使用的發布流程失效,因此不應用來「清理環境」或掩蓋配置問題。若確實需要破壞性操作,必須先列出會受影響的 App、Target、發布渠道與回退步驟。
多應用與多團隊隔離
同一個簽名儲存空間可以在受控範圍內重用相應證書,但每個應用程式的描述檔、Bundle Identifier 與附屬 Target 仍須分別維護。不同 Apple 團隊不應只靠命名規則區分,應使用獨立分支、獨立儲存空間或至少獨立的存取權限。
例如:
<TEAM_ID_A>/<APP_BUNDLE_ID_A>
<TEAM_ID_A>/<APP_BUNDLE_ID_A_EXTENSION>
<TEAM_ID_B>/<APP_BUNDLE_ID_B>
多應用配置需要特別檢查:
- 主應用與 Extension 是否使用正確的
<TEAM_ID>。 - Widget、Watch App 的 Bundle Identifier 是否被納入同步範圍。
- 不同團隊的倉庫密鑰、解密口令與發布令牌是否分離。
- CI 工作區是否會重用上一個 Job 留下的描述檔或 Keychain 狀態。
- App Store Connect 或其他發布服務的認證是否與簽名倉庫權限分開。
多應用和多團隊如何隔離 match 證書倉庫?
若應用屬於不同 Apple 團隊,優先選擇獨立儲存空間與獨立權限;若同一團隊管理多個應用,則可按應用與附屬 Target 分別維護描述檔,並用分支或路徑規範降低誤取風險。Appfile 的團隊設定不可只留在人工記憶中,應放進可審查的 CI 配置,但真正的口令與令牌必須由秘密管理機制注入。
無人值守 CI 配置
遠端 Mac CI 的核心不是讓登入畫面保持開啟,而是讓非互動任務可以在沒有圖形確認、沒有人工輸入與沒有個人 Keychain 依賴的情況下完成簽名。
setup_ci 的用途是建立 CI 適用的臨時鑰匙串,並配合簽名同步流程降低對登入使用者環境的依賴;參數與行為應以 setup_ci 官方文件 為準。
遠端 Mac CI 為什麼要使用臨時鑰匙串?
macOS Keychain 若沿用共享節點的登入鑰匙串,容易出現權限提示、使用者切換後無法存取、前一個 Job 殘留身份,或普通任務讀取到生產私鑰等問題。臨時鑰匙串讓每次 CI 執行有明確的建立、使用與清理邊界;它不是取代簽名倉庫,而是限制私鑰在建置期間的暴露範圍。
在 CI 中,簽名同步應先於 Xcode 建置與歸檔:
setup_ci
match(
type: "<MATCH_TYPE>",
readonly: true,
app_identifier: [
"<APP_BUNDLE_ID>",
"<APP_EXTENSION_BUNDLE_ID>"
]
)
fastlane match readonly 應該在哪些任務中開啟?
所有一般建置、測試歸檔、Pull Request 驗證與正式發布 Job,都應讓 match 以 readonly 模式讀取已批准的簽名資產。寫入或續期只交給受控的簽名管理流程,例如由指定管理員審批後執行,不能讓每一個建置 Job 都具備建立或修改證書的權限。
這樣可以把兩種責任分開:
- 簽名管理流程:建立、匯入、輪換與批准證書及描述檔。
- CI 建置流程:讀取已批准資產,執行測試、歸檔與發布。
同時,應分開管理以下秘密:
<SIGNING_REPO_TOKEN>:簽名倉庫存取憑據。<MATCH_PASSWORD>:簽名資產解密口令。<APPLE_AUTH>:Apple 服務認證。<DISTRIBUTION_TOKEN>:發布渠道令牌。
驗收時刻意觀察任務是否停在鑰匙串確認、雙重認證或圖形彈窗;並保存可收集的 Xcode 與 fastlane 日誌,但不要在日誌中輸出私鑰、口令或完整令牌。
提醒: 長期遠端節點的重啟、登入使用者變更與並發 Job,都可能使臨時鑰匙串、工作區或描述檔狀態改變;「今天能跑」不等於「重啟後仍能無人值守」。
共享節點與生產發布
同一台遠端 Mac 可承擔不同任務,但至少要在帳戶、工作區、鑰匙串與 CI 秘密上劃分邊界。開發建置不應讀取生產簽名身份;測試歸檔與正式發布也不應共用沒有清理策略的工作目錄。
建議透過兩個隔離任務進行驗收:
- 任務 A:只載入非生產證書,完成測試歸檔。
- 任務 B:在不同工作區與秘密範圍中載入生產發布所需資產。
- 連續或並行執行後,檢查 A 是否能看到 B 的身份、描述檔、令牌或殘留檔案。
- 重啟節點後重跑,確認臨時鑰匙串會重新建立,且不依賴人工點擊。
生產發布與證書輪換還要處理證書到期、描述檔重新生成、簽名倉庫不可用、口令輪換及遠端節點更換。Apple 提供了描述檔編輯、下載與刪除的 官方管理說明,可用於核對描述檔變更後的責任範圍。
遷移決策表
| 場景 | 建議策略 | 寫入權限 | 上線前必要驗收 |
|---|---|---|---|
| 全新專案 | 直接初始化 fastlane match | 只限受控初始化流程 | 遠端 Mac 完成同步、歸檔與安裝或分發驗證 |
| 已有生產簽名 | 先 match import,再雙軌運行 |
CI 不得寫入 | 同一提交比較舊鏈路與遠端鏈路 |
| 多應用同一團隊 | 分別管理 Bundle Identifier 與附屬 Target | 依應用與流程分層 | 主應用、Extension、Widget 等均完成簽名 |
| 多個 Apple 團隊 | 獨立分支或儲存空間 | 團隊權限完全分離 | 確認不會讀取其他團隊資產 |
| 一般 CI 建置 | readonly 加臨時鑰匙串 |
僅讀取批准資產 | 無互動提示、無殘留、重啟後可重跑 |
| 正式發布 | 受控流程執行輪換與發布 | 由發布責任人審批 | 歸檔、簽名、分發與回退均可驗證 |
遠端 Mac 驗收清單
遷移完成前,可逐項勾選:
- [ ]
<APP_BUNDLE_ID>、<TEAM_ID>與每個 Target 已核對。 - [ ] 現有證書、對應私鑰、描述檔與生產責任已盤點。
- [ ] 既有發布鏈路仍可用,且已保存回退入口。
- [ ] 匯入流程在隔離分支中完成,沒有先撤銷有效生產身份。
- [ ] match 同步先於 Xcode 建置與歸檔。
- [ ] CI 使用臨時鑰匙串,沒有等待圖形確認或人工輸入。
- [ ] 一般 Job 使用
readonly,寫入與續期被移出普通建置流程。 - [ ] 簽名倉庫憑據、解密口令、Apple 認證與發布令牌互相分離。
- [ ] 主應用、Extension、Widget、Watch App 或其他 Target 都完成驗證。
- [ ] 兩個隔離任務沒有互相繼承私鑰、描述檔、工作區或令牌。
- [ ] 節點重啟、使用者會話變更與並發執行後仍能重建。
- [ ] 同一提交已比較舊鏈路與遠端 Mac 的歸檔、簽名及安裝或分發結果。
三種遷移結論
| 驗收結果 | 結論 | 後續行動 |
|---|---|---|
| 遠端鏈路與舊鏈路結果一致,隔離與回退均通過 | 直接遷移 | 將正式 CI 切換至遠端 Mac,保留受控回退流程 |
| 建置成功,但重啟、附屬 Target 或權限隔離未通過 | 繼續雙軌 | 暫不移除舊鏈路,先修復環境與秘密邊界 |
| 私鑰不完整、簽名結果不一致或無法回退 | 暫緩上線 | 恢復資產盤點與責任確認,不執行 match nuke 或撤銷操作 |
若目前方案是依賴某位工程師的個人 Mac,常見缺點是私鑰留在個人 Keychain、節點無法在團隊成員離線時重建,以及重啟或權限變更後缺少可追溯的恢復流程;若改用一般 Linux 雲端伺服器,則又無法直接提供 Xcode 與 macOS 專屬簽名環境。對需要短期驗證、隔離測試或長期 CI 節點的團隊而言,先租用一台具備完整管理權限、可重啟驗收的遠端 Mac,通常比立即購買硬體或把簽名責任綁在個人裝置上更容易控制風險。可先查看 RUVCLOUD 的遠端 Mac 方案 與 租用方案資訊,再以非生產證書跑通 match、Xcode 歸檔與恢復流程,最後才決定是否遷移正式發布任務。