La plupart des échecs CI/CD de Capacitor proviennent de la même liste courte : la signature iOS code , les outils macOS manquants ou obsolètes, une construction web qui n'a jamais été intégrée au projet natif, des numéros de construction réutilisés, et les exigences de magasin qui ne réussissent qu'à l'heure de l'envoi. Chacun a une cause connue et une solution que vous pouvez appliquer une fois. Ce guide groupe les pièges par étape de pipeline, avec l'erreur que vous verrez, pourquoi cela se produit et ce que vous devez changer.
Si vous configurez un pipeline à partir de zéro, lisez Configurer la CI/CD pour les applications Capacitor tout d'abord, puis utilisez cette liste pour le renforcer.
Étape 1 : Construction de la couche web
Piège : l'application native expédie une construction web ancienne
Symptôme : CI réussit, l'application s'installe, mais elle affiche l'interface d'hier.
Cause : cap sync copie tout ce qui se trouve à webDir à ce moment-là. Si la pipeline s'exécute cap sync avant la construction web, ou la construction web écrit dans un dossier différent de webDir à capacitor.config.tsle projet natif reçoit des fichiers obsolètes.
Fix : exécutez toujours les étapes dans cet ordre, et faites fail si le dossier de sortie est vide.
bun install --frozen-lockfile
bun run build
test -f dist/index.html || { echo "web build missing"; exit 1; }
bunx cap sync
Assurez-vous que webDir correspond à votre sortie de bundler (dist pour Vite, www pour Angular avec Ionic, build pour certaines configurations de React).
Pitfall: dev server URL left in the config
Symptôme : l'écran de la version de production est vide ou essaie de charger http://192.168.x.x:5173.
Cause : server.url dans capacitor.config.ts Ajusté pour le live reload et commité.
Fix : ne jamais commiter server.url. Lisez-l’à partir d'une variable d'environnement que CI ne définit jamais :
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
Pitfall : les variables d'environnement sont incorporées à un moment malheureux
Symptôme : L'application de production communique avec la mise en scène API.
Cause : Vite, webpack et Angular définissent les variables d'environnement inline à l'étape de build. La valeur présente lors de bun run build C'est ran qui se trouve dans le binôme, et dans tout live update construit à partir du même job.
Fix: Définissez les variables spécifiques à l'environnement avant la construction web, par job, et construisez des bundles séparés pour le stade et la production. N'essayez pas de les échanger ensuite. cap sync.
Pitfall : dérive du fichier de verrouillage
Symptôme : Une version du plugin dans CI diffère de celle sur votre machine, et la compilation native échoue en raison de symboles manquants.
Fix: commit le fichier de verrouillage et installez avec bun install --frozen-lockfile (ou npm ci). Fix @capacitor/core, @capacitor/ios, @capacitor/android, et @capacitor/cli à la même version. Les versions incohérentes sont une source fréquente d'erreurs natives ; voir Fix Capacitor les erreurs de version incohérente.
Étape 2 : Le kit de développement natif
Piège : mauvaise version de Node, JDK ou Xcode
Symptôme : Unsupported class file major version, The engine "node" is incompatible, ou erreurs Xcode sur SDK des fonctionnalités.
Cause : hosted runner images change, and Capacitor 8 has firm minimums.
| Outil | Capacitor 8 exigence |
|---|---|
| Node.js | 22 ou plus récent |
| JDK | 21 |
| Xcode | 26 ou plus récent |
| cible de déploiement iOS | 15.0 |
Android minSdkVersion / targetSdkVersion |
24 / 36 |
Solution : fixez explicitement chaque version dans le pipeline au lieu de faire confiance 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'
Pitfall : construction contre un Xcode Apple qui ne l'accepte plus
Symptôme : L'upload a échoué avec un message indiquant que l'application a été construite avec un SDK non pris en charge.
Cause : Depuis le 28 avril 2026, App Store Connect exige Xcode 26 et l'iOS 26 SDK. Les Macs auto-hébergés et les anciens images de runner définissent toujours Xcode 16.
Fix : choisissez un macos-26 image ou installez Xcode 26 sur vos runners. Détails dans l'exigence d'Apple Xcode 26 pour les Capacitor applications.
Pitfall : confusion entre CocoaPods et SPM
Symptôme : xcodebuild: error: 'App.xcworkspace' does not exist, ou les pods non trouvés.
Cause : nouveau Capacitor 8 projets utilisent le gestionnaire de packages Swift et s'éditent ios/App/App.xcodeproj. Projets plus anciens utilisent CocoaPods et construisent ios/App/App.xcworkspace après pod install. Les pipelines copiés à partir de tutoriels plus anciens supposent l'workspace.
Fix : vérifiez lequel votre projet utilise et construisez le bon fichier. Si vous êtes en train de migrer, voir Comment migrer votre Capacitor application vers SPM.
Piège : les builds du plugin Android se cassent après une mise à jour de Gradle
Symptôme : Namespace not specified, package attribute is deprecated, ou des erreurs d'un plugin build.gradle après la mise à jour d'Android Studio.
Fix : fixez la version de l'Android Gradle Plugin android/build.gradle, mettez à jour sur une branche, et mettez à jour les plugins en premier. Les erreurs spécifiques sont couvertes dans Fix les erreurs de construction du plugin Capacitor avec AGP 9.
Étape 3 : Code de signature
Piège : la signature iOS fonctionne uniquement sur votre Mac
Symptôme : No signing certificate "iOS Distribution" found, No profiles for 'com.example.app' were found, ou errSecInternalComponent.
Cause : le clé de votre Mac détient le certificat et Xcode télécharge les profils pour vous. Un exécutant CI n'en a pas, et sa clé de chaîne est verrouillée dans une session non interactive.
Fix : importez le certificat dans une clé de chaîne temporaire non verrouillée et installez le profil où Xcode 16 et ultérieurs le cherchent :
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/"
Utilisez le nom d'identité Apple Distribution, et non le legacy iOS Distributionfastlane’s setup_ci plus match automates this. Capgo Build takes the certificate and profile as environment variables and does the keychain work on its own machines, so the Linux job never touches security.
Piège : certificats expirés ou révoqués
Symptom: Un pipeline qui fonctionnait depuis un an a cessé de fonctionner la nuit.
Cause: Apple distribution certificates and provisioning profiles expire after a year. A teammate creating a new certificate in Xcode can also invalidate the profile your CI uses.
Fix: mettez les dates d'expiration dans votre calendrier d'équipe, utilisez une seule certificat de distribution partagé pour CI, et vérifiez avant de construire. bunx @capgo/cli@latest build prescan --platform ios Vérifie l'expiration du certificat, le mot de passe et l'association de profil avant tout téléchargement.
Pitfall: Apple ID logins and two-factor prompts
Symptôme : fastlane s'arrête en attendant un code à 6 chiffres.
Solution : utilisez une clé App Store Connect API (.p8, key ID, issuer ID) instead of an Apple ID and password. It does not require two-factor authentication and can be scoped to App Manager. If authentication still fails with a correct key, check the runner clock: the token is signed with the local time, and Apple rejects tokens with a skewed timestamp.
Erreur : secrets base64 qui ne se décodent pas
Symptôme : MAC verification failed, invalid keystore formatOrigine : base64: invalid input.
Cause: créé avec OpenSSL 3 par défaut qui ne peut pas être lu par macOS. .p12 Piège : secrets base64 qui ne décodent pas
Solution : encode sur une ligne, et utilisez -legacy lors de la création d'un .p12 avec 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
Pitfall : un clé de stockage Android perdue
Symptôme : Vous ne pouvez pas signer une mise à jour car personne n'a le coffre-fort.
Fix : avec Play App Signing, la clé de stockage est la clé d'envoi, et le support de la console Play peut enregistrer une nouvelle. Stockez la clé de stockage dans vos secrets CI et dans un sauvegarde hors ligne. N'oubliez jamais de la laisser vivre que sur un seul ordinateur portable. Générateur de clés de signature Android générateur de clés de stockage Android
crée une nouvelle si vous commencez frais.
Étape 4 : Conception de la chaîne d'outils
Symptôme : vérifications PR lentes et facture macOS minutes importante.
Cause : les builds iOS sur les exécutants macOS hébergés sont les minutes les plus coûteuses dans la plupart des plans CI, et la plupart des commits ne touchent que le JavaScript.
Fix : exécutez lint, tests et la construction web sur chaque PR. Exécutez les builds natives sur les étiquettes de version ou les merges. mainPour les prévisions PR, expédiez le bundle web vers un canal Capgo au lieu de construire un binaire, comme décrit dans Comparaison des plateformes CI/CD pour les applications Capacitor.
Pitfall : construction d'iOS et d'Android de manière séquentielle
Fix : use a matrix so both platforms build in parallel, and set fail-fast: false afin qu'un problème de signature iOS ne bloque pas une bonne build Android.
strategy:
fail-fast: false
matrix:
platform: [ios, android]
Pitfall: no caching, or the wrong cache
Symptôme : Chaque build télécharge les dépendances Gradle et CocoaPods à partir de zéro, ou une build de production expédie la configuration de production à partir d'une cache obsolète.
Solution : cache ~/.gradle/caches, ~/.gradle/wrapper, et ios/App/Pods Partitionnez les caches en fonction de l'environnement. Avec Capgo, le cache de build par application peut être séparé avec --cache-key prod et --cache-key staging, ou ignoré avec --no-cache pour une build propre.
Pitfall: chemins de monorepo
Symptôme : could not find capacitor.config ou plugins manquants dans le projet natif.
Fix : run Capacitor commands from the app package, and point tools at hoisted node_modulesle Capgo CLI accepte --path et --node-modules pour cela.
Étape 5 : Soumission de magasin
Pitfall : numéros de build réutilisés
Symptôme : « La version du bundle doit être supérieure à la version précédemment téléchargée » sur iOS, ou « La version code a déjà été utilisée » sur Google Play.
Fix : générer le numéro en CI. Avec Fastlane, lire la dernière mise à jour de TestFlight et ajouter un. Avec Capgo Build, c'est la valeur par défaut : il récupère le dernier numéro de build depuis App Store Connect ou le plus élevé versionCode à partir de Google Play et l'incrémente.
Pitfall : les builds bloqués dans TestFlight
Symptôme : l'upload réussit mais les testeurs ne voient jamais la build.
Cause : manque de conformité à l'exportation.
Fix : déclarez-l’une fois dans ios/App/App/Info.plist si vous utilisez uniquement l'encryption standard :
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
Pitfall : rejets de manifeste de confidentialité
Symptôme : email du compte Apple concernant les déclarations de raisons requises manquantes (API) (ITMS-91053).
Corrigez : ajoutez un PrivacyInfo.xcprivacy à la cible de l'application et mettez à jour les plugins qui embarquent leur propre version. une guide de confidentialité pour les applications Capacitor.
Piège : mauvais type d'artefact
Corrigez : Google Play nécessite un AAB (bundleRelease) et non un APK. bundleRelease se poursuit toujours sans une release config de signature et produit un AAB non signé que Play rejette, configurez donc signingConfigs.release en android/app/build.gradle Tout d'abord. iOS nécessite une exportation vers l'App Store, et non une IPA de développement ou ad hoc. Vérifiez la méthode d'exportation dans votre étape de build.
Mise à niveau 6 : mises à jour en direct
Pitfall : livrer un live update qui nécessite une nouvelle compilation native
Symptôme : Après une mise à jour en ligne, l'application s'effondre en appelant une méthode de plugin qui n'existe pas dans le code installé.
Cause : le bundle web dépend d'une version de plugin plus récente que celle compilée dans l'application sur les appareils des utilisateurs.
Solution : laissez le pipeline décider. build needed sorties 0 lorsque les dépendances natives correspondent à ce qui est en direct sur le canal et 1 lorsque un nouveau binaire est requis :
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
Forcez également le chemin natif lorsque les fichiers sous ios/, android/ou capacitor.config.* sont modifiés. Le modèle complet est en Choisissez automatiquement live update ou une build nativeet les règles de compatibilité sont en place compatibilité native.
La correction qui élimine le plus grand nombre de pièges
Si vous n'avez que le temps de changer une chose, déplacez la compilation et la signature iOS hors de vos exécutants CI. La configuration de la clé de cryptage, les mises à jour de Xcode, les coûts macOS et l'installation de profil disparaissent de votre pipeline lorsque le travail Linux transmet le projet préparé à Capgo Construction:
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
Les pièges dans les étapes 1, 5 et 6 s'appliquent toujours, car ils concernent votre projet, et non le runner. Pour en savoir plus sur la débogage des tâches échouées, voir Résoudre les erreurs de construction dans les pipelines CI/CD de Capacitor.
Référence rapide
| Erreur | Piège | Section |
|---|---|---|
| Interface ancienne dans une nouvelle build | cap sync avant la build web |
Étape 1 |
No profiles for ... were found |
Profil non installé ou incohérent | Étape 3 |
errSecInternalComponent |
Chaîne de clés verrouillée | Étape 3 |
MAC verification failed |
Mot de passe incorrect ou OpenSSL 3 .p12 |
Étape 3 |
Unsupported class file major version |
JDK incorrect | Étape 2 |
| SDK trop ancien lors de l'upload | Xcode plus ancien que 26 | Étape 2 |
| La version du paquet doit être supérieure | Numéro de build réutilisé | Étape 5 |
| La version code a déjà été utilisée | Réutilisé versionCode |
Étape 5 |
| Crash après live update | Modification native déployée en ligne | Étape 6 |