Allez directement au contenu principal

CI/CD pour Capacitor: Les pièges courants et les solutions

Les pièges de CI/CD qui cassent les builds Capacitor: signature iOS, exécuteurs macOS, Xcode 26, cap sync obsolète, codes de version, rejets de magasin et solutions pour chacun.

Crédits de l'article

Martin Donadieu

Auteur

Valeria

Réviseur

Jordan

Éditeur

CI/CD pour Capacitor: Les pièges courants et les solutions

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 natif 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 copies whatever is in webDir Lorsqu'il s'exécute le pipeline. cap sync avant la construction web, ou la construction web écrit dans un dossier différent que webDir in capacitor.config.tsLes fichiers natifs du projet deviennent 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

bundler webDir correspond à votre sortie de bundler ("dist bundler 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 tente 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 le 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 mal choisi

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 celui qui se trouve dans le fichier binaire, 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 de 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). Pin @capacitor/core, @capacitor/ios, @capacitor/android, and @capacitor/cli Mettre à jour vers la même version. Les versions incohérentes sont une source fréquente d'erreurs natives ; voir Fixer les erreurs de version de Capacitor.

, ou erreurs Xcode sur __CAPGO_KEEP_0__ fonctionnalités.

Piège : mauvaise version de Node, JDK ou Xcode

Symptom: Unsupported class file major version, The engine "node" is incompatible, ou erreurs Xcode concernant les fonctionnalités SDK.

(ou hosted runner images change, and Capacitor 8 has firm minimums.

Tool 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 par défaut.

Fix : choisissez un macos-26 image ou installez Xcode 26 sur vos runners. Détails dans l'exigence d'Apple de 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 d'anciens tutoriels supposent l'espace de travail.

Fix : vérifiez lequel votre projet utilise et construisez le bon fichier. Si vous migrez, voir Comment migrer votre Capacitor application vers SPM.

Pitfall : les plugins Android ne construisent pas 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 abordées 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 : La clé de votre Mac contient 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 : Importer le certificat dans une clé de chaîne temporaire non verrouillée et installer le profil où Xcode 16 et ultérieur 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 Distribution. fastlane’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.

Pitfall: 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.

Pitfall: secrets base64 qui ne décodent pas

Symptôme : MAC verification failed, invalid keystore formatCause : base64: invalid input.

Cause: créé avec les paramètres par défaut OpenSSL 3 que macOS ne peut pas lire. .p12 créé avec OpenSSL 3 par défaut qui ne peut pas être lu par macOS.

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 accès au 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. Ne la laissez jamais vivre que sur un seul ordinateur portable. Le clé de stockage Android génératrice crée une nouvelle si vous commencez frais.

Étape 4 : Conception de la pipeline

Pitfall : les builds natives sur chaque demande de tirage

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 plus coûteux 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 pour 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 Capgo canal au lieu de construire un binaire, comme décrit dans Comparaison des plateformes CI/CD pour les applications Capacitor.

Pitfall : construction des iOS et 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

Symptom: Chaque build télécharge les dépendances Gradle et CocoaPods à partir de zéro, ou une build de production envoie la configuration depuis un cache obsolète.

Fix: Mise en 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ée avec --no-cache pour une build propre.

Pitfall: chemins de monorepo

Symptom: 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 l'application

Péché : 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érez le numéro en CI. Avec Fastlane, lisez la dernière mise à jour de TestFlight et ajoutez 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 obligatoires manquantes (API) (ITMS-91053).

Fix : ajoutez un PrivacyInfo.xcprivacy à la cible de l'application et mettez à jour les plugins qui embarquent leur propre version. guide de confidentialité pour les applications Capacitor.

Pitfall : type d'artefact incorrect

Fix : Google Play nécessite un AAB (bundleRelease) et non un APK. bundleRelease se poursuit encore sans une release et produit un AAB non signé que Play rejette, configurez donc signingConfigs.release in android/app/build.gradle Veuillez exporter vers l'App Store, et non vers un fichier IPA de développement ou ad hoc. Vérifiez la méthode d'exportation dans votre étape de build.

Étape 6 : Mises à jour en direct

Pitfall : expédier 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 d'un 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 nécessite un nouveau binaire :

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 Auto choose live update or native build, et les règles de compatibilité sont dans Compatibilité native.

La correction qui supprime le plus de pièges

Si vous n'changez qu'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 un job Linux transmet le projet préparé à __CAPGO_KEEP_0__ Build 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

The pitfalls in stages 1, 5, and 6 still apply, because they are about your project, not the runner. For more on debugging failing jobs, see Résoudre les erreurs de construction dans les pipelines CI/CD de Capacitor.

Référence rapide

Error Erreur 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 Cléchainée 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 Changement natif expédié par voie aérienne Étape 6
Mises à jour instantanées pour les applications Capacitor

Quand un bug de la couche web est en ligne, expédiez la correction par Capgo plutôt que d'attendre des jours pour l'approbation de la boutique d'applications. Les utilisateurs reçoivent la mise à jour en arrière-plan tandis que les modifications natives restent dans la voie de revue normale.

Support humain de Martin

Démarrer maintenant

Dernières actualités de notre blog

Capgo vous donne les meilleures informations dont vous avez besoin pour créer une application mobile vraiment professionnelle.