クラウドMac miniでnotarytool公証とTestFlight自動配信を組む
深夜2時、海外展開チームの運用担当がグループチャットで「新バージョンはいつTestFlightに上がるの?」と聞いてくる。その一方であなたは、ターミナルで3回目のエラーを出すcodesignを前に固まっている——公証チェーンはどこか一箇所でも設定を誤ると、提出の段になって静かに止まってしまう。エラーも出ず、通過もせず、曖昧なステータスコードだけが残る。この記事は、クラウドMac miniノード上で署名・公証・TestFlightアップロードの3ステップを完全に通し、無人稼働のパイプラインとして走らせるまでの手順をまとめたものだ。
なぜ公証が海外展開チームの見えないボトルネックになるのか
Xcode 13以降、altoolの公証インターフェースは廃止され、Appleはnotarytoolに一本化した。多くのチームのCIスクリプトは古いチュートリアルの書き方のままで、証明書タイプ、タイムスタンプパラメータ、entitlements設定のどこか一つが間違っていると、公証リクエストは黒暗黒に拒否され、エラー情報は別途取得する必要のあるJSONログの中に埋もれている。さらに厄介なのは、公証はAppleのサーバー側のキュー処理に依存しているため、「なぜ今回は40分も遅れたのか」をローカルで再現する方法がなく、標準化されたプロセスで変動要因を減らすしかない。
このプロセスをクラウドMac mini上で走らせるメリットは、証明書、APIキー、keychainの状態が常時オンラインの1台のノードに固定され、ノートPCを買い替えたりメンバーが退職したりするたびに証明書を再インポートする必要がなくなることだ。これこそ、毎回一時的な環境を立ち上げるのではなく「専有物理マシンを期間貸しする」方式にこだわる理由でもある。
準備:証明書とAPIキー
まず以下の3点が揃っているか確認する。
- Apple Distribution証明書(App Store配信用)とその秘密鍵。
.p12としてエクスポートし、ノードのkeychainにインポートする。 - App Store Connect APIキー:App Store Connectの「ユーザーとアクセス」ページで生成し、
.p8秘密鍵ファイルをダウンロードして、Key IDとIssuer IDを記録する。これが全工程でApple IDにログインする必要がなくなる鍵で、公証もアップロードもすべてAPIキー認証で行う。 - 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で署名から公証までを完了させる
署名時はタイムスタンプとハードンドランタイムを必ず付ける。付けないと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はAppleサーバー側の結果が返るまでブロックする。通常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キー認証で、対話的なログインは不要だ:
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を指定すると、アップロード完了後すぐにスクリプトが終了し、ノード上の他のタスクをブロックしない。処理進捗はポーリングか、Appleからの通知メールで確認すれば十分で、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を再同期する |
| 提出後に長時間応答がない | Appleサーバー側のキューが混雑している | 再提出せず、待つかsubmissionIdのステータスを確認する |
このテーブルをチームのトラブルシューティングドキュメントに貼っておくと、「これは誰のミスか」というやり取りをかなり減らせる。
クラウドMac miniの定時タスクにパイプラインを組み込む
最後のステップは、人手で手動トリガーするのではなく、パイプラインを自動で走らせることだ。クラウドMac mini上でlaunchdを使い、Git webhookに応答するか定期ポーリングするタスクを登録し、署名・公証・アップロードの3ステップを連続実行する。失敗時はスクリプトを黙って終了させるのではなく、メールやチケットシステムで担当者に通知する。ノードは専有物理マシンとして常時オンラインなので、一時的なコンテナのようにkeychainの状態や証明書が毎回初期化されることがなく、公証リクエストの成功率とキュー待ち時間もより安定する。
よくある質問
notarytoolでsecure timestampがないと言われたら?
署名時にタイムスタンプサーバーを使っていないのが原因です。codesign --timestamp --options runtimeで再署名してから再提出してください。
TestFlightの処理中表示が長いのは正常?
通常15〜60分で処理されます。2時間を超える場合はApp Store Connectのアクティビティで拒否メールを確認してください。ITSAppUsesNonExemptEncryptionの欠落による審査待ちが多い原因です。
クラウドMac miniでApple IDにログインする必要はある?
不要です。証明書をノードのキーチェーンに入れ、App Store Connect APIキーの.p8ファイルと環境変数を配置すれば無人運用できます。
ビルド専用に独享Mac miniが必要ですか?
東京・ソウル・シリコンバレーの5拠点に物理ノードを配置。1日単位でレンタル可能、申込から10分でVNC/SSH接続情報をお届けします。
今すぐMac miniをレンタル