本排查清單把官方簽署流程拆成 4 層:簽名倉庫與解密、Keychain 完整簽名身份、Provisioning Profile 映射、專案與 CI 建置設定;這些層次分別對應 fastlane match 官方文件、fastlane 簽署故障排查說明 與 Apple 的 Provisioning Profile 結構文件。因此,遇到 fastlane match 簽名失敗 時,本週先按這個順序取證,不要先執行 match nuke,也不要盲目重建憑證;只有確認資產已失效或無法恢復,才進入輪換流程。
這篇適合在遠端 Mac 上執行 fastlane match,卻突然在 Archive 時看見缺少簽名身份的獨立開發者。
如果你的小團隊本地建置成功、無人值守任務卻失敗,或正準備把手工憑證管理集中化,下面的故障分層會比重新安裝 Xcode 更快縮小範圍。
30 分鐘內的故障定位時間線
先保存一份脫敏後的完整工作紀錄:match 的輸出、Archive 命令、退出狀態、Xcode 版本、活動 Scheme、Configuration、Target 與 export options。不要只截取最後一行;No signing certificate 可能是 Keychain 沒有私鑰,也可能是專案選錯 Team 或 Profile。
你可以按照以下里程碑判斷:
- 第 1 個入口:倉庫層。確認 CI 能以獨立憑據連到簽名倉庫,並能解密所需資產。
- 第 2 個入口:Keychain 層。查詢目前建置使用者可見的憑證、私鑰與完整 code-signing identity。
- 第 3 個入口:Profile 層。檢查 Bundle ID、Team、Profile 類型、有效狀態與 entitlements。
- 第 4 個入口:Archive 層。用同一提交、同一命令與同一匯出目標,確認專案實際選到的簽署資產。
可保留的證據包括:
MATCH_PASSWORD=<REDACTED>
SIGNING_REPO=<REDACTED>
TEAM_ID=<REDACTED>
BUNDLE_ID=<REDACTED>
KEYCHAIN_PATH=<REDACTED>
這些值只能作為佔位符出現在日誌或工單中。Token、部署金鑰、物件儲存憑據、解密密碼、Keychain 路徑與 App 識別資料都應先遮蔽,再交給其他人分析。
注意:原始日誌不要直接貼到公開 Issue 或聊天室。先移除簽名倉庫位置、Team ID、Bundle ID、憑據名稱與任何可重放的認證資訊,再保留錯誤前後的上下文。
倉庫存取與解密分流
原始碼倉庫能拉取,不代表簽名倉庫也能拉取。兩者可能使用不同 Git 部署金鑰、不同物件儲存帳戶,甚至由不同 CI Secret 注入。match 的儲存模式與加密行為應以官方 match 儲存說明為準,不要只用「原始碼 checkout 成功」判定簽名資產可用。
先在不執行 Archive 的診斷工作中驗證:
- CI 工作是否取得正確的簽名倉庫地址、分支或儲存模式。
- 執行使用者是否有讀取該倉庫或物件儲存的權限。
MATCH_PASSWORD是否與建立資產時使用的密碼一致。Team、平台與分發類型是否和目前工作一致。- 解密後的檔案是否真的被安裝,而非只下載到暫存目錄。
不要用真實資料替換下列診斷格式:
git ls-remote <SIGNING_REPO_PLACEHOLDER> <BRANCH_PLACEHOLDER>
security find-identity -v -p codesigning <KEYCHAIN_PLACEHOLDER>
fastlane match appstore --readonly \
--git_url <SIGNING_REPO_PLACEHOLDER> \
--git_branch <BRANCH_PLACEHOLDER>
第一行只能證明倉庫端點可被查詢,不能證明資產可以解密。第二行也不能單獨證明 Profile 正確;它只用來確認目前 Keychain 看得到哪些簽署身份。CI 憑據應逐項測試,避免把所有失敗都歸因於 fastlane。
憑證檔案與完整簽名身份
憑證檔案、私鑰與完整 code-signing identity 是三件不同的事。Apple 的註冊裝置分發說明明確把簽署與分發資產放在同一條鏈路中理解;只有公開憑證而沒有對應私鑰,通常不能完成實際簽署。
在遠端 Mac 上,請核對三個條件:
security find-identity是否列出預期的 Apple Distribution 身份,而不是只有憑證名稱。- 私鑰是否匯入到 fastlane 預期的 Keychain,而不是互動式登入帳戶的另一個 Keychain。
- 重啟後,該 Keychain 是否仍存在、可解鎖,且無人值守的建置使用者可以使用私鑰。
如果本地 Xcode 看到身份,遠端命令列卻看不到,先比較建置使用者、HOME、活動 Keychain 與活動 Xcode。不要把「允許所有程式存取所有 Keychain 項目」當成預設修復;這會擴大憑據暴露範圍,也可能掩蓋真正的帳戶或持久化問題。
Profile 映射與專案設定
Provisioning Profile 不只是檔案存在即可。應依序檢查:
- Profile 的 App ID 是否精確對應目前 Target 的 Bundle ID。
- Team 是否一致。
- Profile 類型是否符合目前是 Development、Ad Hoc、App Store 或其他分發情境。
- Profile 內列出的憑證是否仍然有效,且與 Keychain 的私鑰配對。
- Profile 的 entitlements 是否覆蓋專案實際使用的能力。
Apple 的Profile 管理說明可用來確認資產是否已失效、需要下載或刪除;Profile 內部欄位與 entitlements 則應對照Apple 的 TN3125核對。
多 Target 是最容易被忽略的映射錯誤來源。主 App、Notification Extension、Share Extension 或 macOS 輔助 Target 可能各自有 Bundle ID,也可能需要不同 Profile。多個 App 共用同一個 match 簽名倉庫時,可以集中管理,但不能因此把所有 Target 指向同一個 Profile。
涉及 Profile 檔案目錄、安裝位置或工具行為時,請在工單中同時記錄適用的 Xcode 與 fastlane 版本。這些行為可能隨版本調整;不要把某台機器上的固定路徑當成所有遠端 Mac 都相同。
本地成功與遠端失敗的環境差異
本地 Xcode 可能啟用了自動簽署、使用了已登入的 Apple 帳戶,或持有遠端環境沒有的 Keychain 項目。命令列 Archive 則可能明確使用另一個 Scheme、Configuration、export options 與建置使用者。這就是「本地成功、遠端失敗」不能直接歸因於憑證過期的原因。
修復流程應固定變數:
- 以同一個 Git commit 在本地與遠端建置。
- 記錄兩邊的活動 Xcode 與
xcode-select目標。 - 使用同一個 Scheme、Configuration 與 Archive 命令。
- 使用同一份、已脫敏的 export options。
- 確保
match先於 Archive 動作執行。 - 先讓遠端完成最小 Archive,再測試匯出與上傳。
CI 使用 readonly 時,只會取用既有資產,不會代替管理員建立或更新缺少的憑證與 Profile。fastlane 的持續整合配置指南也應與目前 CI Secret 注入方式一併核對。若資產已過期,先在有權限的受控環境完成輪換,再讓 CI 回到 readonly。
獨立 FAQ:五個高風險分支
match 找不到 Apple Distribution 憑證
先區分「倉庫沒有資產」與「Keychain 沒有完整身份」。如果解密成功但 security find-identity 沒有預期結果,檢查私鑰是否一併匯入,以及建置程序是否使用了正確 Keychain。只有在確認資產不可恢復時,才安排憑證輪換。
readonly 取不到 Profile
readonly 不是修復或建立模式。先確認目前 Bundle ID、Team 與 Profile 類型,再查看簽名倉庫是否真的含有該組合。若管理端需要產生新 Profile,應由有權限的流程執行,完成審核後再讓 CI 重新讀取。
重啟後 Keychain 無法解鎖
先記錄重啟前後的 Keychain 名稱、擁有者與解鎖狀態。遠端 Mac 若以不同帳戶啟動工作,原本互動式登入建立的 Keychain 不一定可用。把建置用 Keychain 的建立、解鎖與最小權限授權納入初始化流程,再以無人值守 Archive 驗證。
多個 Bundle ID 的 match 組織方式
建議先建立一份映射表,列出 App、Extension、平台、Bundle ID、Profile 類型與對應 Scheme。簽名倉庫可以集中保存,但每次執行都要明確指定目標集合,避免一個 Target 的設定被另一個 App 或 Extension 覆寫。
憑證過期後的處理界線
過期不等於必須清空全部資產。先盤點哪些 App、Profile、發布流程與 CI 工作依賴該憑證,再做受控輪換。依照match nuke 官方文件的影響範圍理解,nuke 應是最後選項,而不是看到錯誤訊息後的第一個指令。
修復、輪換與重建的決策表
| 情況 | 先做的動作 | 是否進入輪換 | 風險控制 |
|---|---|---|---|
| 倉庫認證、分支或解密失敗 | 修正獨立憑據與設定 | 否 | 保留脫敏日誌,不改動既有資產 |
| 憑證與私鑰存在,但 Target 映射錯誤 | 修正 Scheme、Bundle ID 或 Profile 指派 | 否 | 先完成最小 Archive |
| Keychain 重啟後不可用 | 修復持久化、解鎖與建置帳戶 | 視驗證結果 | 禁止預設放寬全部 Keychain 權限 |
| Apple Distribution 憑證過期 | 盤點依賴關係後受控更新 | 是 | 同步更新受影響 Profile |
| 資產無法解密且沒有可用備份 | 先確認影響範圍,再重建 | 最後才是 | 不執行未評估的 match nuke |
遠端 Archive 的恢復驗收
修復後不要只看 match 輸出顯示成功。請安排一次不緊急的驗收里程碑:
- 使用非緊急發布分支與固定提交。
- 在乾淨工作目錄執行
match,再執行 Archive。 - 核對主 App 與所有 Extension 的 Bundle ID、Profile 與 entitlements。
- 以相同 export options 完成匯出,確認簽署驗證沒有回退到自動簽署。
- 在不依賴互動式登入的情況下重啟遠端 Mac,再重跑一次 Archive。
- 上傳前只檢查產物與簽署資訊,不要直接把測試產物送到正式發布流程。
下面這份表可作為最後的驗收紀錄;其中的環境欄位必須填入你實際使用的資料,不要用範例值代替。
| 驗收項目 | 通過條件 | 未通過時的回退 |
|---|---|---|
| 簽名倉庫 | CI 可獨立存取並完成解密 | 回到倉庫認證層 |
| Keychain | 重啟後仍可由建置帳戶使用私鑰 | 回到 Keychain 持久化層 |
| Profile | 每個 Target 均對應正確 Bundle ID、Team 與類型 | 回到映射與 entitlements |
| Archive | 同一提交可由命令列完成 | 比較 Xcode、Scheme 與 Configuration |
| 匯出 | 產物由預期身份簽署 | 檢查 export options 與 Profile |
| 上傳前驗證 | 不需互動登入即可完成檢查 | 暫停發布,不執行 nuke |
如果你的失敗根因是臨時機器重置、Keychain 無法持久保存,或 CI 工作經常被中斷,問題就不只是某張憑證的狀態,而是建置環境沒有通過重啟驗收。相較於在每次發布前重新配置本機,使用常駐遠端 Mac 可以把同一套簽署鏈路固定下來;但你仍應先以非緊急分支驗證重啟後能否完成 Archive,再決定是否長期採用。需要臨時或持續的 Mac 建置環境時,可先查看 VMSPIN 的繁體中文服務入口,再依實際保留時間比較遠端 Mac 方案與租用週期。
若你目前依賴的是個人 Mac,常見缺點是 Keychain 綁定互動式帳戶、機器休眠或關機會中斷 CI,而且本地設定難以被團隊重現;若改用臨時雲端工作階段,又可能在重置後遺失簽名環境。把遠端 Mac 租用環境當作候選方案時,請把「重啟後仍能解鎖、match 能解密、Archive 能完成」列為驗收條件,而不是只比較月費或 CPU 規格。需要直接建立測試環境時,再前往VMSPIN 遠端 Mac 訂購頁安排一次非正式發布驗證。