Saltar al contenido principal
CI/CD

Build automático de Capacitor para iOS con GitHub actions y certificado

Cómo configurar un pipeline de CI/CD para tu app Ionic de iOS con fastlane y GitHub Actions en 5 min (2024)

Build automático de Capacitor para iOS con GitHub actions y certificado Capgo ilustración del blog

Configurar CI/CD para apps de Capacitor puede ser complejo y llevar mucho tiempo. Esto es lo que necesitas saber:

Ahora recomendamos usar Capgo Build con el Capgo CLI para builds nativos de Capacitor. Esta guía de Fastlane es para equipos que mantienen pipelines de GitHub Actions existentes, pero para nuevos builds de iOS se recomienda usar Capgo CLI para no tener que gestionar Fastlane, runners de Xcode, certificados y scripts de upload.

Capgo Build para CI/CD por Capgo

Olvídate de mantener Fastlane, runners de Xcode, certificados, provisioning profiles y scripts de upload. Build de Capgo ejecuta builds nativos de iOS firmados desde tu pipeline de CI/CD actual:

  • Funciona con tu pipeline: Dispara Capgo Build desde GitHub Actions, GitLab CI, Jenkins o scripts locales tras tu web build y npx cap sync.
  • Firma mediante secretos de CI: Mantén las claves de App Store Connect, certificados, provisioning profiles, contraseñas y team IDs en tus propios secretos de CI.
  • Sin mantenimiento de runners nativos: Capgo Build ofrece entornos de build de Apple gestionados, para que no tengas que administrar runners de macOS, imágenes de Xcode o lanes de Fastlane.
  • Artifacts y envío: Descarga artifacts firmados para QA o envía builds de release mediante Capgo CLI.

Precios

  • Los planes de Capgo desde $12/mes
  • Incluye actualizaciones OTA y unos 15 Native Build al mes
  • Los minutos de build extra se cobran por minuto mediante créditos

Configura Capgo Build en tu CI/CD

Guía de configuración manual

Esto es lo que tienes que hacer:

Continuous Delivery para iOS con Fastlane, GitHub Actions y certificados

Requisitos

Antes de continuar con el tutorial:

  • Asegúrate de tener Fastlane instalado en tu máquina de desarrollo.
  • Asegúrate de formar parte del programa de desarrolladores de iOS.

Información importante sobre el precio

Precio de GitHub Action

https://github.com/features/actions

El servicio es ‘gratis’ hasta el límite, según la máquina elegida.
Vamos a utilizar un macOS máquina, en la captura puedes ver su precio y límites (precios vigentes al crear el tutorial, podrían cambiar en el futuro)

Una vez vistos los requisitos y precios, continuemos.

Nota: En este post asumo que ya tienes la app creada en App Store Connect. ¡Fastlane se encargará de copiar la información importante!

Qué aprenderás en el tutorial

Pasos a seguir en el post

  1. Uso de App Store Connect API con Fastlane
    • Requisitos:
      • Crear una clave API de App Store Connect
      • Usar una clave API de App Store Connect
  2. Copiar archivos de Fastlane
  3. Configurar GitHub Actions

1. Uso de App Store Connect API con Fastlane

A partir de febrero de 2021, todos los usuarios deben usar la autenticación de dos factores o la verificación en dos pasos para iniciar sesión en App Store Connect. Esta capa adicional de seguridad para tu Apple ID ayuda a garantizar que seas la única persona que pueda acceder a tu cuenta.
Desde Soporte de Apple

Requisitos

Para que Fastlane pueda usar API de App Store Connect para subir tu app, necesitas proporcionar lo siguiente tres detalles:

  1. ID de emisor
  2. ID de clave
  3. Archivo de clave o contenido de la clave

Cómo obtener una clave API de App Store Connect

Para generar claves, debes tener permisos de Admin en App Store Connect. Si no los tienes, puedes redirigir a la persona encargada a este artículo.

  1. Inicia sesión en App Store Connect.

  2. Selecciona Usuarios y acceso.

Acceso de usuarios de App Store Connect

3 — Selecciona la pestaña Integración.

Integración de API en App Store Connect

  1. Haz clic en Generate API Key o en el botón Add (+).

Crear claves API en App Store Connect

  1. Asigna un nombre a la clave. El nombre es solo para tu referencia y no forma parte de la clave.

Nombre de la clave API en App Store Connect

6 — En Access, selecciona el rol para la clave. Los roles que se aplican a las claves son los mismos que se aplican a los usuarios de tu equipo. Ver permisos de rol. Recomendamos seleccionar Gestor de apps.

  1. Haz clic en Generar.

El acceso de una clave API no se puede limitar a apps específicas.

En la página aparecerán el nombre de la nueva clave, su ID, un enlace de descarga y más info.

Claves de descarga de App Store Connect

Aquí puedes obtener los tres datos necesarios.
<1> ID del issue. (APPLE_ISSUER_ID secreto)
<2> ID de la clave. (APPLE_KEY_ID secreto)
<3> Haz clic en “Download API Key” para descargar tu clave privada API. El enlace de descarga solo aparece si aún no se ha descargado la clave privada. Apple no conserva una copia de la clave privada, así que solo podrás descargarla una vez.

🔴 Guarda tu clave privada en un lugar seguro. Nunca compartas tus claves ni las guardes en un repositorio code, ni las incluyas en code del lado del cliente.

Usar una clave API de App Store Connect

El archivo API Key (el archivo p8 que descargaste), el key ID y el issuer ID son necesarios para crear el token JWT de autorización. Hay varias formas de pasar esta información a Fastlane. Elegí usar la nueva action de Fastlane app_store_connect_api_key. Puedes conocer otras formas en la documentación de Fastlane. Te muestro este método porque creo que es la forma más fácil de trabajar con la mayoría de los CI que hay, donde puedes configurar variables de entorno.

Por favor, convierte el archivo p8 que descargaste a Base64 y guárdalo como un secret (APPLE_KEY_CONTENT).

base64 -i APPLE_KEY_CONTENT.p8 | pbcopy

¡Ahora podemos gestionar App Store Connect con Fastlane usando la clave API, genial!

2. Certificados

Abre Xcode y ve a Configuración > Cuentas > Apple ID > Equipos y selecciona tu equipo.

identidades de firma de Code

Haz clic en Gestionar certificados.

Si aún no has creado un certificado, puedes crear uno nuevo.

Haz clic en + y selecciona Distribución de Apple

Distribución de Apple

Luego tienes que ir al Keychain para descargar el certificado como un .p12 archivo.

Para ello, tienes que ir al Keychain y cambiar al login keychain y luego a la pestaña Mis certificados.

Mis certificados

Luego puedes seleccionar el certificado que quieras descargar. (Fíjate en la fecha del certificado)

Y luego haz clic derecho en la clave privada del certificado y selecciona Exportar.

Elige el formato de archivo Intercambio de información personal (.p12).

Esto descargará el certificado como un .p12 archivo.

Abre el archivo en la terminal y usa este comando para convertirlo a Base64:

base64 -i BUILD_CERTIFICATE.p12 | pbcopy

Esto se convertirá en tu BUILD_CERTIFICATE_BASE64 secreto. Además, cuando se te solicite, introduce la contraseña del certificado. Esta será tu P12_PASSWORD secreto.

3. Perfiles de aprovisionamiento

Abrir Apple Developer y selecciona el equipo correcto.

Luego crea un nuevo perfil haciendo clic en +

Crear un nuevo perfil

Y selecciona App Store Connect.

Selecciona App Store Connect

Luego debes seleccionar la app correcta; cuidado, no puedes usar wildcard o la firma fallará.

Selecciona la app correcta

Selecciona el certificado que creaste antes (revisa la fecha de expiración, debe ser el mismo día y mes que hoy) y haz clic en Continuar.

Selecciona el certificado correcto

Finalmente, ponle nombre al perfil y haz clic en Generar.

El nombre se usará para identificar el perfil en Fastlane, con el valor de APPLE_PROFILE_NAME.

Generar el perfil

Puedes descargar el perfil como un .mobileprovision archivo.

Descargar el perfil

Por favor, convierte el perfil a Base64 y guárdalo como un secreto (BUILD_PROVISION_PROFILE_BASE64).

base64 -i BUILD_PROVISION_PROFILE.mobileprovision | pbcopy

4. Copiar los archivos de Fastlane

Fastlane es una librería de Ruby creada para automatizar tareas comunes de desarrollo móvil. Con Fastlane, puedes configurar “lanes” personalizadas que agrupan una serie de “actions” que realizan tareas que normalmente harías usando Android Studio. Puedes hacer mucho con Fastlane, pero para este tutorial usaremos solo unas pocas acciones principales.

Crea la carpeta Fastlane en la raíz de tu proyecto Capacitor/Ionic y añade el Fastfile allí:

  • Carpeta: <project-root>/fastlane/
  • Archivo: <project-root>/fastlane/Fastfile

Esto está al mismo nivel que package.json, capacitor.config.*, y la ios/ la carpeta. No la crees dentro de ios/App/.

platform :ios do
  desc 'Export ipa and submit to TestFlight'
  lane :beta do
    keychain_info = { keychain_name: "ios-build-#{Time.now.to_i}.keychain", keychain_password: SecureRandom.uuid }
    
    begin
      setup_signing(keychain_info)
      bump_build_number
      build_app_with_signing(keychain_info)
      submit_to_testflight
    ensure
      cleanup_keychain(keychain_info)
    end
  end

  private_lane :setup_signing do |options|
    create_keychain(
      name: options[:keychain_name],
      password: options[:keychain_password],
      unlock: true,
      timeout: 0,
      lock_when_sleeps: false, 
      add_to_search_list: true
    )
    import_cert(options)
    install_profile
    update_project_settings
  end

  lane :bump_build_number do
		file = File.read('../package.json')
		data_hash = JSON.parse(file)
		api_key = app_store_connect_api_key(
      key_id: ENV['APPLE_KEY_ID'],
      issuer_id: ENV['APPLE_ISSUER_ID'],
      key_content: ENV['APPLE_KEY_CONTENT'],
      is_key_content_base64: true,
      duration: 1200,
      in_house: false
    )
		build_num = app_store_build_number(
      api_key: api_key,
			app_identifier: ENV['BUNDLE_IDENTIFIER'],
			live: false
    )
		build_num = build_num + 1
		UI.message("Bumped build number to #{build_num}")
		increment_build_number(
			build_number: build_num,
			xcodeproj: "./ios/App/App.xcodeproj",
			skip_info_plist: true
		)
	end

  private_lane :import_cert do |options|
    cert_path = "#{Dir.tmpdir}/build_certificate.p12"
    File.write(cert_path, Base64.decode64(ENV['BUILD_CERTIFICATE_BASE64']))
    import_certificate(
      certificate_path: cert_path,
      certificate_password: ENV['P12_PASSWORD'] || "",
      keychain_name: options[:keychain_name],
      keychain_password: options[:keychain_password],
      log_output: true
    )
    File.delete(cert_path)
  end  
  
  private_lane :cleanup_keychain do |options|
    delete_keychain(
      name: options[:keychain_name]
    )
  end  

  private_lane :install_profile do
    profile_path = "#{Dir.tmpdir}/build_pp.mobileprovision"
    File.write(profile_path, Base64.decode64(ENV['BUILD_PROVISION_PROFILE_BASE64']))
    UI.user_error!("Failed to create provisioning profile at #{profile_path}") unless File.exist?(profile_path)
    ENV['PROVISIONING_PROFILE_PATH'] = profile_path
    install_provisioning_profile(path: profile_path)
    File.delete(profile_path)
  end

  private_lane :update_project_settings do
    update_code_signing_settings(
      use_automatic_signing: false,
      path: "./ios/App/App.xcodeproj",
      code_sign_identity: "iPhone Distribution",
      profile_name: ENV['APPLE_PROFILE_NAME'],
      bundle_identifier: ENV['BUNDLE_IDENTIFIER'],
      team_id: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
    update_project_team(
      path: "./ios/App/App.xcodeproj",
      teamid: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
  end

  private_lane :build_app_with_signing do |options|
    unlock_keychain(
      path: options[:keychain_name],
      password: options[:keychain_password],
      set_default: false
    )
    build_app(
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      configuration: "Release",
      export_method: "app-store",
      output_name: "App.ipa",
      export_options: {
        provisioningProfiles: {
          ENV['BUNDLE_IDENTIFIER'] => ENV['APPLE_PROFILE_NAME']
        }
      },
      xcargs: "-verbose",
      buildlog_path: "./build_logs",
      export_xcargs: "-allowProvisioningUpdates",
    )
  end   

  private_lane :submit_to_testflight do
    api_key = app_store_connect_api_key(
      key_id: ENV['APPLE_KEY_ID'],
      issuer_id: ENV['APPLE_ISSUER_ID'],
      key_content: ENV['APPLE_KEY_CONTENT'],
      is_key_content_base64: true,
      duration: 1200,
      in_house: false
    )
    pilot(
      api_key: api_key,
      skip_waiting_for_build_processing: true,
      skip_submission: true,
      distribute_external: false,
      notify_external_testers: false,
      ipa: "./App.ipa"
    )
  end
end

5. Configuración de secrets

GitHub Actions usa los secrets del repositorio que configures en el siguiente paso. Solo necesitas un .env archivo local si quieres ejecutar o probar Fastlane desde tu propia máquina.

Para pruebas locales, crea <project-root>/fastlane/.env junto al Fastfile. No hagas commit de este archivo. Añade fastlane/.env a tu .gitignore primero (o verifica que ya esté ignorado). Aquí tienes un ejemplo:

APP_STORE_CONNECT_TEAM_ID=UVTJ336J2D
BUNDLE_IDENTIFIER=ee.forgr.testfastlane
# See previous section for these secrets
BUILD_CERTIFICATE_BASE64=
BUILD_PROVISION_PROFILE_BASE64=
APPLE_KEY_ID=
APPLE_ISSUER_ID=
APPLE_KEY_CONTENT=
P12_PASSWORD=
APPLE_PROFILE_NAME=

Obtener el APP_STORE_CONNECT_TEAM_ID

Ve a Centro de Desarrolladores y desplázate hacia abajo hasta Membership details sección. El Team ID es el valor que debes configurar en APP_STORE_CONNECT_TEAM_ID secreto.

app-store-connect-team-id

Obtener el BUNDLE_IDENTIFIER

  1. Abre Xcode
  2. Haz doble clic en App en el project navigator
  3. Luego haz clic en la pestaña Signing and Capabilities
  4. Copia el valor de Bundle identifier. Este es el valor que debes configurar en BUNDLE_IDENTIFIER secreto.
bundle-identifier-xcode

6. Procesamiento de la build

En GitHub Actions, se te factura según los minutos que has usado para ejecutar tu workflow de CI/CD. Según mi experiencia, tarda unos 10–15 minutos antes de que la build se procese en App Store Connect.

Para proyectos privados, el coste estimado por build puede llegar a $0,08/min x 15 min = $1,2, o más, según la configuración y dependencias de tu proyecto.

Si te preocupan los costes en proyectos privados, puedes configurar skip_waiting_for_build_processing a true. Esto ahorrará minutos de build al no tener que esperar a que App Store Connect termine de procesar el build.

Sin embargo, hay una contrapartida: tendrás que actualizar manualmente la información de cumplimiento de tu app en App Store Connect antes de distribuir el build a los usuarios.

Esta optimización es útil principalmente para proyectos privados donde los minutos de build cuestan dinero. En proyectos públicos o gratuitos, los minutos de build son gratis, así que no hace falta activar este ajuste. Consulta la página de precios de GitHub página de precios para más detalles.

7. Configurar GitHub Actions

Configurar los secrets de GitHub

Por favor, copia los secrets del .env archivo y pégalos en los secrets del repositorio de GitHub.

Ve a Configuración > Secrets y variables > Acciones > Nuevo secreto de repositorio

github-secrets

2. BUILD_CERTIFICATE_BASE64 - Certificado en Base64.

3. BUILD_PROVISION_PROFILE_BASE64 - Provisioning profile en Base64.

4. BUNDLE_IDENTIFIER - el bundle identifier de tu app.

5. APPLE_KEY_ID — API Key de App Store Connect 🔺Key ID.

6. APPLE_ISSUER_ID — API Key de App Store Connect 🔺Issuer ID.

7. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 Contenido de la clave .p8, compruébalo

8. Configurar el archivo de workflow de GitHub

Crea un directorio GitHub workflow.

cd .github/workflows

Dentro de la workflow carpeta, crea un archivo llamado build-upload-ios.ymly añade lo siguiente.

name: Build source code on ios

on:
  push:
    tags:
      - '*'

jobs:
  build_ios:
    runs-on: macOS-latest
    steps:
      - uses: actions/checkout@v6
      - name: Set Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: npm
      - name: Install dependencies
        id: install_code
        run: npm ci
      - name: Build
        id: build_code
        run: npm run build
      - uses: actions/cache@v5
        with:
          path: ios/App/Pods
          key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-pods-
      - name: Sync
        id: sync_code
        run: npx cap sync
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.0'
          bundler-cache: true
      - uses: maierj/fastlane-action@v3.1.0
        env:
          APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
          BUNDLE_IDENTIFIER: ${{ secrets.BUNDLE_IDENTIFIER }}
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
          APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
          APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
          APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          APPLE_PROFILE_NAME: ${{ secrets.APPLE_PROFILE_NAME }}
        with:
          lane: ios beta
      - name: Upload release bundle
        uses: actions/upload-artifact@v6
        with:
          name: ios-release
          path: ./App.ipa
          retention-days: 10

Este workflow debe ejecutarse tras cada GitHub etiqueta, si necesitas automatizar el tag, consulta Build y release automáticos con GitHub actions primero.

Este workflow descargará tus deps de NodeJS, las instalará y hará el build de tu app JS.

Cada vez que envíes un nuevo commit, se generará un release en TestFlight.

Tu app no necesita usar Ionic, solo la base de Capacitor es obligatoria. Puede tener módulos antiguos de Cordova, pero se recomienda usar el plugin JS de Capacitor.

8. Ejecutar el workflow

Crear un commit

Haz un commit, y verás el workflow activo en el repositorio.

Ejecutar el workflow

Haz push de los nuevos commits a la rama main o development para ejecutar el workflow.

Iniciado con el commit

En unos minutos, el build debería estar disponible en tu dashboard de App Store Connect.

Dashboard de TestFlight

9. ¿Puedo desplegar desde mi máquina local?

Sí, puedes hacerlo sin esfuerzo.

Puedes usar Xcode para compilar y firmar tu app, como siempre.

Guías de configuración CI/CD

Plataformas CI/CD alternativas

Live Updates y despliegue

Gracias

Este blog se basa en los siguientes artículos:

Continúa desde el build automático de iOS de Capacitor con acciones de GitHub con certificado

Si estás usando Build automático de Capacitor para iOS con GitHub actions y certificado para planificar la automatización de CI/CD, conéctalo con CI/CD de Capgo para el flujo de trabajo del producto en Capgo CI/CD, Native Builds de Capgo para el flujo de trabajo del producto en Capgo Native Builds, Integraciones de Capgo para el flujo de trabajo del producto en Capgo Integraciones, Integración CI/CD para los detalles de implementación de CI/CD Integration, y Integración con GitHub Actions para los detalles de implementación de GitHub Actions Integration.

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.