La CI/CD per un'app Cordova nel 2026 significa compilare e firmare l'AAB Android su un runner Linux, compilare e firmare l'IPA iOS su un runner macOS e caricare entrambi gli artefatti negli store dalla stessa pipeline. I servizi gestiti che i team Cordova usavano per questo sono stati dismessi o stanno chiudendo, quindi la maggior parte dei team esegue ora la CLI Cordova su una CI generica. Questa guida ti fornisce una configurazione funzionante di GitHub Actions, gli stessi passaggi per altre CI e una panoramica onesta sulle live update e sulle opzioni a lungo termine.
Cosa è cambiato per i team Cordova
Tre eventi hanno plasmato la CI/CD di Cordova:
- PhoneGap Build è stato dismesso il 1° ottobre 2020.
- Microsoft App Center è stato ritirato il 31 marzo 2025. Build, distribuzione e il servizio CodePush hostizzato sono terminati con esso.
- Alternative a Ionic Appflow ha interrotto le nuove vendite a febbraio 2025. I clienti esistenti mantengono l'accesso fino al 31 dicembre 2027.
Cordova stesso funziona ancora. Il progetto Apache rilascia cordova-android e cordova-ios releases, e molte app interne ed enterprise girano su di essa. Il punto debole è il layer gestito: archiviazione delle firme, upload sugli store e aggiornamenti over-the-air. Oggi devi assemblare tutto questo con un servizio CI e qualche tool.
Anatomia di una pipeline Cordova
| Fase | Android | iOS |
|---|---|---|
| Runner | Linux | macOS con Xcode 26 |
| Ripristina | cordova prepare (piattaforme e plugin da package.json) |
stesso |
| Build web | il tuo bundler (Vite, webpack, Angular CLI) in www/ |
stesso |
| Native Build | Gradle tramite cordova build android --release |
xcodebuild tramite cordova build ios --release --device |
| Firma | keystore di upload (.jks) |
certificato di distribuzione (.p12) e provisioning profile |
| Artefatto | .aab per Google Play |
.ipa per TestFlight e l'App Store |
| Carica | Account di servizio Google Play Developer API | Chiave API di App Store Connect |
Due regole delle piattaforme si applicano a ogni pipeline:
- Dal 28 aprile 2026, App Store Connect accetta solo build create con Xcode 26 e iOS 26 SDK o versioni successive. Verifica che la tua
cordova-iosversione lo supporti. - Google Play richiede upload AAB per le nuove app e aumenta ogni anno il livello minimo di target API. Mantieni
cordova-androidattualmente è il valore predefinitotargetSdkVersionfollows.
Passo 1: Rendi il progetto riproducibile
La CI può ricostruire solo ciò che è nel repository. Prima di scrivere qualsiasi workflow:
- Commit
package.jsone il lockfile. Cordova memorizza piattaforme e plugin nelcordovasezione dipackage.json, quindicordova prepareli ripristina. - Non committare
platforms/oppureplugins/. Vengono generati. - Installa Cordova CLI come dev dependency in modo che ogni macchina usi la stessa versione:
bun add -d cordova
- Sposta i valori di firma da
config.xmla unbuild.jsonche legge i segreti al momento del build (passo successivo). - Fissa la versione di Node.js in
.nvmrcoppurepackage.jsonengines.
Passo 2: Firma con build.json
Cordova legge le impostazioni di firma da un build.json file. Committa un build.template.json con placeholder, genera il build.json reale in CI dai secrets e non committare mai il file generato:
{
"android": {
"release": {
"keystore": "release.jks",
"storePassword": "${KEYSTORE_STORE_PASSWORD}",
"alias": "upload",
"password": "${KEYSTORE_KEY_PASSWORD}",
"packageType": "bundle"
}
},
"ios": {
"release": {
"codeSignIdentity": "Apple Distribution",
"developmentTeam": "ABCDE12345",
"packageType": "app-store",
"provisioningProfile": "com.example.app AppStore",
"automaticProvisioning": false
}
}
}
Il ${...} i placeholder non vengono espansi da Cordova. Il workflow seguente scrive i valori reali con envsubst.
Se non hai ancora un keystore Android, creane uno con keytool o basato su browser Generatore di keystore per Androidbasato su browser. Per iOS, il Generatore di certificati iOS crea una richiesta di firma del certificato e .p12 senza un Mac.
Passo 3: un workflow GitHub Actions
Questo workflow compila entrambe le piattaforme in parallelo su un tag di versione e le carica sui track di test interni.
# .github/workflows/cordova-release.yml
name: Cordova release
on:
push:
tags: ['v*']
jobs:
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- run: bun install --frozen-lockfile
- run: bun run build # outputs to www/
- run: bunx cordova prepare android
- name: Signing files
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
run: |
echo "$KEYSTORE_BASE64" | base64 --decode > release.jks
envsubst < build.template.json > build.json
- run: bunx cordova build android --release --buildConfig=build.json
- uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.example.app
releaseFiles: platforms/android/app/build/outputs/bundle/release/app-release.aab
track: internal
status: completed
ios:
runs-on: macos-26
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bunx cordova prepare ios
- name: Install certificate and profile
env:
P12_BASE64: ${{ secrets.IOS_P12_BASE64 }}
P12_PASSWORD: ${{ secrets.IOS_P12_PASSWORD }}
PROFILE_BASE64: ${{ secrets.IOS_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo "$P12_BASE64" | base64 --decode > dist.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -lut 21600 build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security import dist.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security list-keychains -d user -s build.keychain login.keychain
for dir in "$HOME/Library/MobileDevice/Provisioning Profiles" \
"$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"; do
mkdir -p "$dir"
echo "$PROFILE_BASE64" | base64 --decode > "$dir/app.mobileprovision"
done
cp build.template.json build.json # iOS values hold no secrets
- run: bunx cordova build ios --release --device --buildConfig=build.json
- name: Upload to TestFlight
env:
API_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
API_ISSUER: ${{ secrets.APPLE_ISSUER_ID }}
API_KEY_BASE64: ${{ secrets.APPLE_KEY_CONTENT }}
run: |
mkdir -p ~/.appstoreconnect/private_keys
echo "$API_KEY_BASE64" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8
IPA=$(find platforms/ios/build -name '*.ipa' | head -n 1)
xcrun altool --upload-app -f "$IPA" -t ios --apiKey "$API_KEY_ID" --apiIssuer "$API_ISSUER"
- name: Clean up keychain
if: always()
run: security delete-keychain build.keychain || true
Perché alcune righe sono lì:
- Due cartelle di profili di provisioning. Xcode 16 e versioni successive leggono i profili da
~/Library/Developer/Xcode/UserData/Provisioning Profiles. Le versioni precedenti di Xcode usavano~/Library/MobileDevice/Provisioning Profiles. Scrivere entrambi evita l'errore “No profiles for ‘com.example.app’ were found”. set-key-partition-listimpedisce a macOS di richiedere l'accesso al keychain, il che bloccherebbe un job CI.- Versione JDK. Usa il JDK che
cordova-androiddocumenti di rilascio. Le versioni recenti vengono compilate con JDK 17. - Numeri di build. Incrementa
android-versionCodeeios-CFBundleVersionnellaconfig.xmlprima della build, ad esempio dal numero di esecuzione CI, altrimenti gli store rifiuteranno l'upload come duplicato.
Puoi sostituire altool upload per fastlane upload_to_testflight e l'azione Play con fastlane supply se il tuo team usa già fastlane.
Passo 4: La stessa pipeline su altri servizi CI
I comandi non cambiano tra le CI. Cambiano solo la selezione del runner e la sintassi dei secret.
| CI | Runner Android | Runner iOS | Note |
|---|---|---|---|
| Azioni GitHub | ubuntu-latest |
macos-26 / macos-latest |
I minuti macOS costano di più rispetto a Linux sui repository privati |
| GitLab CI | qualsiasi runner Linux | runner macOS hosted (beta, Premium e Ultimate) o self-hosted | immagini come macos-26-xcode-26 |
| Bitbucket Pipelines | Immagine Docker Linux | solo Mac self-hosted | vedi Guida Bitbucket per iOS |
| Gitea / Forgejo | Runner Linux | Mac self-hosted in modalità host | vedi Guida Gitea per iOS |
| Bitrise | Stack Linux | Stack macOS | include gli step Cordova (prepare, archive) |
| Codemagic | Linux | Istanze Mac mini | documenta i build Ionic Cordova in codemagic.yaml |
| Azure DevOps | ubuntu-latest |
macOS-latest |
task per installare certificati e profili |
Per una panoramica delle funzionalità, leggi Confronto tra piattaforme CI/CD per app Cordova.
Passaggio 5: Live Updates per Cordova
I Live Update ti permettono di rilasciare fix HTML, CSS e JavaScript senza la revisione degli store. App Center CodePush è terminato a marzo 2025, Appflow Live Updates terminerà il 31 dicembre 2027 e i vecchi plugin community di hot code push non sono più mantenuti. Un'opzione ancora mantenuta è Live updates di Capgo con il @capgo/cordova-updater plugin, che usa lo stesso backend, gli stessi canali e lo stesso CLI dell'updater Capacitor.
Requisiti
- Cordova CLI 12+
cordova-android13+ e/ocordova-ios7+ (il plugin si basa sui lorohttps://localhost/eapp://localhost/schemes)- Non usare
cordova-plugin-ionic-webviewcon esso. Ionic WebView bypassa gli scheme handler di Cordova, quindi i bundle scaricati non verrebbero caricati.
Installa il plugin
Crea l'app in Capgo nello stesso modo di Capacitor, quindi installa il plugin con il tuo Capgo app ID:
cordova plugin add @capgo/cordova-updater \
--variable APP_ID=com.example.app \
--variable DEFAULT_CHANNEL=production
cordova prepare android ios
DEFAULT_CHANNEL, UPDATE_URL, e AUTO_UPDATE sono variabili di installazione opzionali. Esegui cordova prepare di nuovo ogni volta che le modifichi.
Conferma ogni lancio
Dopo aver deviceready, il plugin è disponibile come cordova.plugins.Updater. Chiama notifyAppReady() a ogni lancio, oppure il plugin considera il nuovo bundle corrotto e esegue il rollback:
document.addEventListener('deviceready', async () => {
const { Updater } = cordova.plugins
await Updater.notifyAppReady()
const latest = await Updater.getLatest()
if (latest.url && !latest.error) {
const bundle = await Updater.download({
url: latest.url,
version: latest.version,
checksum: latest.checksum,
})
await Updater.next({ id: bundle.id })
}
})
Carica i bundle da CI
Aggiungi un job che compila il layer web e lo carica, per modifiche solo web:
live-update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bunx @capgo/cli@latest bundle upload --channel=production
env:
CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
Le Live updates sostituiscono solo il bundle web. Aggiungere, rimuovere o aggiornare un plugin Cordova richiede comunque una nuova build per lo store. Capgo confronta i plugin nativi registrati per il bundle live con il tuo progetto e segnala i caricamenti incompatibili; consulta compatibilità live update. La configurazione completa è nella Documentazione updater Cordova.
Per quanto riguarda la build, la Build Capgo documentazione copre solo i progetti Capacitor (carica il progetto nativo generato da cap sync). I binari Cordova continuano a essere compilati sulla tua CI come mostrato sopra.
Opzionale: migrazione a Capacitor in seguito
Non devi abbandonare Cordova per avere una pipeline funzionante e live updates. La migrazione è una decisione separata, utile quando:
- Vuoi build iOS senza mantenere runner macOS. Capgo Build compila le app Capacitor da un job CI Linux.
- I plugin su cui dipendi non ricevono più aggiornamenti per Cordova, ma hanno equivalenti Capacitor.
- Vuoi il supporto a Swift Package Manager e le versioni attuali di Android Gradle Plugin senza patchare le piattaforme.
La migrazione mantiene il tuo www code. Aggiungi Capacitor, sposta i plugin (la maggior parte dei plugin Cordova funziona così com'è) e sostituisci quelli che hanno equivalenti Capacitor-native. L'ID app e i canali di Capgo restano invariati; cambia solo il plugin updater da @capgo/cordova-updater a @capgo/capacitor-updater. I passaggi sono in Migrazione da Cordova a Capacitor, e il pagina di soluzione da Cordova a Capacitor che copre come ottenere supporto. Dopo la migrazione, la CI sopra si riduce a un web build, cap sync, e una build request call per piattaforma, come descritto in Confronto tra piattaforme CI/CD per app Capacitor.
Troubleshooting delle build Cordova in CI
| Errore | Causa e soluzione |
|---|---|
Current working directory is not a Cordova-based project |
config.xml oppure www/ mancante. Esegui la build web prima dei comandi Cordova, oppure crea un file vuoto www/. |
No platforms added to this project |
Le piattaforme non sono elencate in package.json. Esegui cordova platform add in locale una volta e committa package.json. |
Could not find an installed version of Gradle |
Recenti cordova-android usa il wrapper Gradle. Aggiorna la piattaforma invece di installare Gradle globalmente. |
Unsupported class file major version |
Il JDK sul runner non corrisponde a quello cordova-android previsto. Fissalo con setup-java. |
No signing certificate "iOS Distribution" found |
Usa Apple Distribution come identity e verifica che il .p12 contenga la chiave privata. |
errSecInternalComponent |
Keychain bloccata o partition list mancante. Riesegui i comandi di setup della keychain. |
| Upload rifiutato: SDK troppo vecchio | L'immagine del runner usa Xcode più vecchio di 26. Seleziona macos-26 oppure imposta Xcode con xcode-select. |
Upload su Play: Version code has already been used |
Incrementa android-versionCode per ogni upload. |
Riassunto
Un pipeline Cordova nel 2026 è un job CI per piattaforma: un runner Linux per l'AAB Android, un runner macOS con Xcode 26 per l'IPA, segreti decodificati in un file generato build.json, e chiavi API per entrambi gli store. Qualsiasi CI mainstream può eseguirlo. Aggiungi @capgo/cordova-updater e a una bundle upload un job per le correzioni solo web, e hai le parti di build, store e over-the-air che Appflow e App Center fornivano in passato. Passare a Capacitor resta un'opzione per il futuro, non un requisito.