無頭租賃雲 Mac 上的 iOS / macOS 公證:notarytool 與 stapler(2026)
在 香港、日本、韓國、新加坡、美國 租用的 裸金屬 Apple Silicon 雲 Mac 上,團隊通常用 SSH 跑 CI/CD。若要把簽過名的 macOS .app、命令行工具或 .dmg 在 App Store 之外分發,仍要走 Apple 的公證(Notarization)。本文給出 2026 可落地流程:用登入鑰匙串保存 notarytool 憑據、非交互提交、保留 JSON 日誌與 submission id、成功後 stapler staple 並 stapler validate,以及在流水線裡設硬門禁。可配合 遠程 Archive 與導出、遠程簽名優化、僅 CLT 與完整 Xcode 一起閱讀。
為什麼在獨立雲 Mac 上做公證
- 更接近 Apple API 路徑:把構建機放在目標區域,可縮短提交與拉取日誌的尾延遲。
- 環境可復現:同一臺 NVMe、同一 Xcode 補丁、同一套鑰匙串佈局,夜構建不會“因筆記本狀態而漂移”。
- 與開發機解耦:公證不跟 Zoom/Slack 搶 CPU,也不汙染個人鑰匙串。
stapler validate 失敗則整 job 失敗。
無頭環境常見痛點
- 鑰匙串彈窗不會出現:純 SSH 無 GUI;需
security unlock-keychain或使用已預授權 codesign/notarytool 的 專用 CI 用戶。 - ZIP 結構錯誤:壓扁 bundle、重複
MacOS、剝離擴展屬性會導致日誌裡出現 “Invalid” 或難懂的簽名錯誤。 - 忘了裝訂:Apple 側已 Accepted,但未對下載件執行
stapler staple,新加坡/美國 用戶仍可能遇到 Gatekeeper 告警。 - 並行任務搶帶寬:三條發佈分支各丟 400 MB zip,1 Gbps 出口會被打滿,看起來像 “Apple 掛了”。
- 審計缺口:不保存 submission id 與 JSON,安全覆盤無法對齊 2026-04-09 公證產物與對外發布物。
按產物類型選打包方式
macOS 分發不是 iOS TestFlight。為 .app / .dmg / .pkg 各寫一條規則,避免值班同事在 港/日/韓 現場臨時發明流程。
| 交付物 | 常見提交形態 | 裝訂對象 | 易錯點 |
|---|---|---|---|
已簽名 .app |
ditto -c -k --keepParent MyApp.app MyApp.zip |
受理後的 bundle 目錄 | 強化運行時與 entitlement 必須匹配 Developer ID 證書 |
.dmg |
按工具鏈提交 DMG(或遵循 Apple 文檔的容器流程) | 用戶實際掛載的 DMG 文件 | 卷內佈局與內含 app 簽名必須一致 |
扁平 .pkg |
productbuild 等安裝包產物 |
磁盤上的 .pkg |
組件包 ID / receipt 版本錯誤會直接變成工單量 |
雲 Mac 前置條件
| 要求 | 說明 |
|---|---|
可用 notarytool |
隨新版 Xcode/CLT 提供;用 xcrun notarytool --version 自檢 |
| 簽名身份 | Developer ID Application(macOS 分發)——與導出流水線一致 |
| App Store Connect API Key | Issuer ID、Key ID、.p8 —— 用鑰匙串 profile 保存,勿明文進倉庫 |
| 無人值守鑰匙串 | SSH 用戶需加載登入鑰匙串,或在受控密鑰步驟執行 security unlock-keychain |
為 notarytool 寫入鑰匙串配置
一次性(交互或安全引導)把憑據存成 profile,例如 ac-notary:
xcrun notarytool store-credentials "ac-notary" --key ~/AuthKey_XXXXXX.p8 --issuer <issuer-uuid> --key-id <key-id>
.p8 輪換週期與其他 ASC API Key 對齊。
提交 zip 並等待
壓縮 .app(務必保留符號鏈接,常用 ditto -c -k --keepParent MyApp.app MyApp.zip):
xcrun notarytool submit MyApp.zip --keychain-profile "ac-notary" --wait
若在 CI 拆分等待與輪詢,從 JSON 取 submission id,循環執行 xcrun notarytool log <id> --keychain-profile "ac-notary" 直到狀態穩定,並把日誌寫入製品庫。
解析建議:用 jq(或語言內置 JSON)斷言每次綠構建都包含 id、status、message 摘要。Invalid 時日誌常指向 zip 內具體路徑——請保留原始 zip 與日誌,便於和同一 Xcode 固定版本的“黃金包”做 diff。
限流:若出現 HTTP 429 或連續超時,可在單臺上對公證階段加互斥:例如 /tmp 文件鎖,或每臺 Mac 隊列深度 1。
裝訂與校驗
Apple 受理成功後:
xcrun stapler staple MyApp.app
xcrun stapler validate MyApp.app
對磁盤映像請遵循當前 Apple 文檔;務必以 validate 收尾,避免 新加坡/美國 用戶側 Gatekeeper 意外。
順序:僅在 Apple 報告成功之後裝訂。若本地再次重籤或重打包,必須重新提交——stapler 不能“修復”已漂移的簽名。
常見失敗與優先排查
| 現象 | 先查 | 常見修復 |
|---|---|---|
notarytool 報 Unable to authenticate |
profile 名稱拼寫;鑰匙串是否解鎖;CI 用戶能否讀 .p8 |
重跑 store-credentials;API Key 過期則輪換 |
| 日誌提示二進制簽名無效 | zip 前對 app 做深度 codesign 驗證;隔離屬性(quarantine) | 用正確 Developer ID 重新導出;用 ditto 打 zip,避免 Finder“壓縮” |
DMG 上 stapler validate 失敗 |
是否裝訂錯文件;裝訂時 DMG 仍掛載 | 卸載卷;對實際上傳的 DMG 路徑裝訂並再校驗 |
| 輪詢一直 In Progress | 機器時鐘漂移;出口防火牆攔截 Apple 端點 | 同步 NTP;放行 notary 相關域名/IP;退避上限建議 15 分鐘 |
CI 門禁清單
notarytool submit非零退出則失敗。- 構建記錄必須附帶 submission id + 完整 JSON 日誌(保留期 ≥ 合規要求)。
- 對即將發佈的二進制執行
stapler validate,失敗則失敗。 - 在同一製品清單記錄 Xcode build 字串 與 notarytool 版本。
- 上傳 CDN/MDM 前對裝訂後產物做
shasum -a 256。 - 可選:在乾淨切片上跑 spctl 評估,模擬終端用戶環境。
常見問題(表格式)
| 問題 | 回答 |
|---|---|
| 這和 iOS App Store 上傳是一回事嗎? | 不是——公證面向 Mac App Store 之外的 macOS 分發。iOS IPA 走不同路徑;參見 Archive 實操。 |
| 公證任務該選 1TB 還是 2TB? | 大體積 .zip、日誌、多份 Xcode 並存會吃滿 NVMe;並行前擴容。見 套餐與節點。 |
| 只要 CLT 還是要完整 Xcode? | 新版 CLT 通常含 notarytool,但多數團隊仍在同一臺機做 Archive+導出——對比 CLT 與完整 Xcode。 |
公證能否與 xcodebuild 同機? |
可以,但應用工作區分隔 + 互斥,避免簽名/打 zip 競態;見 並行任務佈局。 |
| 不用 VNC 怎麼排障? | 先拉日誌;確需 GUI 工具時短期開 VNC,再切回 SSH 自動化。 |
為何租賃 Mac mini M4 適合公證 CI
公證負載呈突發型:壓縮數百 MB bundle、上傳、輪詢 API,再在快速盤上裝訂校驗。Mac mini M4 提供原生 Apple Silicon 的 xcrun 行為、可預期的 NVMe 延遲,並可在 香港/日本/韓國/新加坡/美國 選址以貼合合規與 CDN 出口。按需租用避免一次性資本開支,同時保留與工位機一致的 SSH 與可選 VNC——適合 7×24 專線且不搶佔筆記本登入鑰匙串。
租用還便於鏡像固定:每週同一 Xcode 與 notarytool 語義。Apple 調整策略時,先升一小撮節點驗證綠構建,再全量推廣。磁盤與節點規格見 定價;交互調試可偶爾用 VNC,生產仍以無頭為主。
結語:只要把鑰匙串與日誌當作一等公民,無頭租賃 Mac mini M4 就是按計劃跑 notarytool + stapler 的合理位置。下一步:閱讀 SSH 與部署幫助,或強化 並行構建隔離,避免公證與簽名任務互相搶鎖。