Saltare al contenuto principale

CI/CD for Capacitor: Common Pitfalls and Fixes

I pitfalls di CI/CD che rompono Capacitor costruzioni: firma iOS, esecutori macOS, Xcode 26, sincronizzazione cap datata, codici versione, rifiuti di archiviazione e correzioni per ogni.

Crediti dell'articolo

Martin Donadieu

Autore

Valeria

Revisione

Jordan

Curatore

CI/CD per Capacitor: Pitfall comuni e correzioni

La maggior parte delle fallite CI/CD di Capacitor provengono dalla stessa breve lista: firma iOS code , strumenti macOS mancanti o datati, una build web che non è mai stata inserita nel progetto nativo, numeri di costruzione riutilizzati e requisiti di archiviazione che falliscono solo al momento dell'invio. Ogni ha una causa nota e una correzione che puoi applicare una volta. Questa guida raggruppa le pitfall per fase di pipeline, con l'errore che vedrai, perché succede e cosa cambiare.

Se stai configurando una pipeline da zero, leggi Configurazione di CI/CD per applicazioni Capacitor first, then use this list to harden it.

Stadio 1: Costruzione della layer web

Trappola: l'app nativa invia una versione vecchia del build web

Sintomo: CI ha successo, l'app si installa, ma mostra l'interfaccia utente di ieri.

Causa: cap sync copia tutto ciò che c'è in webDir a quel momento. Se il pipeline esegue cap sync prima del build web, o il build web scrive in una cartella diversa da webDir in capacitor.config.ts, il progetto nativo riceve file obsoleti.

Soluzione: sempre esegui i passaggi in questo ordine, e falli se la cartella di output è vuota.

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

Assicurati webDir corrisponda all'output del tuo bundler (dist per Vite, www per Angular con Ionic, build per alcune configurazioni React).

Pitfall: dev server URL left in the config

Sintomo: la versione di rilascio mostra uno schermo vuoto o cerca di caricare http://192.168.x.x:5173.

Causa: server.url in capacitor.config.ts was set for live reload and committed.

Risoluzione: non commettere mai server.url. Leggilo da una variabile di ambiente che il CI non imposta mai:

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: variabili di ambiente incorporate in un momento sbagliato

Sintomo: l'applicazione di produzione parla con il API di staging.

Causa: Vite, webpack e Angular impostano le variabili di ambiente inline al momento della compilazione. Il valore presente quando bun run build è il presente nel binario, e in qualsiasi live update costruito dallo stesso job.

Risoluzione: Impostare variabili specifiche per ambiente prima della build web, per ogni job, e creare bundle separati per staging e produzione. Non cercare di scambiarli dopo. cap sync.

Risoluzione: non commettere mai

Sintomo: Un plugin con una versione diversa in CI rispetto a quella sul tuo computer, e la compilazione nativa fallisce con simboli mancanti.

Soluzione: commit il file di lock e installa con bun install --frozen-lockfile (o npm ciaggiorna @capacitor/core, @capacitor/ios, @capacitor/androidPin @capacitor/cli alla stessa versione. Le versioni non corrispondenti sono una fonte frequente di errori nativi; vedere Fix Capacitor version mismatch errors.

Soluzione degli errori di versione __CAPGO_KEEP_0__

Fase 2: La catena di strumenti nativi non corretta

Sintomo: Unsupported class file major version, The engine "node" is incompatible, o errori di Xcode sui SDK feature.

Causa: le immagini dei runner ospitati cambiano, e Capacitor 8 ha requisiti minimi fermi.

Strumento Requisito Capacitor 8
Node.js 22 o più recente
JDK 21
Xcode 26 o più recente
Target di distribuzione iOS 15.0
Android minSdkVersion / targetSdkVersion 24 / 36

Correzione: pinare ogni versione esplicitamente nella pipeline al posto di fidarsi 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'

Fallimento: costruire contro un Xcode Apple che non accetta più

Sintomo: l'upload fallisce con un messaggio che l'app è stata costruita con un SDK non supportato.

Causa: dal 28 aprile 2026, App Store Connect richiede Xcode 26 e l'SDK iOS 26. Le Mac auto-hosted e le immagini di esecuzione più vecchie ancora utilizzano Xcode 16 per impostazione predefinita.

Correzione: selezionare un macos-26 immagine o installare Xcode 26 sui propri esecutori. Dettagli in Requisito di Xcode 26 di Apple per applicazioni Capacitor.

Fallimento: confusione tra CocoaPods e SPM

Sintomo: xcodebuild: error: 'App.xcworkspace' does not existo i pod non trovati.

Causa: nuovi Capacitor 8 progetti utilizzano il gestore di pacchetti Swift e costruiscono ios/App/App.xcodeprojProgetti più vecchi utilizzano CocoaPods e costruiscono ios/App/App.xcworkspace dopo pod installLe pipeline copiate da tutorial più vecchi suppongono il workspace.

Soluzione: controlla quale utilizza il tuo progetto e costruisci il file giusto. Se stai migrando, vedi Come migrare il tuo Capacitor app a SPM.

Pitfall: gli build plugin Android si interrompono dopo un aggiornamento di Gradle

Sintomo: Namespace not specified, package attribute is deprecated, o errori da un plugin’s build.gradle dopo l'aggiornamento di Android Studio.

Risoluzione: blocca il plugin Gradle di Android in android/build.gradle, aggiorna su una branch, e aggiorna i plugin per primo. Gli errori specifici sono trattati in Risoluzione degli errori di build del plugin con AGP 9: Capacitor.

Fase 3: Code firma

Trappola: la firma iOS funziona solo sul tuo Mac

Sintomo: No signing certificate "iOS Distribution" found, No profiles for 'com.example.app' were found, o errSecInternalComponent.

Causa: il tuo Mac tiene la chiave e la chiave Xcode scarica i profili per te. Un esecutore CI non ne ha, e la sua chiave è bloccata in una sessione non interattiva.

Correzione: importa il certificato in una chiave temporanea non protetta e installa il profilo dove Xcode 16 e successive lo cercano:

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/"

Usa il nome dell'identità Apple Distributione non il legacy iOS Distribution. fastlane's setup_ci plus match automatizza questo processo. Capgo Build prende il certificato e il profilo come variabili di ambiente e fa il lavoro sulla chiave da solo, quindi il job Linux non tocca mai security.

Fallimento comune: certificati scaduti o revocati

Sintomo: un flusso di lavoro che funzionava da un anno fallisce di notte.

Causa: i certificati di distribuzione Apple e i profili di provisioning scadono dopo un anno. Un collega che crea un nuovo certificato in Xcode può anche invalidare il profilo utilizzato dal tuo CI.

Risoluzione: Inserisci le date di scadenza nel calendario della tua squadra, utilizza un certificato di distribuzione condiviso per la CI e controlla prima di costruire. bunx @capgo/cli@latest build prescan --platform ios verifica la scadenza del certificato, la password e l'associazione del profilo prima di caricare qualsiasi cosa.

Trappola: accessi Apple ID e sollecitazioni di due fattori

Sintomo: fastlane si blocca in attesa di un codice a 6 cifre code.

Risoluzione: utilizzare una chiave 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.

Trappola: segreti base64 che non si decodificano

Sintomo: MAC verification failed, invalid keystore formato base64: invalid input.

Causa: linee di fine riga aggiunte quando si copia il valore base64, o un .p12 creato con OpenSSL 3 impostazioni che macOS non può leggere.

Rimedio: codifica su una riga sola, e utilizza -legacy quando si crea un .p12 con 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 keystore Android perso

Sintomo: non puoi firmare un aggiornamento perché nessuno ha il keystore.

Rimedio: con Play App Signing, il keystore è la chiave di caricamento, e il supporto di Play Console può registrare uno nuovo. Memorizza il keystore nelle tue segrete CI e in un backup offline. Non lasciarlo vivere solo su un laptop. Il Generatore di keystore Android crea uno nuovo se sei fresco di partenza.

Stadio 4: progettazione della pipeline

Trappola: build nativi su ogni richiesta di pull

Sintomo: controlli PR lenti e un grande conto per i minuti di macOS.

Causa: Costruisce iOS su runner macOS ospitati sono i minuti più costosi in quasi tutti i piani CI, e la maggior parte dei commit tocca solo JavaScript.

Soluzione: esegui lint, test e il build web su ogni PR. Esegui build nativi su tag di rilascio o merge per maininvia il bundle web a un canale Capgo per le anteprime PR al posto di costruire un binario, come descritto in Comparazione delle piattaforme CI/CD per le app Capacitor.

Pitfall: costruire iOS e Android in sequenza

Rimedio: utilizza una matrice per costruire entrambi i piattaforme in parallelo, e impostalo fail-fast: false affinché un problema di firma iOS non annulli un buon build Android.

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

Pitfall: nessuna cache, o la cache sbagliata

Sintomo: ogni build scarica le dipendenze Gradle e CocoaPods da zero, o un build di staging invia la configurazione di produzione da una cache obsoleta.

Rimedio: cache ~/.gradle/caches, ~/.gradle/wrapper, e ios/App/Pods Riflettiamo sui lockfile. Partitione i cache per ambiente. Con Capgo Build, il cache di build per applicazione può essere suddiviso con --cache-key prod and --cache-key stagingo saltare con --no-cache per una costruzione pulita.

Pitfall: percorsi di repository unico

Sintomo: could not find capacitor.config o plugin mancanti dal progetto nativo.

Soluzione: eseguire i comandi Capacitor dal pacchetto dell'applicazione, e puntare gli strumenti ai file hoistati node_modules . Il comando Capgo CLI accetta --path e --node-modules per questo.

Fase 5: Invio di sottoscrizione

Pitfall: numeri di costruzione ripresi

Sintomo: “La versione del bundle deve essere superiore alla versione precedentemente caricata” su iOS, o “La versione code è già stata utilizzata” su Google Play.

Soluzione: genera il numero in CI. Con fastlane, leggi l'ultima build di TestFlight e aggiungi uno. Con Capgo Build, questo è il default: recupera il numero di build più recente da App Store Connect o il più alto versionCode da Google Play e lo incrementa.

Pitfall: build bloccate in TestFlight

Sintomo: l'upload ha successo ma i tester non vedono mai la build.

Causa: risposta di conformità all'esportazione mancante.

Soluzione: declaralo una volta in ios/App/App/Info.plist se usi solo la crittografia standard:

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

Pitfall: rifiuti manifest di privacy

Sintomo: e-mail da Apple sul mancato motivo richiesto API dichiarazioni (ITMS-91053).

Soluzione: aggiungi un PrivacyInfo.xcprivacy alla destinazione dell'app e aggiorna i plugin che inviano i propri. guida alla privacy per le app Capacitor.

Pitfall: tipo di artefatto sbagliato

Soluzione: Google Play richiede un AAB (bundleRelease), non un APK. bundleRelease rimane efficace senza una release configurazione di firma e produce un AAB non firmato che Play rifiuta, quindi configura signingConfigs.release in android/app/build.gradle primo. L'iOS richiede un'esportazione per l'App Store, non un IPA di sviluppo o ad hoc. Controlla il metodo di esportazione nel tuo passaggio di build.

Stage 6: Aggiornamenti in tempo reale

Fallimento: inviare un live update che richiede una nuova build nativa

Sintomo: dopo un aggiornamento over-the-air, l'app si blocca chiamando un metodo di plugin che non esiste nella versione installata.

Causa: il bundle web dipende da una versione di plugin più recente di quella compilata nell'app sulle dispositivi degli utenti.

Soluzione: lascia che il pipeline decida. build needed uscita 0 quando le dipendenze native corrispondono a quelle presenti sul canale e 1 quando è necessario un nuovo binario:

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

Anche forza la cartella native quando i file sotto ios/, android/, o capacitor.config.* . Il pattern completo è in Scegli automaticamente live update o costruisci native, e le regole di compatibilità sono in compatibilità native.

La soluzione che elimina più ostacoli

Se cambi solo una cosa, sposta la compilazione e la firma iOS fuori dai tuoi esecutori CI. La configurazione della chiave di sicurezza, gli aggiornamenti di Xcode, i costi di macOS e l'installazione del profilo scompaiono dalla tua pipeline quando un lavoro Linux passa il progetto preparato a Capgo Build:

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 Risolvendo gli errori di compilazione nei flussi CI/CD di Capacitor.

Riferimento rapido

Errore Pitfall Sezione
Interfaccia vecchia in una nuova build cap sync prima della build web Fase 1
No profiles for ... were found Profilo non installato o non corrispondente Fase 3
errSecInternalComponent Chiavechain bloccata Fase 3
MAC verification failed Password sbagliata o OpenSSL 3 .p12 Fase 3
Unsupported class file major version JDK sbagliato Fase 2
SDK troppo vecchio all'upload Xcode più vecchio di 26 Fase 2
La versione del bundle deve essere più alta Numero di build riutilizzato Fase 5
Versione code già utilizzata Riutilizzato versionCode Fase 5
Crash dopo live update Modifica nativa spedita via aria Fase 6
Aggiornamenti in tempo reale per le app Capacitor

Quando un bug del layer web è attivo, spedisci la correzione attraverso Capgo invece di attendere giorni per l'approvazione della store. Gli utenti ricevono l'aggiornamento in background mentre le modifiche native rimangono sulla normale via di revisione.

Ultimi da il nostro Blog

Capgo vi offre le migliori informazioni che avete bisogno per creare un'app mobile veramente professionale.