CI/CD para una app de Cordova en 2026 significa compilar y firmar el AAB de Android en un runner de Linux, compilar y firmar el IPA de iOS en un runner de macOS, y subir ambos a las tiendas desde el mismo pipeline. Los servicios gestionados que los equipos de Cordova usaban para esto han desaparecido o están cerrando, por lo que la mayoría de los equipos ahora ejecutan el CLI de Cordova en un CI de propósito general. Esta guía te ofrece una configuración funcional de GitHub Actions, los mismos pasos para otros CIs y una mirada honesta a las actualizaciones en vivo y las opciones a largo plazo.
Qué cambió para los equipos de Cordova
Tres eventos marcaron el CI/CD de Cordova:
- PhoneGap Build cerró el 1 de octubre de 2020.
- Microsoft App Center retirado el 31 de marzo de 2025. Con ello finalizaron el build, la distribución y el servicio de CodePush alojado.
- Ionic Appflow dejó de vender en febrero de 2025. Los clientes actuales mantienen su acceso hasta el 31 de diciembre de 2027.
Cordova sigue funcionando. El proyecto Apache publica cordova-android y cordova-ios releases, y muchas apps internas y empresariales funcionan sobre él. El hueco está en la capa gestionada: almacenamiento de firmas, subidas a la tienda y actualizaciones OTA. Ahora tienes que montar eso con un servicio de CI y unas cuantas herramientas.
Anatomía de un pipeline de Cordova
| Etapa | Android | iOS |
|---|---|---|
| Runner | Linux | macOS con Xcode 26 |
| Restaurar | cordova prepare (plataformas y plugins de package.json) |
mismo |
| Build web | tu bundler (Vite, webpack, Angular CLI) en www/ |
mismo |
| Native Build | Gradle mediante cordova build android --release |
xcodebuild mediante cordova build ios --release --device |
| Firma | keystore de subida (.jks) |
certificado de distribución (.p12) y perfil de aprovisionamiento |
| Artefacto | .aab para Google Play |
.ipa para TestFlight y la App Store |
| Subir | Google Play Developer API (cuenta de servicio) | Clave API de App Store Connect |
Se aplican dos reglas de plataforma a cada pipeline:
- Desde el 28 de abril de 2026, App Store Connect solo acepta builds hechas con Xcode 26 y el SDK de iOS 26 o superior. Verifica que tu
cordova-iosversión lo soporte. - Google Play requiere uploads de AAB para apps nuevas y sube su nivel mínimo de API cada año. Mantente
cordova-androidal día para que su valortargetSdkVersionsigue.
Paso 1: Haz que el proyecto sea reproducible
El CI solo puede reconstruir lo que está en el repositorio. Antes de escribir cualquier workflow:
- Haz commit de
package.jsony del lockfile. Cordova guarda las plataformas y los plugins en lacordovasección depackage.json, así quecordova preparelos restaura. - No hagas commit
platforms/oplugins/. Se generan automáticamente. - Instala el CLI de Cordova como dev dependency para que cada máquina use la misma versión:
bun add -d cordova
- Mueve los valores de firma fuera de
config.xmly pásalos a unbuild.jsonque lea los secrets en el build (siguiente paso). - Fija la versión de Node.js en
.nvmrcopackage.jsonengines.
Paso 2: Firma con build.json
Cordova lee la configuración de firma desde un build.json archivo. Commitea un build.template.json con placeholders, genera el real build.json en CI desde secrets, y nunca commitees el archivo generado:
{
"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
}
}
}
El ${...} los placeholders no se expanden en Cordova. El workflow siguiente escribe los valores reales con envsubst.
Si aún no tienes un keystore de Android, crea uno con keytool o el basado en navegador Generador de keystore para Android. Para iOS, el generador de certificados iOS crea una solicitud de firma de certificado y .p12 sin un Mac.
Paso 3: Un workflow de GitHub Actions
Este workflow compila ambas plataformas en paralelo con un tag de versión y las sube a los canales de testing internos.
# .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
Por qué aparecen estas líneas:
- Dos carpetas de provisioning profiles. Xcode 16 y versiones posteriores leen perfiles desde
~/Library/Developer/Xcode/UserData/Provisioning Profiles. Las versiones anteriores de Xcode usaban~/Library/MobileDevice/Provisioning Profiles. Tener ambas evita el error “No profiles for ‘com.example.app’ were found”. set-key-partition-listevita que macOS pida acceso al keychain, lo que colgaría un job de CI.- Versión de JDK. Usa el JDK que tu
cordova-androidrelease documenta. Las versiones recientes compilan con JDK 17. - Números de build. Subir
android-versionCodeyios-CFBundleVersionenconfig.xmlantes del build, por ejemplo a partir del número de ejecución de CI, o las tiendas rechazarán la subida por duplicado.
Puedes cambiar el altool upload por el de fastlane upload_to_testflight y la acción Play por fastlane supply si tu equipo ya usa fastlane.
Paso 4: El mismo pipeline en otros servicios de CI
Los comandos no cambian entre CIs. Solo cambian la selección del runner y la sintaxis de los secrets.
| CI | Runner de Android | Runner de iOS | Notas |
|---|---|---|---|
| Acciones de GitHub | ubuntu-latest |
macos-26 / macos-latest |
Los minutos de macOS cuestan más que Linux en repositorios privados |
| GitLab CI | Cualquier runner de Linux | Runners de macOS alojados (beta, Premium y Ultimate) o self-hosted | imágenes como macos-26-xcode-26 |
| Bitbucket Pipelines | Imagen Docker de Linux | Solo Mac self-hosted | ver Guía de iOS para Bitbucket |
| Gitea / Forgejo | Runner de Linux | Mac autoalojado en modo host | ver Guía de iOS para Gitea |
| Bitrise | Stacks de Linux | Stacks de macOS | incluye pasos de Cordova (prepare, archive) |
| Codemagic | Linux | instancias de Mac mini | documenta los builds de Ionic Cordova en codemagic.yaml |
| Azure DevOps | ubuntu-latest |
macOS-latest |
tareas para instalar certificados y perfiles |
Para ver el detalle por funcionalidad, lee Comparativa de plataformas CI/CD para apps Cordova.
Paso 5: Live Updates para Cordova
Con Live Updates puedes lanzar correcciones de HTML, CSS y JavaScript sin pasar por la revisión de la tienda. App Center CodePush terminó en marzo de 2025, Appflow Live Updates finaliza el 31 de diciembre de 2027, y los antiguos plugins de hot code push de la comunidad ya no tienen mantenimiento. Una opción con mantenimiento es actualizaciones Live Update de Capgo con el @capgo/cordova-updater plugin, que usa el mismo backend, los mismos canales y CLI que el updater de Capacitor.
Requisitos
- Cordova CLI 12+
cordova-android13+ y/ocordova-ios7+ (el plugin depende de sushttps://localhost/yapp://localhost/esquemas)- No lo uses
cordova-plugin-ionic-webviewcon ello. Ionic WebView omite los scheme handlers de Cordova, por lo que los bundles descargados no se cargarían.
Instalar el plugin
Crea la app en Capgo igual que para Capacitor, y luego instala el plugin con tu app ID de Capgo:
cordova plugin add @capgo/cordova-updater \
--variable APP_ID=com.example.app \
--variable DEFAULT_CHANNEL=production
cordova prepare android ios
DEFAULT_CHANNEL, UPDATE_URL, y AUTO_UPDATE son variables de instalación opcionales. Ejecuta cordova prepare de nuevo cada vez que las cambies.
Confirmar cada lanzamiento
Después de deviceready, el plugin está disponible como cordova.plugins.Updater. Llama a notifyAppReady() en cada lanzamiento, o el plugin trata el nuevo bundle como dañado y hace 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 })
}
})
Subir bundles desde CI
Añade un job que construya la capa web y la suba, para cambios solo de 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 }}
Las actualizaciones en vivo solo reemplazan el bundle web. Añadir, eliminar o actualizar un plugin de Cordova sigue requiriendo un nuevo build para la tienda. Capgo compara los plugins nativos registrados para el bundle en vivo con tu proyecto y marca las subidas incompatibles; ver compatibilidad con live update. La configuración completa está en la documentación de Cordova updater.
En cuanto al build, la Build de Capgo documentación cubre solo proyectos de Capacitor (sube el proyecto nativo generado por cap sync). Los binarios de Cordova siguen compilándose en tu CI, como se muestra arriba.
Opcional: migrar a Capacitor más adelante
No necesitas abandonar Cordova para tener un pipeline funcional y actualizaciones en vivo. La migración es una decisión aparte, que vale la pena cuando:
- Quieres builds de iOS sin mantener runners de macOS. Capgo Build compila apps de Capacitor desde un job de CI en Linux.
- Los plugins en los que dependes dejaron de recibir actualizaciones de Cordova, pero tienen equivalentes de Capacitor.
- Quieres soporte para Swift Package Manager y versiones actuales de Android Gradle Plugin sin parchear plataformas.
La migración mantiene tu www code. Añades Capacitor, migras los plugins (la mayoría de los plugins de Cordova funcionan tal cual) y reemplazas algunos que tienen equivalentes nativos de Capacitor. Tu ID de app y tus canales de Capgo se mantienen igual; solo cambia el plugin de actualizaciones de @capgo/cordova-updater a @capgo/capacitor-updater. Los pasos están en Migración de Cordova a Capacitor, y la Página de solución de Cordova a Capacitor incluye cómo obtener ayuda. Tras la migración, el CI anterior se reduce a un build web, cap sync, y una build request llamada por plataforma, como se describe en Comparativa de plataformas CI/CD para apps Capacitor.
Solución de problemas de builds de Cordova en CI
| Error | Causa y solución |
|---|---|
Current working directory is not a Cordova-based project |
config.xml o www/ falta. Ejecuta tu build web antes de los comandos de Cordova, o crea uno vacío www/. |
No platforms added to this project |
Las plataformas no aparecen en package.json. Ejecuta cordova platform add localmente una vez y haz commit package.json. |
Could not find an installed version of Gradle |
Recientes cordova-android usa el wrapper de Gradle. Actualiza la plataforma en vez de instalar Gradle globalmente. |
Unsupported class file major version |
El JDK del runner no coincide con el cordova-android que espera. Fíjalo con setup-java. |
No signing certificate "iOS Distribution" found |
Usa Apple Distribution como identidad, y verifica que el .p12 contenga la clave privada. |
errSecInternalComponent |
Keychain bloqueado o lista de particiones incompleta. Ejecuta de nuevo los comandos de configuración del keychain. |
| Upload rechazado: SDK demasiado antiguo | La imagen del runner usa un Xcode anterior al 26. Selecciona macos-26 o configura Xcode con xcode-select. |
Subida a Play: Version code has already been used |
Subir android-versionCode para cada subida. |
Resumen
Un pipeline de Cordova en 2026 es un job de CI por plataforma: un runner Linux para el AAB de Android, un runner macOS con Xcode 26 para el IPA, secretos decodificados en un archivo generado build.json, y claves de API para ambas tiendas. Cualquier CI habitual puede ejecutarlo. Añade @capgo/cordova-updater y un bundle upload un trabajo para correcciones solo web, y tienes las piezas de build, store y over-the-air que Appflow y App Center solían ofrecer. Migrar a Capacitor sigue siendo una opción para más adelante, no un requisito.