メインコンテンツへスキップ

Capacitor の CI/CD: よくある落とし穴と解決策

Capacitor ビルドを壊すCI/CDの落とし穴:iOS署名、macOSランナー、Xcode 26、古いcap sync、バージョンコード、ストア審査のリジェクト、それぞれの修正方法。

Code の署名アイデンティティと Capacitor パイプラインで失敗する CI ステップ

ほとんどの Capacitor CI/CD 失敗は、同じ少数の原因に集約されます。iOS の code 署名、macOS ツールチェーンの欠落や古さ、ネイティブプロジェクトに反映されない Web ビルド、ビルド番号の再利用、そしてアップロード時に初めて発覚するストア要件です。それぞれに既知の原因と、一度適用すれば解決する修正方法があります。本ガイドでは、パイプラインのステージごとに落とし穴を分類し、表示されるエラー、その原因、そして変更すべき点を解説します。

パイプラインをゼロから構築する場合は、まず Capacitor アプリの CI/CD セットアップ を読み、その後このリストを使って強化してください。

ステージ 1: Web レイヤーのビルド

落とし穴: ネイティブアプリが古い Web ビルドを配布している

症状: CI は成功し、アプリもインストールされますが、表示されるのは前日の UI です。

原因: cap sync は、その時点で webDir にあるものをコピーします。パイプラインが実行された場合 cap sync Webビルドの前に、またはWebビルドが別のフォルダに書き出す場合 webDir における capacitor.config.tsネイティブプロジェクトに古いファイルが混入します。

解決策: 常にこの順序でステップを実行し、出力フォルダが空の場合は失敗させます。

bun install --frozen-lockfile
bun run build
test -f dist/index.html || { echo "web build missing"; exit 1; }
bunx cap sync

必ず webDir バンドラーの出力(dist Vite の場合: www Ionic付きAngularの場合、 build 一部のReact構成の場合)。

落とし穴: 設定に開発サーバーURLが残っている

症状: リリースビルドで白画面が表示されたり、読み込みを試みたりする http://192.168.x.x:5173.

原因: server.url における capacitor.config.ts ライブリロード用に設定され、コミットされてしまった

解決策: 絶対にコミットしない server.url。CIでは設定されない環境変数から読み込む:

import type { CapacitorConfig } from '@capacitor/cli'

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'Example',
  webDir: 'dist',
  ...(process.env.LIVE_RELOAD_URL && {
    server: { url: process.env.LIVE_RELOAD_URL, cleartext: true },
  }),
}

export default config

落とし穴: 不適切なタイミングで焼き込まれる環境変数

症状: 本番アプリがステージングの API と通信してしまう

原因: Vite、webpack、Angular はビルド時に環境変数をインライン化します。その時点で存在する値が bun run build 実行されるのはバイナリ内のもの、そして同じジョブからビルドされたすべての live update です。

解決策: Web ビルド前に、ジョブごとに環境固有の変数を設定し、staging と production 用にそれぞれ別の bundle をビルドしてください。後から切り替えることはできません。 cap sync.

落とし穴: lockfile のドリフト

症状: CI 上のプラグインバージョンがローカル環境と異なり、シンボル不足でネイティブコンパイルが失敗します。

解決策: lockfile をコミットし、以下でインストールします bun install --frozen-lockfile (または npm ci)。 @capacitor/core, @capacitor/ios, @capacitor/androidや @capacitor/cli を同じバージョンに固定してください。バージョン不一致はネイティブエラーの頻繁な原因です。詳細は Capacitor バージョン不一致エラーの修正.

ステージ2: ネイティブツールチェーン

落とし穴: Node、JDK、または Xcode のバージョン違い

症状: Unsupported class file major version, The engine "node" is incompatible、または SDK 機能に関する Xcode エラー。

原因: ホスト型ランナーのイメージは変更され、Capacitor 8 には明確な最低要件があります。

ツール Capacitor 8 の要件
Node.js 22 以降
JDK 21
iOSプロファイリング 26 以降
iOS デプロイメントターゲット 15.0
Androidでは minSdkVersion / targetSdkVersion 24 / 36

解決策: 信頼せず、パイプライン内ですべてのバージョンを明示的にピン留めする latest:

- uses: actions/setup-node@v6
  with:
    node-version-file: .nvmrc
- uses: actions/setup-java@v4
  with:
    distribution: temurin
    java-version: '21'
- uses: maxim-lobanov/setup-xcode@v1
  with:
    xcode-version: '26'

落とし穴: Apple がもはや受け入れない Xcode でビルドする

症状: サポートされていない SDK でビルドされたというメッセージとともに、アップロードが失敗します。

原因: 2026 年 4 月 28 日以降、App Store Connect は Xcode 26 と iOS 26 SDK を必須とします。セルフホスト Mac や古いランナーイメージは、まだ Xcode 16 がデフォルトです。

解決策: 選択 macos-26 イメージ、またはランナーに Xcode 26 をインストールしてください。詳細は Appleによる Capacitor アプリ向けのXcode 26要件.

落とし穴: CocoaPods と SPM の混同

症状: xcodebuild: error: 'App.xcworkspace' does not exist、または pods が見つからない。

原因: 新しい Capacitor 8 プロジェクトは Swift Package Manager を使用し、 ios/App/App.xcodeproj をビルドします。古いプロジェクトは CocoaPods を使用し、 ios/App/App.xcworkspace その後 pod install をビルドします。古いチュートリアルからコピーしたパイプラインはワークスペースを前提としています。

解決策: プロジェクトで使用されているものを確認し、適切なファイルをビルドしてください。移行する場合は、 Capacitor アプリを SPM に移行する方法.

落とし穴: Gradle アップグレード後に Android プラグインのビルドが壊れる

症状: Namespace not specified, package attribute is deprecated、またはプラグインによるエラー build.gradle Android Studio を更新した後。

解決策: Android Gradle Plugin のバージョンを固定する android/build.gradle、ブランチでアップグレードし、先にプラグインを更新してください。具体的なエラーについては AGP 9 で Capacitor プラグインのビルドエラーを修正.

ステージ3: Code の署名

落とし穴: iOS の署名は Mac 上でしか動作しない

症状: No signing certificate "iOS Distribution" found, No profiles for 'com.example.app' were found、または errSecInternalComponent.

原因: Mac のキーチェーンには証明書が保存され、Xcode がプロファイルをダウンロードしてくれます。CI ランナーにはこれらがなく、非対話セッションではキーチェーンがロックされています。

解決策: 証明書を一時のアンロックされたキーチェーンにインポートし、Xcode 16 以降が参照する場所にプロファイルをインストールします:

security create-keychain -p "$KEYCHAIN_PASSWORD" ci.keychain
security set-keychain-settings -lut 21600 ci.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" ci.keychain
security import dist.p12 -k ci.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" ci.keychain
security list-keychains -d user -s ci.keychain login.keychain

PROFILES="$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"
mkdir -p "$PROFILES"
cp app.mobileprovision "$PROFILES/"

アイデンティティ名を使用します Apple Distribution、レガシーの iOS Distributionではなく。fastlane の setup_ci と match がこれを自動化します。Capgo Build は証明書とプロファイルを環境変数として受け取り、独自のマシンでキーチェーンの処理を行うため、Linux ジョブは security.

落とし穴: 期限切れまたは失効した証明書

症状: 1年間安定して動いていたpipelineが、ある夜突然失敗する。

原因: Appleの配布証明書とプロビジョニングプロファイルは1年で期限切れになります。チームメンバーがXcodeで新しい証明書を作成すると、CIで使用しているプロファイルが無効になることもあります。

解決策: 期限日をチームカレンダーに記録し、CI用に共有の配布証明書を1つ維持し、ビルド前に確認しましょう。 bunx @capgo/cli@latest build prescan --platform ios アップロード前に、証明書の期限、パスワード、プロファイルの対応関係を確認します。

落とし穴: Apple IDのログインと2要素認証のprompt

症状: fastlaneが6桁のcodeを待ってハングする。

解決策: App Store ConnectのAPIキーを(.p8、キーID、イシューアID) を使用します。Apple IDとパスワードは不要です。2要素認証も不要で、App Managerにスコープを限定できます。正しいキーでも認証が失敗する場合は、ランナーの時刻を確認してください。トークンはローカル時刻で署名されており、タイムスタンプがずれているとAppleによって拒否されます。

落とし穴: デコードできない base64 シークレット

症状: MAC verification failed, invalid keystore format、または base64: invalid input.

原因: base64 値をコピーした際に改行が追加されたり、 .p12 OpenSSL 3 のデフォルト設定で作成され、macOS が読み取れないもの。

解決策: 1行でエンコードし、 -legacy を作成する際は .p12 OpenSSL 3 で:

base64 -i dist.p12 | tr -d '\n' > dist.p12.b64
openssl pkcs12 -export -legacy -inkey key.pem -in cert.pem -out dist.p12

落とし穴: 失われた Android keystore

症状: 誰もkeystoreを持っていないため、アップデートに署名できません。

解決策: Play App Signingでは、keystoreはアップロードキーであり、Play Consoleのサポートに新しいキーの登録を依頼できます。keystoreはCIのシークレットとオフラインバックアップに保存してください。1台のラップトップだけに置かないでください。 Android keystore ジェネレーター ゼロから始める場合は、新しいものを作成します。

ステージ4: パイプライン設計

落とし穴: 全プルリクエストでのネイティブビルド

症状: 遅いPRチェックと高額なmacOS分課金。

原因: ホスト型macOSランナーでのiOSビルドは、ほとんどのCIプランで最も高価な分課金ですが、多くのコミットはJavaScriptのみを変更します。

解決策: すべてのPRでlint、テスト、Webビルドを実行します。リリースタグまたはマージ時にネイティブビルドを実行します main。PRプレビューでは、バイナリをビルドする代わりに、Capgo チャネルにWebバンドルを配信します(詳細は Capacitor アプリ向け CI/CD プラットフォームの比較.

落とし穴: iOSとAndroidを順次ビルドする

解決策: マトリックスを使用し、両プラットフォームを並列でビルドし、 fail-fast: false を設定して、iOSの署名問題で正常なAndroidビルドがキャンセルされないようにします。

strategy:
  fail-fast: false
  matrix:
    platform: [ios, android]

落とし穴: キャッシュなし、または誤ったキャッシュ

症状: すべてのビルドでGradle依存関係とCocoaPodsを最初からダウンロードするか、古いキャッシュからステージングビルドに本番設定が含まれてしまう。

解決策: キャッシュ ~/.gradle/caches, ~/.gradle/wrapperや ios/App/Pods lockfile をキーにしています。キャッシュは環境ごとに分割してください。Capgo Build では、アプリごとのビルドキャッシュを次のように分割できます --cache-key prod と --cache-key staging、または --no-cache でスキップしてクリーンビルドできます。

落とし穴: モノレポのパス

症状: could not find capacitor.config 、またはネイティブプロジェクトにプラグインがない場合。

解決策: Capacitor コマンドはアプリパッケージから実行し、ツールはホイスティングされた node_modules. Capgo CLI は次を受け付けます --path と --node-modules これについて。

ステージ5: ストアへの提出

落とし穴: ビルド番号の再利用

症状: iOSでは「バンドルバージョンは以前アップロードされたバージョンより高くする必要があります」、Google Playでは「バージョン code はすでに使用されています」というエラー。

解決策: CIで番号を生成します。fastlaneを使う場合は、最新のTestFlightビルドを読み取って1を加算します。Capgo Buildではこれがデフォルトです: App Store Connectから最新のビルド番号を取得するか、最大 versionCode Google Playから取得して増分します。

落とし穴: TestFlightでビルドが停滞

症状: アップロードは成功するが、テスターがビルドを見ることができない。

原因: エクスポートコンプライアンスの回答が不足しています。

解決策: を1か所で宣言します ios/App/App/Info.plist 標準的な暗号化のみを使用する場合:

<key>ITSAppUsesNonExemptEncryption</key>
<false/>

落とし穴: プライバシーマニフェストの拒否

症状: 必須の理由 API 宣言が不足しているという Apple からのメール (ITMS-91053)。

解決策: を追加します PrivacyInfo.xcprivacy アプリターゲットに追加し、独自のマニフェストを同梱するプラグインを更新します。詳細は iOSアプリ向けプライバシーマニフェストガイド.

落とし穴: 誤ったアーティファクトタイプ

解決策: Google PlayにはAABが必要です (bundleRelease), APK ではありません。 bundleRelease 署名設定がなくても成功してしまい、 release Play Store で拒否される未署名の AAB が生成されるので、まず signingConfigs.release における android/app/build.gradle 設定してください。iOS では、開発用や ad hoc の IPA ではなく、App Store 用のエクスポートが必要です。ビルドステップのエクスポート方法を確認してください。

ステージ 6: Live Updates

落とし穴: 新しい Native Build が必要な live update をリリースしてしまう

症状: OTAアップデート後、インストール済みのバイナリに存在しないプラグインメソッドを呼び出すため、アプリがクラッシュします。

原因: web bundle が、ユーザーのデバイス上のアプリにコンパイルされているものよりも新しいバージョンのプラグインに依存している場合に発生します。

解決策: パイプラインに判断させる。 build needed ネイティブ依存関係がチャネル上のライブ状態と一致する場合に 0 で終了し、新しいバイナリが必要な場合に 1 で終了します:

if bunx @capgo/cli@latest build needed com.example.app --channel production; then
  bunx @capgo/cli@latest bundle upload com.example.app --channel production
else
  bunx cap sync
  bunx @capgo/cli@latest build request com.example.app --platform ios
  bunx @capgo/cli@latest build request com.example.app --platform android
fi

また、以下のファイルが変更された場合もネイティブパスを強制します ios/, android/、または capacitor.config.* 。完全なパターンは live update か Native Build を自動選択にあり、互換性ルールは ネイティブ互換性.

最も多くの落とし穴を解消する修正

If you only change one thing, move iOS compilation and signing out of your CI runners. Keychain setup, Xcode upgrades, macOS costs, and profile installation all disappear from your pipeline when a Linux job hands the prepared project to Capgo のビルド:

bun install --frozen-lockfile && bun run build
bunx cap sync ios
bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release

ステージ1、5、6の落とし穴は、ランナーではなくプロジェクト自体に関わるため、引き続き適用されます。失敗したジョブのデバッグについては、こちらを参照してください。 CapacitorのCI/CDパイプラインでのビルド失敗の修正.

クイックリファレンス

エラー 落とし穴 セクション
新しいビルドで古いUIが表示される cap sync Webビルドの前 ステージ1
No profiles for ... were found プロファイルが未インストール、または不一致 ステージ 3
errSecInternalComponent キーチェーンがロックされている ステージ 3
MAC verification failed パスワード誤りまたは OpenSSL 3 .p12 ステージ 3
Unsupported class file major version JDK が正しくない ステージ 2
アップロード時の SDK が古すぎる Xcode が 26 より古い ステージ 2
Bundle バージョンは高い必要がある ビルド番号の再利用 ステージ 5
バージョン code は既に使用済みです 再利用済み versionCode ステージ 5
live update 後のクラッシュ ネイティブ変更の OTA 配信 ステージ 6
Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。