fastlane match 遠端 Mac 簽名:2026 CI 指南

fastlane match 遠端 Mac 簽名:2026 CI 指南

本地簽名成功、遠端 Mac 卻在 codesignexportArchive 階段失敗,通常不是編譯指令本身出錯。

最快的解法是:讓 CI 使用 readonly 模式同步既有簽名資產,配合臨時或隔離鑰匙串、每個 Target 的顯式 Profile 映射,以及建置後清理;完成後還要測試重啟、並發、證書輪換與節點重建。

這篇文章適合維護 iOS 自動打包流程的 DevOps 工程師、需要讓多個 App 或 Extension 共用節點的發布工程師,以及準備把本地 fastlane 流程搬到長期在線遠端 Mac 的獨立開發者。

本地能簽名,遠端 CI 失敗的真正邊界

一次成功的 Apple 代碼簽名,不只依賴一張證書。至少要同時確認四類資產:

  • 簽名證書,以及與證書配對的私密金鑰。
  • 對應 Bundle ID 的 provisioning profile。
  • 能被 Runner 讀取的鑰匙串與解鎖狀態。
  • Xcode 專案中的簽名模式、Team、Entitlements 與 Profile 映射。

本地 Mac 通常已有登入會話、預設鑰匙串和歷史資產。遠端 Mac 的背景 Runner 則可能沒有互動式桌面會話,甚至使用另一個執行帳戶。結果是「證書已匯入」不等於「私密金鑰能被 codesign 使用」。

Apple 將 CODE_SIGN_IDENTITY 定義為鑰匙串中有效簽名身份的名稱;CODE_SIGN_STYLE 則決定由 Xcode 自動管理,還是由工程團隊手動管理簽名資產。這兩項設定不一致時,CI 可能選到錯誤身份,或在沒有權限建立資產時直接失敗。Apple Build Settings Reference

可先在遠端節點輸出不含密碼的診斷資料:

security find-identity -v -p codesigning
xcodebuild -showBuildSettings -scheme "$SCHEME" \
  | grep -E 'PRODUCT_BUNDLE_IDENTIFIER|DEVELOPMENT_TEAM|CODE_SIGN_STYLE|PROVISIONING_PROFILE'

可以記錄身份名稱、Team ID、Bundle ID、Profile 名稱和錯誤類型。不要把 MATCH_PASSWORD、Apple 登入權杖、私密金鑰密碼或簽名倉庫憑據寫入公開日誌。

單一 App:readonly 同步比現場產生資產更適合 CI

fastlane match 的職責是取得並安裝既有證書與 Profiles;build_appgym 的職責才是執行歸檔與輸出。兩者不應混成「建置時臨時向 Apple 申請資產」的流程。

官方文件建議 CI 使用 readonly,只抓取現有證書和 Profiles,不在建置期間建立新資產。Match 儲存結構也會區分 certsprofiles,這讓節點同步和資產審查有清晰邊界。fastlane match 官方說明

一個較穩妥的 lane 可以寫成:

platform :ios do
  lane :ci_release do
    setup_ci(
      keychain_name: "fastlane_tmp_keychain",
      timeout: 0
    )

    match(
      type: "appstore",
      readonly: true,
      app_identifier: ENV.fetch("APP_IDENTIFIER")
    )

    build_app(
      scheme: ENV.fetch("SCHEME"),
      clean: true,
      export_method: "app-store"
    )
  end
end

setup_ci 目前官方文件列出的預設臨時鑰匙串名稱是 fastlane_tmp_keychain,逾時預設為 3600 秒;設定 timeout: 0 代表不設定逾時。該 Action 支援 iOS 和 macOS 流程,並會把 match 切換到唯讀模式。setup_ci 官方參數

這段設定仍然需要透過環境變數注入秘密:

export MATCH_PASSWORD='[由 CI Secret 注入]'
export APP_IDENTIFIER='com.example.app'
export SCHEME='Release'
bundle exec fastlane ci_release

[由 CI Secret 注入] 只是佔位符,不應替換成真實密碼後提交到原始碼倉庫。若工程團隊需要先初始化或更新簽名資產,應在受控工作站完成;發布 CI 只負責取得已審核版本。

多 Target:把 Profile 映射從隱式選擇改成可審計設定

主 App、Widget、Notification Service Extension 通常有不同 Bundle ID,也可能需要不同的 App Group、Push Notification 或其他 Entitlements。只呼叫一次 match,再讓 Xcode 自動猜測所有 Profile,會讓結果依賴節點狀態和專案設定,難以重現。

Profile 映射至少要在三個層次保持一致:

  1. Matchfile 保存儲存位置、Team 和共同同步策略。
  2. Fastfile 指定每個 Bundle ID 需要的簽名類型。
  3. Xcode Build Settings 或 .xcconfig 保存 Target 對應的 Profile 名稱。

例如:

match(
  type: "appstore",
  readonly: true,
  app_identifier: [
    "com.example.app",
    "com.example.app.widget",
    "com.example.app.notification"
  ]
)

之後應從 Xcode 的最終設定確認每個 Target,而不是只看 lane 是否返回成功:

xcodebuild -showBuildSettings -scheme "$SCHEME" \
  | grep -E 'TARGETNAME|PRODUCT_BUNDLE_IDENTIFIER|PROVISIONING_PROFILE_SPECIFIER'

Apple 說明,Target 的 Build Settings 可以由專案、設定檔和 Target 層級共同組成;不同層級的值會影響最後建置結果。將簽名相關設定放入版本控制中的 .xcconfig,通常比依賴某台 Mac 的圖形介面狀態更容易審查。Apple Build Configuration 說明

兩種遠端 Mac CI 設計,哪一種更容易復原?

下表不是單純比較成本,而是比較簽名故障發生後,工程團隊能否快速定位和重建。

決策維度 共用登入鑰匙串 CI 臨時或隔離鑰匙串
私密金鑰邊界 容易混入舊證書 每次任務可重新同步
多 Team 共用節點 舊資產誤用風險較高 可按工作流程分隔
背景 Runner 依賴使用者會話 可在 lane 內明確建立
任務完成後清理 容易留下 Profiles 和金鑰 可配合清理程序
節點重建 需要人工盤點 以 Match 儲存庫作為來源
適合情境 個人互動式開發 發布 CI、長期 Runner

對長期在線遠端 Mac 而言,隔離鑰匙串通常更容易驗收。共用鑰匙串並非絕對不可用,但必須清楚知道哪些 Team、證書和 Profile 可存在於同一個執行帳戶中,並禁止不同發布流程同時修改簽名資產。

多人團隊與多 Apple Team:權限要按發布邊界拆開

多人共用節點時,問題不只在「能不能下載 Match 儲存庫」。還要確認:

  • 不同 Apple Team 是否使用不同的存取憑據。
  • 不同 App 是否應使用不同分支或不同儲存路徑。
  • CI 是否只有讀取權限,還是也能建立、撤銷證書。
  • 離職或角色變更後,舊的 Match 密碼、部署金鑰和 API 憑據是否已失效。

readonly 的價值不只是避免建立新 Profile,也能降低 CI 誤撤銷或改寫團隊資產的風險。對正式發布流程,顯式設定通常比自動簽名更容易留下可審計證據;自動簽名則較適合開發者在本地快速建立或更新測試環境。

每次發布應保留以下非秘密證據:

Team ID
Bundle ID 清單
簽名身份名稱
Profile 名稱與 UUID
Xcode scheme
export method
archive 路徑
建置提交版本

不要保存私密金鑰內容、Match 密碼或完整存取權杖。日誌的目標是證明「使用了哪一組資產」,不是把秘密複製到另一個系統。

長期 Runner:鑰匙串隔離與清理要一起設計

互動式登入可以簽名,但背景 Runner 失敗,常見檢查順序如下:

  1. 確認 Runner 使用的 macOS 使用者。
  2. 確認該使用者看到的鑰匙串搜尋路徑。
  3. 確認臨時鑰匙串是否已建立並設為可用。
  4. 確認私密金鑰的存取控制沒有等待圖形介面確認。
  5. 確認建置結束後,歸檔、Profile 和臨時鑰匙串沒有污染下一個任務。

建置後可先輸出非秘密狀態:

security list-keychains
security default-keychain
security find-identity -v -p codesigning
ls -la "$HOME/Library/MobileDevice/Provisioning Profiles"

清理動作必須依實際建立的名稱執行,不能直接把破壞性指令當成通用排障方案。若使用長期節點,先保存失敗任務的日誌和 Profile 識別資料,再刪除臨時資產。若同一節點有並發任務,應避免兩個流程共用同一個可寫入鑰匙串。

官方 fastlane 的 CI 指南也指出,setup_ci 會建立臨時鑰匙串;缺少這個步驟時,建置可能因鑰匙串狀態而卡住,而不是立即回報清晰的簽名錯誤。fastlane CI 指南

證書輪換與節點重建:先確認資產,再重建環境

遇到證書到期、撤銷或 Profile 不再匹配時,不宜先執行破壞性重置。較安全的恢復順序是:

先確認遠端節點目前持有什麼

security find-identity -v -p codesigning
find "$HOME/Library/MobileDevice/Provisioning Profiles" \
  -type f -name '*.mobileprovision' -print

再檢查 Archive 使用的身份與 Profile:

codesign -dvvv "Payload/App.app" 2>&1 \
  | grep -E 'Identifier|Authority|TeamIdentifier'

security cms -D -i "[PROFILE_PATH]" \
  | grep -E 'application-identifier|UUID|Name|ExpirationDate'

再確認 Match 儲存庫是否為已核准版本

如果儲存庫內的證書、私密金鑰和 Profile 已更新,先在隔離節點執行唯讀同步。不要讓多個工作流程同時修改相同資產。

最後重新歸檔並驗證輸出

bundle exec fastlane ci_release

codesign --verify --deep --strict --verbose=2 \
  "Payload/App.app"

「lane 成功」只代表流程沒有在該步驟返回錯誤。完整驗收還應包括:

  • Archive 確實產生。
  • 每個 Target 使用預期 Profile。
  • App 的 Team、Bundle ID 和 Entitlements 正確。
  • 輸出的 IPA 可被驗證。
  • 舊證書失效後,流程不會靜默使用錯誤身份。

Apple 的簽名身份和 Profile 行為會受到 Xcode Build Settings、Scheme 及 Target 層級設定影響,因此 Xcode 的最終輸出比單看 Matchfile 更具證明力。Apple Scheme 說明

五步完成遠端 Mac 簽名驗收

  1. 固定資產來源:確認 Match 儲存方式、分支、Team 和 Bundle ID 清單。
  2. 建立隔離鑰匙串:在 lane 起始處執行 setup_ci,並把秘密交由 CI Secret 注入。
  3. 唯讀同步:以 match(..., readonly: true) 取得已核准證書和 Profiles。
  4. 顯式建置:固定 Scheme、Export 方法和各 Target 的 Profile 映射。
  5. 做三輪測試:重啟後首次建置、連續建置、失敗後清理再建置。

若還要把節點交給多個 App 或 Team 使用,追加並發測試和節點重建演練。只有在新節點不依賴舊登入狀態、舊鑰匙串或人工匯入資產時,才可視為真正可維護的 macOS CI。

遠端 Mac 與實體 Mac:簽名流程之外的取捨

自建 Mac mini 或本地 Mac 的優點是可直接連接實體裝置、除錯本地周邊,且長期固定使用時不必處理遠端連線。但它也有幾個現實缺點:前期硬體支出集中、設備故障需要自行維修、外網連線與電力由團隊負責,而且多人共用時仍要自行處理 SSH、權限、鑰匙串和 Runner 清理。

遠端 Mac 的價值不在於把簽名問題自動消失,而在於提供一台具備完整 root 權限、可按週期使用的真實 macOS 節點。若目前只是一次遷移、階段性發布,或需要先完成重啟與節點重建測試,使用 SFTPMAC 的 Mac 使用方案 往往比立即購買實體設備更容易控制投入;需要核對週期與方案時,可再查看 Mac mini 租用價格

不過,若流程需要長期固定負載、實體 iPhone 連線或完全離線作業,自購 Mac 仍可能更合適。若目標是先驗證 fastlane match、Xcode 歸檔和證書輪換,則可先使用 具備完整權限的遠端 Mac 節點 完成重啟、並發與重建測試,再決定是否投入長期硬體。