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 |