Saltar al contenido principal

CI/CD para Capacitor: Trampas comunes y soluciones

Los errores de CI/CD que rompen los compilados de Capacitor: firma de iOS, ejecutores de macOS, Xcode 26, sincronización de capa caducada, códigos de versión, rechazos de tienda y soluciones para cada uno.

Créditos del artículo

Martín Donadieu

Escritor

Valeria

Revisor

Jordan

Editor

CI/CD para Capacitor: Problemas y soluciones comunes

La mayoría de los errores de CI/CD de Capacitor provienen de la misma lista corta: firma de iOS code, herramientas macOS faltantes o desactualizadas, una compilación web que nunca llegó al proyecto nativo, números de compilación reutilizados y requisitos de tienda que solo fallan en el momento de subir.

Si está configurando una pipeline desde cero, lea Configurando CI/CD para aplicaciones Capacitor primero, y luego utilice esta lista para endurecerla.

Etapa 1: Construcción de la capa web

Problema: la aplicación nativa envía una compilación web antigua

Síntoma: CI tiene éxito, la aplicación se instala, pero muestra la interfaz de usuario de ayer.

Causa: cap sync copias lo que está en webDir en ese momento. Si se ejecuta el pipeline cap sync antes de la compilación web, o la compilación web escribe a un carpeta diferente que webDir Solución: capacitor.config.tsEl proyecto nativo obtiene archivos obsoletos.

Fix: Siempre ejecuta los pasos en este orden, y falla si el carpeta de salida está vacía.

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

para Vite, webDir coincide con tu salida del empaquetador (dist para Rollup, www para Angular con Ionic, build para algunas configuraciones de React).

Pitfall: dev server URL left in the config

Síntoma: la compilación de lanzamiento muestra una pantalla en blanco o intenta cargar http://192.168.x.x:5173.

Causa: server.url en capacitor.config.ts se configuró para el recarga en vivo y se comprometió.

Solución: nunca se comite server.url. Leerlo de una variable de entorno que CI nunca establece:

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

Trampa: las variables de entorno se hornearon en el momento equivocado

Síntoma: the production app talks to the staging API.

Causa: Vite, webpack y Angular establecen variables de entorno inline en tiempo de compilación. El valor presente cuando bun run build ran es el que se encuentra en el binario, y en cualquier live update construido a partir del mismo trabajo.

Fix: Establezca variables de entorno específicas para el ambiente antes de la compilación web, por tarea, y compile bundles separados para staging y producción. No intente intercambiarlos después. cap sync.

Trampa: desfase de archivo de bloqueo

Síntoma: Una versión del plugin en CI difiere de la instalada en tu máquina, y la compilación nativa falla por faltantes de símbolos.

Fix: Pitfall: drift del archivo de bloqueo bun install --frozen-lockfile (o npm ci). Pestaña @capacitor/core, @capacitor/ios, @capacitor/android, y @capacitor/cli hasta la misma versión. Las versiones desincronizadas son una fuente frecuente de errores nativos; consulte Corregir errores de versión de Capacitor.

Etapa 2: La herramienta de cadena nativa

Trampa: versión de Node, JDK o Xcode incorrecta

Síntoma: Unsupported class file major version, The engine "node" is incompatible, o errores de Xcode sobre SDK características.

Causa: imagenes de ejecución de corredor hospedado cambian, y Capacitor 8 tiene firmes mínimos.

Tool Capacitor 8 requisito
Node.js 22 o más reciente
JDK 21
Xcode 26 o más reciente
Objetivo de compilación para iOS 15.0
Android minSdkVersion / targetSdkVersion 24 / 36

Solución: pinar cada versión explícitamente en la canalización en lugar de confiar 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'

Pitfall: construir contra un Xcode Apple ya no acepta

Síntoma: el subir falla con un mensaje que indica que la aplicación se construyó con un SDK no soportado.

Causa: desde el 28 de abril de 2026, App Store Connect requiere Xcode 26 y el iOS 26 SDK. Los Macs autoadministrados y las imágenes de ejecución más antiguas siguen utilizando Xcode 16.

Solución: seleccione una macos-26 imagen o instale Xcode 26 en sus ejecutores. Detalles en Requisito de Xcode 26 de Apple para Capacitor apps.

Pitfall: confusión entre CocoaPods y SPM

Síntoma: xcodebuild: error: 'App.xcworkspace' does not existo no se encuentran los pods.

Causa: nuevos Capacitor 8 proyectos utilizan el gestor de paquetes Swift y se construyen ios/App/App.xcodeprojProyectos más antiguos usan CocoaPods y construyen ios/App/App.xcworkspace después pod install. Las pipelines copiadas de tutoriales más antiguos asumen el workspace.

Solución: verifique qué una utiliza su proyecto y construya el archivo correcto. Si está migrando, consulte Cómo migrar su Capacitor aplicación a SPM.

Fallas comunes: Los plugins de Android rompen después de una actualización de Gradle

Síntoma: Namespace not specified, package attribute is deprecated, o errores de un plugin’s build.gradle después de actualizar Android Studio.

Solución: pinche el plugin de Gradle de Android en android/build.gradleactualiza en una rama, y actualiza los plugins primero. Los errores específicos se cubren en Soluciona errores de compilación del plugin Capacitor con AGP 9.

Etapa 3: Code de firma

Trampa: la firma de iOS funciona solo en tu Mac

Síntoma: No signing certificate "iOS Distribution" found, No profiles for 'com.example.app' were foundo errSecInternalComponent.

Causa: La llave de tu Mac almacena el certificado y Xcode descarga perfiles para ti. Un ejecutor de CI no tiene ninguno, y su llave de acceso está bloqueada en una sesión no interactiva.

Solución: importa el certificado en una llave de acceso temporal desbloqueada e instala el perfil donde Xcode 16 y posteriores buscan:

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

Utiliza el nombre de identidad Apple Distribution, no la legado iOS Distribution. fastlane’s setup_ci plus match automates this. Capgo Build takes the certificate and profile as environment variables and does the keychain work on its own machines, so the Linux job never touches security.

Pitfall: certificados expirados o revocados

Symptom: Un pipeline que funcionaba durante un año falla de repente.

Cause: Apple distribution certificates and provisioning profiles expire after a year. A teammate creating a new certificate in Xcode can also invalidate the profile your CI uses.

Fix: Coloque las fechas de vencimiento en el calendario de su equipo, mantenga un certificado de distribución compartido para CI y revise antes de compilar. bunx @capgo/cli@latest build prescan --platform ios Verifica la expiración del certificado, la contraseña y la pareja de perfil antes de subir cualquier cosa.

Pitfall: Apple ID logins and two-factor prompts

Síntoma: fastlane se queda esperando un code de 6 dígitos.

Solución: utilice una clave de 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.

Trampa: secretos base64 que no se pueden descodificar

Síntoma: MAC verification failed, invalid keystore formatCausa: base64: invalid input.

Cause: generado con OpenSSL 3 por defecto que macOS no puede leer. .p12 creado con OpenSSL 3 por defecto que macOS no puede leer.

Solución: codificar en una sola línea, y usar -legacy cuando se 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 de Android perdido

Síntoma: no puedes firmar una actualización porque nadie tiene el keystore.

Solución: con Play App Signing, el keystore es la clave de carga, y el soporte de la consola de Play puede registrar una nueva. Almacena el keystore en tus secretos de CI y en un respaldo offline. Nunca lo dejes vivo solo en una laptop. El generador de keystore de Android crea uno nuevo si estás empezando de nuevo.

Etapa 4: Diseño de la canalización

Pitfall: compilaciones nativas en cada solicitud de extracción

Síntoma: revisión de PR lenta y un gran cargo por minutos en macOS.

Causa: Los builds de iOS en ejecutores macOS hospedados son los minutos más costosos en la mayoría de los planes de CI, y la mayoría de los commits solo tocan JavaScript.

Solución: ejecutar lint, pruebas y la construcción web en cada PR. Ejecutar builds nativos en etiquetas de lanzamiento o fusiones para mainPara PR previas, envía el paquete web a un canal Capgo en lugar de construir un binario, como se describe en Comparando plataformas de CI/CD para aplicaciones Capacitor.

Comparar plataformas CI/CD para aplicaciones __CAPGO_KEEP_0__

Solución: utilice una matriz para que tanto las plataformas construyan en paralelo, y configure fail-fast: false Un problema de firmado de iOS no cancela un buen build de Android.

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

Pitfall: sin caché, o el caché equivocado

Simptomá: cada compilación descarga dependencias de Gradle y CocoaPods desde cero, o una compilación de staging envía la configuración de producción desde una caché obsoleta.

Solución: caché ~/.gradle/caches, ~/.gradle/wrapper, y ios/App/Pods Almacenar cachés por entorno. Con Capgo Build, se puede dividir la caché de compilación por aplicación. --cache-key prod y --cache-key staging, o saltarse con --no-cache para una construcción limpia.

Pitfall: rutas de repositorio único

Simptomá: could not find capacitor.config o plugins faltantes en el proyecto nativo.

Solución: run Capacitor commands from the app package, and point tools at hoisted node_modulesEl Capgo CLI acepta --path y --node-modules para esto.

Etapa 5: Envío de la tienda

Trampa: números de compilación reutilizados

Síntoma: “La versión del paquete debe ser mayor que la versión subida anteriormente” en iOS, o “La versión code ya ha sido utilizada” en Google Play.

Solución: generar el número en CI. Con fastlane, leer la última compilación de TestFlight y sumar uno. Con Capgo Build, esto es el default: obtiene el último número de compilación de App Store Connect o el más alto versionCode desde Google Play y lo incrementa.

Pitfall: compilaciones atascadas en TestFlight

Síntoma: el subir tiene éxito pero los probadores nunca ven la compilación.

Causa: falta de cumplimiento de exportación.

Solución: decláralo una vez en ios/App/App/Info.plist si solo usas cifrado estándar:

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

Pitfall: rechazos del manifiesto de privacidad

Síntoma: correo electrónico de Apple sobre la razón requerida faltante API (ITMS-91053).

Solución: agregar un PrivacyInfo.xcprivacy a la configuración de destino de la aplicación y actualizar los plugins que envían sus propios. guía de privacidad para aplicaciones Capacitor.

Fallas: tipo de artefacto incorrecto

Solución: Google Play necesita un AAB (bundleRelease), no un APK. bundleRelease sigue teniendo éxito sin un release configura la firma y produce un AAB sin firmar que Play rechaza, así que signingConfigs.release in android/app/build.gradle Primero. iOS requiere una exportación a la Tienda de Aplicaciones, no un IPA de desarrollo o ad hoc. Verifique el método de exportación en su paso de compilación.

Etapa 6: Actualizaciones en vivo

Obstáculo: enviar un live update que requiere una nueva compilación nativa

Síntoma: después de una actualización por aire, la aplicación se bloquea al llamar a un método de plugin que no existe en el binario instalado.

Causa: la biblioteca web depende de una versión de plugin más nueva que la compilada en la aplicación en los dispositivos de los usuarios.

Solución: deje que el pipeline decida. build needed sale 0 cuando las dependencias nativas coinciden con lo que está vivo en el canal y 1 cuando se requiere un nuevo 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

también fuerza el camino nativo cuando los archivos bajo ios/, android/o capacitor.config.* cambian. El patrón completo está en Elija automáticamente live update o compilación nativay las reglas de compatibilidad están en compatibilidad nativa.

La solución que elimina más obstáculos

Si solo cambias una cosa, mueve la compilación y firma de iOS fuera de tus ejecutores de CI. La configuración de Keychain, las actualizaciones de Xcode, los costos de macOS y la instalación de perfiles desaparecen de tu pipeline cuando un trabajo de Linux le entrega el proyecto preparado a Capgo Construcción:

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

La mayoría de los obstáculos en las etapas 1, 5 y 6 siguen siendo aplicables, ya que se refieren a tu proyecto, no al ejecutor. Para más información sobre depuración de tareas fallidas, consulta Corrigiendo errores de compilación en pipelines CI/CD de Capacitor.

Referencia rápida

Error Trampa Sección
Interfaz antigua en una nueva compilación cap sync antes de la compilación web Etapa 1
No profiles for ... were found Perfil no instalado o no coincidente Etapa 3
errSecInternalComponent Cadena de claves bloqueada Etapa 3
MAC verification failed Contraseña incorrecta o OpenSSL 3 .p12 Etapa 3
Unsupported class file major version JDK incorrecto Etapa 2
SDK demasiado antiguo en carga Xcode mayor a 26 Etapa 2
La versión del paquete debe ser mayor Número de compilación reutilizado Etapa 5
La versión code ya se ha utilizado Reutilizado versionCode Etapa 5
Crash después de live update Cambios nativos enviados por aire Etapa 6
Actualizaciones en vivo para aplicaciones Capacitor

Cuando haya un error en la capa de web, envíe la corrección a través de Capgo en lugar de esperar días a la aprobación de la tienda de aplicaciones. Los usuarios obtienen la actualización en segundo plano mientras que los cambios nativos siguen en el camino de revisión normal.

Apoyo humano de Martin

Iniciar ahora

Últimas noticias de nuestro blog

Capgo te da las mejores herramientas que necesitas para crear una aplicación móvil profesional.