オファー

Xcode Product → Archive 全工程解説:CI がここで止まる理由

CI Xcode Archive · iOS リリース
2026-06-15 約 14 分

ローカルで Product → Archive を押せば Organizer から IPA を出せるのに、CI の xcodebuild archive だけが同じ段階で20 分ハング、タイムアウト、無言失敗——iOS チームなら一度は経験があるはずです。差は「Xcode の使い方」ではなく、Archive を観測可能なパイプラインとして扱えているかどうかにあります。

本稿では Build → Archive → Sign → Export を段階ごとに分解し、メニュー操作を xcodebuild でどう再現するか、CI で止まりやすい 7 パターンの症状—対処表までまとめます。

最初に押さえるポイント

  1. Archive ≠ Build:Archive は Release + Any iOS Device 固定で .xcarchive を生成。Debug で通っていても Archive は落ちる。
  2. CI の詰まりは 4 段——Scheme/設定コンパイルキャッシュ署名とキーチェーンexportArchive。ログで段を特定してから手を入れる。
  3. xcodebuild archive はメニュー Archive と等価だが、CI では -scheme-configuration Release-destination 'generic/platform=iOS' を明示必須。
  4. 「動かない」の多くはキーチェーン待ちプロビジョニング取得待ちメモリ swap——xcodebuild 自体が死んでいるわけではない。
  5. クラウド Mac + 持続 DerivedData + 専用キーチェーンは、ホステッド Runner の「毎回コールド Archive」より桁違いに安定しやすい。
Xcode Archive と CI パイプラインの概略
ローカル Archive には GUI のフィードバックがある。CI では同じチェーンをログで段分けしないと「Job が 40 分」しか見えない。

1. Archive と Build の違い

CI 失敗を「xcodebuild が落ちた」と片付けるチームは多いですが、⌘B BuildProduct → Archive は別ルートです。

観点 Build(⌘B) Archive
典型 Configuration Debug(日常開発) Release(App Store / TestFlight)
Destination シミュレータ or 接続中の実機 Any iOS Device (arm64)
成果物 DerivedData 内の .app .xcarchive + IPA エクスポート可
署名 開発証明書で通ることも 配布証明書 + プロビジョニング必須
最適化 低く、速い フル最適化でコンパイル時間が大幅増

だから「ローカル 3 分で通るのに CI Archive が 20 分で失敗」は矛盾しません。ローカルが Debug + シミュレータ、CI が Release + 実機アーキ + フル署名チェーン、というケースが典型です。比較対象を揃えてから最適化を議論してください。コンパイル遅延そのものは xcodebuild が CI でローカルの 2〜3 倍遅い理由 を参照。

2. メニューから CLI:Product → Archive の等価操作

Xcode GUI で Product → Archive を押したとき、裏側ではだいたい次が走ります。

  1. Shared Scheme を選択し、Configuration は Release
  2. Destination を Any iOS Device に(シミュレータ不可);
  3. xcodebuild archive を実行し、ARCHIVE_PATH に出力;
  4. (任意)Organizer の Distribute Appxcodebuild -exportArchive に相当。

CI でよく使う最小 Archive コマンド(パスはプロジェクトに合わせて置換):

xcodebuild archive \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
  CODE_SIGN_STYLE=Manual \
  DEVELOPMENT_TEAM=XXXXXXXXXX \
  | tee archive.log

漏れやすい 3 点:

  • -destination 'generic/platform=iOS':省略するとシミュレータが選ばれ、Archive 失敗や挙動ブレの原因に。
  • -archivePath:書き込み可能なパス必須。ホステッド Runner の一時ディレクトリ権限で「コンパイル成功→書き込み失敗」が起きる。
  • Scheme は Shared で Git 管理:ローカルだけの Scheme は CI checkout 後に scheme not found になる。

公式:Apple — Building your appDistributing your app

3. 四段階パイプライン(一覧)

┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ 1. Resolve   │ →  │ 2. Archive   │ →  │ 3. Sign      │ →  │ 4. Export    │
│ SPM/Pods     │    │ Release 全量ビルド│    │ 証明書/プロファイル│    │ IPA / Upload │
│ DerivedData  │    │ → .xcarchive │    │ キーチェーン     │    │ TestFlight   │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
     pod install          xcodebuild           codesign           exportArchive
     5–15 分がち          archive 8–25 分      無言ハング多発      plist 設定ミス多発

CI が「ここで止まった」と言うとき、まずログ最終行がどの段かを確認。CompileSwift ならコンパイル、CodeSign なら署名、exportArchive ならエクスポート設定。段を混ぜてキャッシュ全消しを繰り返すと時間だけ溶けます。

4. 段階 1:Resolve & Compile(CI が遅い理由)

Archive 前の依存解決とコンパイルは、ローカルは「温まった」状態、CI は「冷えた」状態になりがちです。

  • CocoaPods / SPM:CI で毎回 pod install すると Installing … だけで 10 分。Archive 前に時間が消える。→ Flutter iOS CI のキャッシュ伝播
  • DerivedData コールド:Release フルビルド + キャッシュなしで Pod ネイティブコードを全部再コンパイル。自前 Runner で DERIVED_DATA_PATH を SSD 固定にすると 2 回目 Archive が半分になることも。
  • メモリ圧:Archive のピークは Debug より高い。16GB で Job 並列すると swap し、CPU 低いのに進まない。→ Runner メモリと swap 対策

判断基準:ログが CompileC / SwiftCompile で、触っていないサードパーティを大量にコンパイルしているなら、署名より先にキャッシュを直す。

5. 段階 2:Archive(.xcarchive 生成)

xcodebuild archive 成功後、-archivePath に次の構造があるはずです。

MyApp.xcarchive/
  Info.plist
  Products/Applications/MyApp.app
  dSYMs/...

よくある失敗:

  • Scheme の Archive アクションにターゲット未設定:ローカル別 Scheme ではビルドできても、CI 指定 Scheme の Archive が空。
  • Extension 署名漏れ:本体 App は通るが Notification Service Extension で落ち、Archive 全体失敗。
  • ビルド番号衝突:CI で CFBundleVersion 未インクリメント。アップロード段階で爆発し、Archive 遅延と誤認されがち。

検収:Archive 直後に ls -la "$ARCHIVE_PATH"plutil -p "$ARCHIVE_PATH/Info.plist"ApplicationProperties を確認。export 失敗後に Archive を疑うより早い。

6. 段階 3:Code Sign(CI 最大の落とし穴)

ローカル Archive では Xcode がキーチェーン許可ダイアログを出し、「常に許可」で通る。無人 CI にはその UI がない

  • errSecInternalComponentUser interaction is not allowed:ログインキーチェーンの秘密鍵に GUI 承認が必要。
  • プロファイル期限切れ / Bundle ID 不一致CodeSignProvisioning profile … doesn't match
  • -allowProvisioningUpdates の誤用:Apple ID 対話ログイン待ちで Headless が永久待機。
  • キーチェーン未解錠:Job 冒頭の security unlock-keychain / set-key-partition-list 欠落。

クラウド Mac / 自前 Runner では CI 専用キーチェーンファイル(ログインキーチェーンではない)を作り、配布証明書 + 秘密鍵をインポート、Job 開始時に解錠してデフォルトに。詳細は クラウド Mac iOS CI:codesign とキーチェーン境界

# Job 冒頭(パスワードは CI Secret)
KEYCHAIN=$RUNNER_TEMP/ci-signing.keychain-db
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security import dist.p12 -k "$KEYCHAIN" -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security list-keychains -s "$KEYCHAIN" login.keychain-db

fastlane match や Xcode Cloud でも原則同じ:CI プロセスが秘密鍵に無操作で触れること。match ドキュメント:fastlane match

7. 段階 4:Export IPA(exportArchive)

Archive は .xcarchive まで。TestFlight 配布には Export が別段。Organizer の Distribute App は次に相当します。

xcodebuild -exportArchive \
  -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
  -exportPath "$RUNNER_TEMP/export" \
  -exportOptionsPlist ExportOptions.plist

ExportOptions.plistmethod がエクスポート種別(app-storead-hocdevelopment 等)を決めます。CI で多い問題:

  • method とプロファイル種別の不一致:development プロファイルなのに app-store
  • teamID / signingCertificate 欠落:Manual 署名で export 段階初出エラー→ Archive 遅いと誤解。
  • upload を同じ step に混在altool / Transporter のネット待ちが「Archive タイムアウト」に計上される。

archiveexportupload を 3 step に分け計測。アップロードは xcrun altool --upload-app 等で Archive から切り離す。

8. CI が止まる 7 類型早見表

症状 / ログキーワード 段階 最初の一手
長時間ログ更新なし、CPU 低い Sign キーチェーン解錠確認;GUI 要 provisioning 自動更新を無効化
scheme 'Foo' not found 前置 Scheme を Shared にしてコミット;CI で -list 照合
No profiles for … were found Sign Bundle ID・プロファイルのリポジトリ同梱 or match 取得成功を確認
未変更 Pod への大量 CompileC Compile DerivedData 持続化;Job 内 clean 回避
pod install > 8 分 Resolve ios/Pods キャッシュ;pod install --deployment
プラットフォームが Job を kill(スタックなし) 全体 timeout 延長;swap / ディスク満杯(ディスク対策
exportArchive failed + plist 関連 Export export 単体実行;ExportOptions.plist の method 確認

オンコールではログを末尾から最初の error: を探し、上表と照合——flutter clean 全消しより 1 時間短いことが多いです。

9. Runbook:段階計測してから直す

  1. ローカルで Release Archive 再現:Product → Scheme → Edit Scheme → Archive を Release;Destination は Any iOS Device。ローカルで落ちるなら CI を回す意味が薄い。
  2. CI に 4 段タイマーpod installarchiveexportArchiveupload、各 step summary に記録。
  3. 生ログを残す:過度なログ整形は署名失敗の CodeSign 行を消す。
  4. Xcode バージョン固定xcode-select -s でパス固定;.xcode-version や workflow の macos-15 と整合。
  5. 48 時間検収:同一 commit で Archive を 2 連続。2 回目が明らかに速ければキャッシュ命中。まだ遅いならメモリとディスク。

リリース週のビルド機増設は リモート Mac 発版スプリント短期レンタル と相性が良い。ホステッド Runner で Archive タイムアウトを硬く耐えるより、専有 Mac を足す方が早い場面も多い。

10. GitHub Actions テンプレート(コピー可)

jobs:
  archive-ios:
    runs-on: [self-hosted, macOS, ios]  # または macos-14 ホステッド
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v4

      - name: Install pods
        run: pod install --deployment
        working-directory: ios

      - name: Archive
        run: |
          set -o pipefail
          xcodebuild archive \
            -workspace ios/MyApp.xcworkspace \
            -scheme MyApp \
            -configuration Release \
            -destination 'generic/platform=iOS' \
            -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
            | tee archive.log
        env:
          DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer

      - name: Export IPA
        run: |
          xcodebuild -exportArchive \
            -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
            -exportPath "$RUNNER_TEMP/export" \
            -exportOptionsPlist ios/ExportOptions.plist

自前 Runner のラベルルーティング、launchd 常駐、CI ユーザー分離は Mac mini 自前 Runner 構築Flutter + GitHub Actions アーキテクチャ を参照。

11. まとめと次の一手

Product → Archive は「もう少しビルド」ではなく、Release フルビルド + アーカイブ + 配布級署名 + 任意エクスポートの一連です。CI がここで止まるのは Xcode 壊れが稀で、Debug の感覚を Release に当てはめる署名をローカル GUI 専用と思い込むarchive と export を 1 つのブラックボックス step にする——この 3 つが大半です。

実行順:ローカル Archive 合格 → CI 4 段計測 → 専用キーチェーン + 持続キャッシュ → その後に増機・スペックアップ。Apple プラットフォームチームにとって、クラウド Mac 自前 Runner はホステッド cache 調整より、Archive が求める安定したウォームパスを作れる点で総時間を削りやすいです。

クラウド Mac で Archive 全工程を通す

M4 専有ベアメタルは xcodebuild archive、専用署名キーチェーン、持続 DerivedData に向いています。日払いで Archive 時間と安定性を検証し、問題なければ月額の常駐 Runner へ。

Mac レンタルプラン · M4 スペック · 開通検収チェックリスト