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

Martin 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 construcción web que nunca llegó al proyecto nativo, números de construcción reutilizados y requisitos de tienda que solo fallan en el momento de carga.

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 construcció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 Siempre ejecuta los pasos en este orden, y falla si la carpeta de salida está vacía. webDir in capacitor.config.tscoincida con el resultado del empaquetador (

para Vite, 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 Rollup, webDir coincide con tu salida del empaquetador (dist para swc, 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: variables de entorno horneadas en el momento equivocado

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

Causa: Vite, webpack y Angular establecen variables de entorno en línea 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 trabajo, y compile bundles separados para staging y producción. No intente intercambiarlos después. cap sync.

Pitfall: desfase de archivo de bloqueo

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

Fix: Pitfall: desplazamiento de archivo de bloqueo bun install --frozen-lockfile (o npm ci). Pega @capacitor/core, @capacitor/ios, @capacitor/android, y @capacitor/cli al mismo versión. Las versiones desincronizadas son una fuente frecuente de errores nativos; consulte Corrija 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 requerimiento
Node.js 22 o más reciente
JDK 21
Xcode 26 o más reciente
Objetivo de despliegue de iOS 15.0
Android minSdkVersion / targetSdkVersion 24 / 36

Solución: pin 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'

Falla: construir contra un Xcode Apple ya no acepta

Síntoma: la subida 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 exist, o 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 utilizan 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 cuál 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.gradle, actualiza en una rama y actualiza los plugins primero. Los errores específicos se cubren en Soluciona errores de compilación de plugin con AGP 9 con Capacitor.

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 found, o errSecInternalComponent.

Causa: La llave de tu Mac almacena el certificado y Xcode descarga los 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/"

Usa el nombre de identidad Apple Distribution, no el 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 su calendario de equipo, mantenga un certificado de distribución compartido para CI y verifique antes de construir. 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 código de 6 dígitos code.

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

Pitfall: secretos base64 que no se descodifican

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 vivir 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 pipeline

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 alojados 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 el build web en cada PR. Ejecutar builds nativos en etiquetas de lanzamiento o fusiones para mainPara PR previos, 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.

Pitfall: construir iOS y Android de forma secuencial

Solución: Comparar plataformas de CI/CD para aplicaciones __CAPGO_KEEP_0__ fail-fast: false un problema de firma de iOS no cancela un buen build de Android.

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

Pitfall: sin caché, o el caché incorrecto

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 Particiona las cachés por ambiente. Con Capgo Build, la caché de compilación por aplicación se puede dividir con --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: Submisión de tienda

Obstáculo: 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 valor predeterminado: 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 del app y actualizar los plugins que envían sus propios. guía de privacidad para aplicaciones Capacitor.

Fallas comunes: 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 configúrala 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

Pitfall: enviar un live update que necesita una nueva compilación nativa

Simptomático: 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 la canalización 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 fuerce el camino nativo cuando los archivos bajo ios/, android/o capacitor.config.* cambian. El patrón completo está en Elige automáticamente live update o compilación nativa, y 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

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 Corrigiendo errores de compilación en Capacitor pipelines de CI/CD.

Referencia rápida

Error Error Section
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 Cambio nativo enviado por aire Etapa 6
Actualizaciones en vivo para aplicaciones Capacitor

Cuando haya un error en la capa 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.

soporte humano de Martin

Iniciar ahora

Últimas noticias de nuestro Blog

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