ログの最後には「exit code 65」しか表示されず、原因が署名なのかコンパイルなのか分かりません。
最短の解決策は、修正コマンドを先に試すことではありません。完全なログとxcresultを保存し、Build、Test、Archive、exportArchiveのどこで止まったかを分けてから、Scheme、依存関係、Simulator、署名、実行環境の順に確認します。
[ SECTION_01 ] この記事の対象者
Xcodeの画面ではビルドできるのに、SSHやCIからxcodebuildを実行すると失敗する独立開発者向けです。
Simulatorを使った自動テストや、リモートMacでArchive、署名、無人運用のパッケージ作成を行う小規模チームにも適しています。
[ SECTION_02 ] exit code 65は原因ではなく、失敗を知らせる状態です
xcodebuild exit code 65は、処理が正常に完了しなかったことを示す集約的な終了状態です。これだけで署名エラーと断定することはできません。Appleのテスト結果確認方法でも、終了状態だけでなく結果情報とログを合わせて解釈する流れが示されています。Appleのテスト結果と解釈方法を基準に、最初に意味のあるエラーを探します。
まず、次の情報を同じジョブに保存します。
- 実行した完全なコマンド
- 標準出力と標準エラー
- Gitのコミット識別子
- 選択されたDeveloper Directory
- Build、Test、Archive、
exportArchiveの別 - Scheme、Configuration、SDK、Destination
xcresultまたはArchiveの保存先- プロジェクト名、Scheme名、Bundle ID、Team ID、証明書名、ユーザー名、パスを脱敏した記録
ログ末尾の「Command failed with exit code 65」は結果です。調査対象は、その前に出たerror:、テスト失敗、署名拒否、Destination不一致などです。
[ SECTION_03 ] 最初にタスクの段階を分けます
同じ終了状態でも、調べる場所は異なります。次の表で最初の分岐を作ると、署名関連の操作を無駄に実行せずに済みます。
| 実行段階 | 主に確認する対象 | 先に保存する成果物 | 優先する確認 |
|---|---|---|---|
| Build | ソース、依存関係、リンク、Run Script | ビルドログ、結果バンドル | 最初のコンパイル・リンクエラー |
| Test | テスト対象、Simulator、起動 | xcresult、テストログ |
テスト開始前か開始後か |
| Archive | Archive生成、署名準備 | .xcarchive、ログ |
ビルド完了後に止まったか |
exportArchive |
Distribution署名、Profile、Export設定 | Exportログ、生成物 | Archiveは存在するか |
結果バンドルの保存先を指定する方法や、テストアクションを段階的に扱う考え方は、AppleのXcode Cloudにおけるテストアクションの説明でも確認できます。CIの画面に表示された短いログだけを根拠に、プロジェクト全体を変更するのは危険です。
[ SECTION_04 ] Schemeと入口の違いがリモートMacで表面化します
ローカルではXcodeが適切なworkspaceとSchemeを自動的に選んでいても、コマンドラインでは明示した入口がそのまま使われます。依存管理を導入したプロジェクトでprojectを指定している場合、必要なターゲットや設定が読み込まれないことがあります。
確認する順番は次のとおりです。
- リポジトリ内にworkspaceとprojectのどちらを使うべきか確認する
- 実行対象のSchemeが存在するか確認する
- Schemeが共有設定としてコミットされているか確認する
- ConfigurationがDebug、Releaseなど想定どおりか確認する
- SDKとDestinationがリモートMacに存在するか確認する
- 対話シェルとSSH・CIの両方で同じ脱敏済みコマンドを実行する
Schemeの共有やカスタマイズは、Apple公式のScheme設定ガイドに沿って確認します。Xcodeの画面で成功し、SSHだけでxcodebuildが失敗する場合は、コードを変更する前にこの差分を比較します。
注意:Scheme名、作業ディレクトリ、環境変数はログに残りやすい情報です。公開用のログでは、プロジェクト名やユーザー名だけでなく、Bundle IDや端末識別子も置き換えてください。
[ SECTION_05 ] 依存関係とスクリプトは、キャッシュ削除より先に比較します
依存パッケージの解決、ソースのコンパイル、リソース処理、リンク、Run Scriptは、別々の故障層です。すべてを「キャッシュの問題」と扱い、最初にDerived Dataを削除する方法では、再現条件まで消えることがあります。
ローカルとリモートMacで、次の項目を比較します。
- 依存関係のロックファイルと取得済みバージョン
- プライベートリポジトリの認証方法
- スクリプトが呼び出すインタープリター
- 作業ディレクトリと相対パス
- CIで渡される環境変数
- 実行ユーザーの権限と利用可能なツール
Build Configurationをファイルで管理している場合は、AppleのBuild Configurationファイルに関する文書と実際のCI変数を照合します。修正後は、変更済みの作業ディレクトリで再試行するだけでなく、同じコミットをクリーンなチェックアウトへ取得して検証します。
[ SECTION_06 ] Simulatorとテスト開始後の失敗を分離します
テストが失敗した場合、アプリをビルドできていないのか、テスト製品を作成した後にSimulatorへ起動できていないのかで対応が変わります。DestinationのOSやデバイス識別子が実際の環境に存在するか、必要なサービスがグラフィカルなログインセッションなしでも起動できるかを確認します。
調査では、Build for TestingとTest without Buildingを分けます。前者でテスト製品を作成できるなら、ソースのコンパイルよりもテスト実行、Simulatorの選択、アプリ起動、タイムアウトを優先して調べます。テスト関連の環境変数はAppleのテスト環境変数リファレンスと照らし合わせ、CIだけで追加される値を洗い出します。
| 観測された状態 | 判断の軸 | 次の確認 |
|---|---|---|
| テスト製品の作成前に失敗 | コンパイル、リンク、依存関係 | BuildログとScheme |
| 製品作成後、テスト開始前に失敗 | Destination、Simulator、起動条件 | OS、識別子、セッション |
| テスト開始後に失敗 | テストコード、アプリ起動、タイムアウト | xcresultのテスト階層 |
| Archive後のExportで失敗 | 署名、Profile、Export設定 | Archiveの存在と署名資産 |
[ SECTION_07 ] Archiveと署名は4つの状態に分けて確認します
「App Storeへ出せない」という症状でも、コンパイル、Archive生成、コード署名、Exportのどこで止まったかを区別します。Archiveが生成済みなら、少なくともソースのコンパイルだけを再調査する必要はありません。
Keychainでは、署名証明書の表示だけでなく、対応する秘密鍵が同じ実行ユーザーから利用できるかを確認します。Provisioning Profileはアプリ識別子、Team、用途、証明書との整合性を確認します。AppleのDistribution署名コード作成に関する公式説明と、Provisioning Profileの技術ノートを参照してください。
SSHやバックグラウンドジョブでは、ログイン中のユーザーと同じKeychain状態になるとは限りません。Keychainのアクセス権変更、証明書の削除、署名資産の置換は影響範囲を記録し、元のProfileやバックアップへ戻せる場合だけ実施します。チームの署名証明書を同期する手順も、導入前の確認材料になります。
[ SECTION_08 ] 再現性でプロジェクトと環境を判定します
次のチェック項目をすべて満たせるか確認します。
- [ ] 同じコミットをクリーンな作業ディレクトリへ取得した
- [ ] 実行入口、Scheme、Configuration、SDK、Destinationを記録した
- [ ] Build、Test、Archive、
exportArchiveを別ジョブとして判定した - [ ] 標準出力、標準エラー、
xcresult、Archiveを保存した - [ ] 対話シェルとSSHまたはCIで同じコマンドを比較した
- [ ] 依存関係のロックファイルと認証状態を照合した
- [ ] 署名証明書、秘密鍵、Profileの対応を確認した
- [ ] セッション再接続またはホスト再起動後にも同じ処理を実行した
- [ ] 脱敏済みログで失敗段階を第三者が追える状態にした
固定したコミットで複数回失敗し、失敗がどのホストでも再現するなら、プロジェクトや依存関係の修正を優先します。特定ユーザー、SSHセッション、ホストだけで失敗するなら、実行環境を修正します。環境を作り直す前に、どの条件で直るのかを記録してください。
リモートMacを選ぶ場合は、単に接続できるかではなく、同じアカウントで署名資産を扱えるか、再接続後もジョブが継続できるか、ログと成果物を回収できるかで評価します。長期運用の比較では、Mac miniの購入条件を地域別に確認できる案内も、自購入とレンタルの判断材料になります。特定地域での購入を検討する場合は、日本向けのMac mini購入情報も併せて確認できます。
[ SECTION_09 ] FAQ
Xcodeでは成功するのに、コマンドラインだけ失敗する理由
Xcodeの画面とxcodebuildでは、Scheme、Configuration、作業ディレクトリ、環境変数、ログイン中のKeychainが一致しない場合があります。特に共有されていないSchemeや、SSHセッションから見えない署名情報は典型的な差分です。同じコミットと同じ入口を使い、対話シェルとの差分を比較します。
exit code 65が署名かコンパイルかを判断する方法
終了状態だけでは判定できません。コンパイル前後、Archive生成後、exportArchive実行中のどこで失敗したかをログで確認し、最初の有効なエラーを採用します。Archiveが存在するなら、コンパイルではなく署名やExport設定に調査範囲を絞れます。
リモートMacでSchemeが見つからない場合の確認方法
projectとworkspaceの指定を確認し、対象Schemeが共有設定としてリポジトリに含まれているかを確認します。さらに、リモートMacで同じコミットを使い、同じConfiguration、SDK、Destinationを指定します。ローカルだけで成功するなら、Schemeそのものではなく取得状態や実行環境の差分が疑われます。
CIで完全なログとxcresultを保存する方法
標準出力と標準エラーをジョブ成果物として保存し、テストやビルドの結果バンドルも明示的な保存先へ出力します。コミット、実行コマンド、Developer Directory、Scheme、Destinationを同じジョブのメタデータに残すと、再試行やホスト変更後の比較ができます。短いコンソール表示だけは証拠として不十分です。
現在の構成が個人PCや一時的なCIホストに依存している場合、GUIでは成功してもSSHではKeychain、Simulator、環境変数、作業ディレクトリが分離しやすいという欠点があります。ホスト変更で依存関係やSDKの状態が変わり、Archive後の署名だけ再現しないこともあります。
プロジェクトの原因を修正した後、同じコミットで安定した対照環境を用意するなら、NOVAKVMのリモートMacを一時的なBuild、Test、Archive検証や常駐のiOS打ち合わせ環境として比較できます。長期の固定負荷や物理デバイス接続が必要なケースでは自購入が適しますが、専用のMacをすぐに購入せず、隔離されたMacで原因を切り分けたいケースでは、NOVAKVMのMac利用方法を確認すると判断しやすくなります。
よくある質問
Xcodeでは成功するのに、コマンドラインだけ失敗するのはなぜですか?
Xcodeの画面で使われるScheme、Configuration、環境変数、ログイン中のキーチェーンと、SSHやCIの実行環境は一致しないことがあります。特に共有されていないScheme、異なる作業ディレクトリ、非対話セッションから見えない署名情報は、xcodebuildの失敗要因になります。
exit code 65は署名エラーを意味しますか?
いいえ。exit code 65はビルドやテストなどの処理が正常終了しなかったことを示す終了状態であり、単独では署名エラー、コンパイルエラー、Simulatorの起動失敗を区別できません。ログの最初の有効なエラーと、失敗したアクションを確認して判断します。
リモートMacでSchemeが見つからない場合はどう確認しますか?
まず、ローカルと同じリポジトリのコミットを取得し、projectではなくworkspaceを使うべき構成でないか確認します。そのうえでSchemeが共有設定になっているか、実行時のConfigurationとSDKが一致しているかを確認し、同じ脱敏済みコマンドを対話シェルと自動化セッションで実行します。
CIでexit code 65のログとxcresultを残す方法はありますか?
標準出力と標準エラーを保存するだけでなく、テストやビルドの結果バンドルを指定して保存します。ジョブ終了時にxcresultをアーティファクトとして退避し、実行コマンド、コミット、Xcodeの選択状態、Destinationも同じ記録に含めると、再実行後の比較が可能になります。