Saltar al contenido principal
CI/CD Migración Guías

CI/CD para apps de Cordova en 2026: Guía completa de configuración

Configura CI/CD para apps Cordova en 2026: builds firmados de Android AAB e iOS IPA, subidas a tiendas, opciones de live update y cuándo migrar a Capacitor.

App de Apache Cordova pasando por un pipeline CI/CD hacia App Store y Google Play

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-ios versió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-android al día para que su valor targetSdkVersion sigue.

Paso 1: Haz que el proyecto sea reproducible

El CI solo puede reconstruir lo que está en el repositorio. Antes de escribir cualquier workflow:

  1. Haz commit de package.json y del lockfile. Cordova guarda las plataformas y los plugins en la cordova sección de package.json, así que cordova prepare los restaura.
  2. No hagas commit platforms/ o plugins/. Se generan automáticamente.
  3. Instala el CLI de Cordova como dev dependency para que cada máquina use la misma versión:
bun add -d cordova
  1. Mueve los valores de firma fuera de config.xml y pásalos a un build.json que lea los secrets en el build (siguiente paso).
  2. Fija la versión de Node.js en .nvmrc o package.json engines.

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-list evita que macOS pida acceso al keychain, lo que colgaría un job de CI.
  • Versión de JDK. Usa el JDK que tu cordova-android release documenta. Las versiones recientes compilan con JDK 17.
  • Números de build. Subir android-versionCode y ios-CFBundleVersion en config.xml antes 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-android 13+ y/o cordova-ios 7+ (el plugin depende de sus https://localhost/ y app://localhost/ esquemas)
  • No lo uses cordova-plugin-ionic-webview con 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.

Live updates para apps Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.