xcodebuild Exit Code 65が返ったら、まず完全なコマンド、原始ログ、xcresultを保存し、最初に失敗した動作を特定してください。65だけに対応する万能な修正コマンドはありません。Scheme、依存関係、実行先、署名、リモートMacの環境を順番に比較し、ノード再構築は状態を安定して再現できない場合に限るべきです。

この手順は、ローカルのXcodeでは成功するのにリモートMac CIだけ失敗するアプリ開発者向けです。共有macOSビルドノードを管理するDevOpsエンジニアや、コンパイル・テスト・署名・アーカイブの責任範囲を分けたいリリース担当者にも適しています。

まず30分で行う証拠保全:終了コードと本当の失敗を分ける

xcodebuildの終了状態が65でも、それは原因名ではありません。ログ末尾のCIメッセージは、コンパイルエラー、テスト起動失敗、署名エラーなどをまとめて表示している場合があります。Appleのxcodebuildコマンドライン技術資料でも、実際の操作はScheme、アクション、SDK、実行先などの指定で決まることが示されています。

最初に次の情報を1つの障害記録へ固定します。

  • 実行したxcodebuildコマンド全体
  • buildtestarchiveのどのActionか
  • 選択されたXcodeとDEVELOPER_DIR
  • CIで実行したアカウントと作業ディレクトリ
  • 失敗時刻、コミット、実行先の識別子
  • 生ログと.xcresultの保存場所

xcresultは捨てずに残してください。テスト結果には、失敗したテスト、対象デバイス、実行セッション、関連ログが含まれるためです。テスト結果の読み取りに関する公式資料を基準に、最後のExit Code 65ではなく、最初のerror:、失敗したScript、依存関係の解決停止、またはテスト開始失敗を探します。

ローカルのXcodeでは成功したのにCIで失敗するのはなぜですか。
ローカルとCIで、Schemeの共有状態、実行アカウント、鍵へのアクセス、作業ディレクトリ、SDK、Simulatorの実行先が異なるためです。ローカルの画面で成功した事実は、CIの非対話セッションが同じ条件を持つ証拠にはなりません。

第一の比較:Workspace・Scheme・Build Settingsは同じか

CIが.xcodeprojを指定しているのに、ローカルでは.xcworkspaceを使っているなら、依存関係やBuild Phaseが一致しません。まずプロジェクト形式をそろえ、Schemeが共有設定になっているかをリポジトリ内で確認します。

利用可能なSchemeは、対象のプロジェクトやWorkspaceに合わせて明示的に列挙します。

xcodebuild -list -workspace <WORKSPACE_PATH>
xcodebuild -showBuildSettings \
  -workspace <WORKSPACE_PATH> \
  -scheme <SCHEME_NAME>

次に、ローカルで成功したConfiguration、SDK、Destination、署名関連の設定をCIの実行コマンドと比較します。コマンドラインで渡したBuild Settingsはプロジェクト側の設定より優先されることがあるため、-configurationSDKROOTCODE_SIGN_STYLEなどをラッパースクリプトが上書きしていないか確認してください。Build Settingsの優先順位に関するAppleの説明を照合すると、GUI上の値と実行時の値を分けて確認できます。

ここでは、いきなりDerivedDataを削除しません。最小構成で同じSchemeと実行先を指定し、設定差分が原因かを確認します。設定を変えた場合は、変更前のコマンドと変更後のコマンドを両方保存してください。

第二の比較:Swift PackageとRun Scriptはどこで止まったか

依存関係の解決が最初の失敗なら、コンパイラーを疑う前にPackage.resolvedの内容と取得権限を確認します。プライベートリポジトリをSSHで取得する構成では、CI実行アカウントの秘密鍵、known_hosts、SSHエージェント、読み取り権限が、ログインユーザーと一致しないことがあります。

確認する項目は次のとおりです。

  1. Package.resolvedが対象コミットと一緒に保存されているか確認する。
  2. CIアカウントで依存先の認証が成立するか確認する。
  3. gitのパス、SSH設定、ホスト鍵の検証結果をログへ記録する。
  4. 依存解決の開始時刻と停止時刻を、生ログと突き合わせる。
  5. 解決後に初めてコンパイルが始まっているか確認する。

Appleの継続的インテグレーションにおけるSwift Packageの公式ガイドは、再現可能な依存関係とCI環境でのシステムGit利用を扱っています。依存取得の失敗を「Xcodeのビルドエラー」として処理すると、無関係なキャッシュ削除を繰り返すことになります。

Run Script Phaseで停止した場合は、Scriptの終了状態、Shell、現在のディレクトリ、環境変数、入力ファイルの存在を調べます。生成物がまだ作られていないのに後段のScriptが読み込もうとしていないか、ローカルだけに存在するツールやパスを参照していないかも確認します。

第三の比較:コンパイル失敗か、Simulatorのテスト失敗か

Simulatorが起動しないと、アプリのコンパイル自体は成功していてもtest Actionが失敗し、結果としてxcodebuild Exit Code 65になることがあります。したがって、ログを「コンパイル」「起動」「テスト実行」の3段階に分けて読みます。

  • コンパイル前後で失敗しているなら、ソース、Package、Build Settingsを確認する。
  • アプリのビルド後に実行先を準備できないなら、Schemeの対応プラットフォーム、インストール済みRuntime、Destinationを確認する。
  • テストセッション開始後に失敗するなら、xcresultのテスト対象、デバイスログ、起動エラーを確認する。

Simulatorが手動で開いたことだけでは、CIのテストチェーンが利用可能だとはいえません。CIアカウント、指定したDestination、テスト対象の対応状況が同じ条件で成立している必要があります。修正後は、失敗したときと同じDestinationで再実行し、別の端末や別Schemeによる成功を代替証拠にしないでください。

DerivedDataを削除すればExit Code 65は直りますか。
原因が壊れた生成物や古い中間ファイルだと確認できた場合に限り、対象WorkspaceのDerivedDataだけを退避または削除します。削除は診断情報と再現条件を失わせる可能性があるため、先にパスとログを保存し、全ノード共通の対処にはしないでください。

Simulatorの起動失敗で65になるのはなぜですか。
test Actionはビルドだけでなく、指定された実行先へのインストール、起動、テストセッションの開始まで必要とするためです。xcresultでテストセッションが始まっていないなら、コンパイルエラーとSimulator障害を同じ修正として扱わないでください。

第四の比較:署名エラーなら、アーカイブと書き出しを分ける

署名は、最初のエラーが証明書、秘密鍵、Provisioning Profile、Team設定、Keychainアクセスを示す場合だけ調査します。署名設定を無効化して先へ進む方法は、配布用アーカイブの一般的な解決策ではありません。

CI実行アカウントが、ログイン中のGUIユーザーと同じ証明書の秘密鍵へアクセスできるか確認します。自動署名がローカルで成功していても、リモートMacの無人実行環境に同じ認証情報やプロファイルがあるとは限りません。

復旧テストは一度に全部行わず、次の順番で分けます。

  • 同じSchemeで通常のbuildを実行する。
  • 同じ成果物条件でarchiveを実行する。
  • 保存したアーカイブを対象にexport処理を実行する。
  • 各段階のログ、署名情報、xcresultを別々に保存する。

デバッグ情報を含めたビルドに関するAppleの資料も参照し、失敗時に診断に必要な情報が残る構成にします。Keychainを削除したり証明書を入れ直したりする前に、どのアカウントがどのデジタルIDを見られないのかを特定してください。

失敗から復旧までのマイルストーンを固定する

同じコマンドを1回再実行して成功しても、CIが復旧したとは判断しません。次の再検証マトリクスを、同じリポジトリと同じ実行先で記録します。

マイルストーン1:原命令の失敗

完全なコマンド、Xcodeの選択状態、実行アカウント、作業ディレクトリ、ログ、xcresultを保存します。CIプラットフォームが末尾に付けた要約だけは証拠として扱いません。

マイルストーン2:最小命令の結果

対象を1つのWorkspace、1つのScheme、1つのActionへ絞ります。依存解決、コンパイル、テスト起動のどこまで進むかを確認し、原因候補を減らします。

マイルストーン3:修正後の反復

設定、依存関係、Simulator、署名のいずれかを1項目だけ変更し、同一条件で再実行します。修正前後のログを比較し、最初の失敗動作が消えたか、新しい失敗へ移っただけではないかを確認します。

マイルストーン4:再起動後の確認

DEVELOPER_DIR、Command Line Tools、アカウント権限、ディスク状態、作業領域の残留物を再起動後にも確認します。Command Line Tools設定の公式資料を使い、GUIで選択されたツールとCIが実際に呼び出すツールを区別します。

修復か隔離かを決める条件リスト

次の条件を上から確認し、該当する分岐だけを実行してください。チェックが付かない項目を推測で処理するのではなく、原命令、最小命令、修正後、再起動後の結果をそろえてから判断します。

  • [ ] 最初の失敗が毎回同じ設定値で発生するなら、ノードを再構築せず、SchemeまたはBuild Settingsを修正します。
  • [ ] 依存取得だけがCIアカウントで失敗するなら、鍵、認証、known_hosts、実行ユーザーのGit設定を直します。
  • [ ] ビルドは成功し、同じDestinationのテストだけが失敗するなら、Simulator Runtimeとテスト対象を分離して調べます。
  • [ ] 署名の秘密鍵だけが見えないなら、証明書を削除せず、Keychainアクセスと実行アカウントを修正します。
  • [ ] 再起動後にXcode選択、権限、作業領域の状態が変わるなら、ノードを隔離してから再構築を判断します。
  • [ ] すべての条件で同じプロジェクト設定が失敗するなら、ノード交換ではなくリポジトリ、Scheme、依存関係の再現条件を再確認します。

自分で管理するMac miniサーバーは物理機器を確保できる一方、故障交換、常時稼働、遠隔復旧、ツールチェーンの固定を自分で担う必要があります。LinuxやWindowsの既存CIは運用に慣れていても、Xcode、署名、Simulatorを同じ条件で維持できず、物理インターフェースやmacOS専用工程を代替できません。仮想macOS環境も、実機での署名やテスト実行を検証する用途では条件差が残ります。

そのため、最小再現が終わった段階で、完全な権限を持ち、必要なら初期状態へ戻せるリモートMacを使って同じリポジトリとコマンドを再確認する方法が現実的です。VMSPINの日本語向けリモートMac利用案内料金プランを確認し、短期の再検証に向くか、継続CIノードとして分離すべきかを、ログの再現性と運用期間から判断してください。

CIの現在の構成をそのまま使い続けると、macOS専用ツールチェーンを別のOSで補えないこと、署名鍵を共有環境へ安全に渡しにくいこと、障害時に同一条件を再現しにくいことが負担になります。まず一時的なリモートMacで同一条件を検証し、エラーがノード変更で消える場合にだけ、レンタル期間、環境分離、継続実行用ノードへの移行を検討するのが安全です。

VMSPINのリモートMac申込みページでは、必要な検証期間と運用形態を確認できます。根因がプロジェクト設定にある場合は既存環境を直し、ノード側にある場合は、再現可能なMac環境へ段階的に切り替えてください。