在雲端 Mac mini 上打通 notarytool 公證與 fastlane TestFlight 自動發布

CI/CD 實踐 ·約 9 分鐘閱讀

在雲端 Mac mini 上打通 notarytool 公證與 fastlane TestFlight 自動發布

在雲端 Mac mini 上打通 notarytool 公證與 fastlane TestFlight 自動發布

凌晨兩點,出海團隊的營運在群組裡問「新版本什麼時候能進 TestFlight」,而你正對著終端機裡第三次報錯的 codesign 發愣——公證鏈路只要有一個環節設定不對,整套流程就會在提交那一步靜靜卡死,不報錯也不通過,只留一個模糊的狀態碼給你猜。這篇文章記錄我們在雲端 Mac mini 節點上,把簽章、公證、上傳 TestFlight 這三步徹底打通、跑成無人值守流水線的完整過程。

為什麼公證是出海團隊的隱形卡點

從 Xcode 13 開始,altool 的公證介面已經廢棄,蘋果統一收攏到 notarytool。不少團隊的 CI 腳本還停留在舊教學的寫法,證書類型、時間戳參數、entitlements 設定只要有一處出錯,公證請求就會被靜默拒絕,錯誤訊息藏在一份要另外拉取的 JSON 日誌裡。更麻煩的是,公證要排隊等蘋果伺服器處理,本地端根本無法重現「這次為什麼慢了 40 分鐘」,只能靠標準化流程來降低變數。

把這套流程放在雲端 Mac mini 上跑,好處是證書、API Key、keychain 狀態都固定在一台長期在線的節點上,不會因為換了台筆電或團隊成員離職就得重新匯入證書——這也是我們堅持「獨享實體機、按週期租用」而不是每次現開臨時環境的原因。

事前準備:證書與 API Key

先確認三件事都齊了:

  1. Apple Distribution 證書(用於 App Store 分發)+ 對應私鑰,匯出為 .p12 後匯入節點的 keychain。
  2. App Store Connect API Key:在 App Store Connect 的「使用者與存取權」頁面產生,下載 .p8 私鑰檔,記下 Key ID 和 Issuer ID。這是全程不需要登入任何 Apple 帳號的關鍵——公證與上傳都走 API Key 驗證。
  3. provisioning profile:要與 Bundle ID、證書相符,建議用 fastlane match 統一託管,避免每台機器各自產生一份。

把證書匯入節點的 keychain:

security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security import DistributionCert.p12 -k build.keychain -P "$CERT_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security list-keychains -d user -s build.keychain login.keychain

.p8 檔案放到節點固定路徑,例如 ~/.appstoreconnect/private_keys/AuthKey_ABCDE12345.p8,權限設為 600,千萬不要提交進任何程式碼倉庫。

用 notarytool 走完簽章到公證

簽章時務必帶上時間戳與硬化執行時期(hardened runtime),否則公證會因為缺少 secure timestamp 而直接失敗:

codesign --sign "Apple Distribution: Your Company" \
  --timestamp --options runtime \
  --entitlements App.entitlements \
  build/App.app

打包成 .pkg 或直接用 xcodebuild -exportArchive 產出 .ipa 後,送出公證:

xcrun notarytool submit build/App.ipa \
  --key ~/.appstoreconnect/private_keys/AuthKey_ABCDE12345.p8 \
  --key-id ABCDE12345 \
  --issuer 69a6de7x-xxxx-47e3-e053-5b8c7c11a4d1 \
  --wait

--wait 會阻塞直到蘋果伺服器回傳結果,通常 3~8 分鐘,偶爾遇到排隊高峰會拖到 20 分鐘以上。拿到 submissionId 後,如果狀態是 Invalid,一定要把完整日誌拉下來查清楚,別憑猜測就重新簽章:

xcrun notarytool log <submissionId> \
  --key ~/.appstoreconnect/private_keys/AuthKey_ABCDE12345.p8 \
  --key-id ABCDE12345 --issuer 69a6de7x-xxxx-47e3-e053-5b8c7c11a4d1

我們踩過的一個坑:entitlements 裡開了 App Sandbox,但某個第三方動態庫沒有對應簽章,notarytool 的日誌只會提示「The binary is not signed with a valid Developer ID certificate」,看起來像是證書問題,實際上是子模組漏簽——用 codesign --verify --deep --verbose=4 逐層檢查才能定位到具體檔案。

用 fastlane pilot 自動分發到 TestFlight

公證通過後,用 fastlane pilot 上傳到 TestFlight,同樣走 API Key 驗證,不需要互動式登入:

lane :beta do
  api_key = app_store_connect_api_key(
    key_id: ENV["ASC_KEY_ID"],
    issuer_id: ENV["ASC_ISSUER_ID"],
    key_filepath: ENV["ASC_KEY_PATH"]
  )

  build_app(scheme: "App", export_method: "app-store")

  pilot(
    api_key: api_key,
    skip_waiting_for_build_processing: true,
    changelog: "自動化分發:修復啟動崩潰與網路重試邏輯"
  )
end

skip_waiting_for_build_processing: true 讓腳本上傳完就結束,不會卡住節點上的其他任務;後續處理進度改用輪詢,或乾脆等蘋果的通知信即可,沒必要把 CI 任務耗在等待上。

常見錯誤與排查清單

錯誤訊息關鍵字 可能原因 處理方式
signature does not include a secure timestamp codesign 沒加 --timestamp 重新簽章並加上 --timestamp --options runtime
The binary is not signed with a valid Developer ID 某個內嵌 framework/動態庫未簽章 codesign --verify --deep 逐層排查後單獨簽章
Missing Info.plist key: ITSAppUsesNonExemptEncryption 使用了加密相關 API 但未宣告 在 Info.plist 補上對應鍵值,或依實際情況宣告豁免
Invalid Provisioning Profile profile 與證書/Bundle ID 不匹配 fastlane match 重新同步證書與 profile
提交後長時間無回應 蘋果伺服器在排隊 不要重複提交,等待或查詢 submissionId 狀態

把這張表貼進團隊的排障文件,能省下大量「到底是誰的錯」的來回溝通。

把流程釘進雲端 Mac mini 的排程任務

最後一步,是讓整條流水線自動跑起來,而不是靠人手動觸發。在雲端 Mac mini 上用 launchd 註冊一個回應 Git webhook 或定時輪詢的任務,把簽章、公證、上傳三步串接執行,失敗時透過郵件或工單系統通知負責人,而不是讓腳本靜默退出。因為節點是獨享實體機、常駐在線,keychain 狀態與證書不會像臨時容器那樣每次重新初始化,公證請求的成功率與排隊時間也更穩定。

常見問題

notarytool 顯示 The signature does not include a secure timestamp 怎麼辦?

簽章時缺少時間戳記所致,重新以 codesign --timestamp --options runtime 簽章後再提交公證即可。

fastlane pilot 上傳後 TestFlight 一直顯示處理中正常嗎?

蘋果通常 15~60 分鐘處理完成,超過 2 小時建議查看 App Store Connect 活動紀錄,常見原因是 Info.plist 缺少 ITSAppUsesNonExemptEncryption 導致人工審核。

在雲端 Mac mini 跑這套流程需要登入 Apple 帳號嗎?

不需要,只要把憑證匯入節點鑰匙圈,並放上 App Store Connect API Key 的 .p8 檔與環境變數即可全程無人值守。

需要一台獨享 Mac mini 跑建置作業?

新加坡/東京/韓國/香港/美西三地實體節點,按天起租,10 分鐘內收到 VNC/SSH 憑證。

立即租用 Mac mini