Pipeline CI React Native (Expo) con GitHub Actions y EAS: builds automáticas y distribución interna para testers — paso a paso

Apps mobile · Por · Publicado el · 9 min de lectura

Mãos segurando um smartphone sobre um laptop aberto numa mesa com xícara de café e acessórios.
Ambiente de desenvolvimento móvel com dispositivo e notebook, ideal para representar builds e testes. Foto: Airam Dato-on / Pexels

Pipeline CI React Native (Expo) con GitHub Actions y EAS: builds automáticas y distribución interna para testers. En este tutorial configurarás perfiles en eas.json, secrets y un workflow YAML que lanza builds preview y, en main, envía a Play (internal track) y TestFlight. Al final validamos el flujo y cubrimos errores comunes.

Qué vamos a construir: visión general del pipeline

La idea es que en cada PR o push a la rama principal se disparen builds automáticas en EAS (Expo Application Services). En los PRs generamos builds internas (preview) para validación rápida. En los merges a main, además de los builds, haremos el envío automático a canales internos: internal track en Google Play y TestFlight en iOS.

EAS realiza el build de forma remota; GitHub Actions orquesta el proceso. Esto mantiene los runners ligeros, centrados en tests, lint y versionado. El resultado: links o disponibilización directa a los testers, reduciendo el rozamiento entre código y validación.

Flujo resumido:

  • PR abierto: dispara build interno (Android e iOS) en EAS para pruebas rápidas.
  • Merge en main: dispara build interno y envía a Play (internal track) y TestFlight.
  • Los testers reciben la app por los canales oficiales o vía link interno (cuando aplica).

Pre-requisitos y organización del proyecto

Antes de empezar, asegúrate de que el proyecto y las cuentas están alineados. Necesitarás accesos válidos a las tiendas y a EAS. No uses credenciales personales fuera de entornos seguros.

  • Repositorio en GitHub con tu app Expo (SDK compatible con EAS).
  • Cuenta Expo con EAS habilitado y el proyecto vinculado.
  • Identificadores configurados: android.package e ios.bundleIdentifier en app.json/app.config.js.
  • Acceso al Google Play Console (permisos para integración vía API).
  • Acceso al App Store Connect (clave de API activa para la subida).
  • GitHub Actions habilitado en el repositorio.

Define perfiles de build y canales en eas.json

Usaremos tres perfiles: development (para dev client), preview (distribución interna para QA) y production (tiendas). El perfil preview será nuestro foco para testers. Configura también los canales (channels) para organizar updates/lanzamientos.

En Android es habitual usar AAB para la subida y, opcionalmente, APK para instalación directa entre testers técnicos. En iOS, la vía típica de distribución interna es TestFlight.

{
  "cli": { "version": ">= 3.13.0" },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "channel": "development",
      "ios": { "simulator": false },
      "android": { "buildType": "apk" }
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview"
    },
    "production": {
      "channel": "production"
    }
  },
  "submit": {
    "internal": {
      "android": { "track": "internal" },
      "ios": { "ascAppId": "SEU_APP_ID_ASC" }
    },
    "production": {
      "android": { "track": "production" }
    }
  }
}

Secrets en GitHub y permisos en las tiendas

Crea secrets en GitHub para el workflow. Nunca versiones claves privadas, JSONs de servicio o tokens. Usa únicamente GitHub Secrets y restringe el acceso (environments protegidos).

  • EXPO_TOKEN: token de acceso de la cuenta Expo/EAS para builds no interactivos.
  • GOOGLE_SERVICE_ACCOUNT_KEY: JSON de la cuenta de servicio de Google Play (con permisos para subir al track internal).
  • ASC_ISSUER_ID, ASC_KEY_ID, ASC_PRIVATE_KEY: credenciales de la API de App Store Connect para envío a TestFlight.
  • Variables de build de la app (si las hay): mantenlas como secrets e inyecta en tiempo de build.

Workflow de GitHub Actions: build y distribución interna

El YAML siguiente orquesta el flujo: instala dependencias, ejecuta tests, autentica en EAS y lanza builds preview para Android e iOS. En pushes a main, envía a Play (internal track) y TestFlight.

Si quieres entender la base de los triggers y buenas prácticas de Actions, consulta este artículo sobre cómo configurar un pipeline de despliegue automático: cómo configurar un pipeline de despliegue automático. Atención: el job corre en runners hospedados por GitHub; el build se ejecuta de forma remota en EAS.

name: ci-expo-eas

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build-and-distribute:
    runs-on: ubuntu-latest
    env:
      EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
      EXPO_APPLE_APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
      EXPO_APPLE_APP_STORE_CONNECT_KEY_ID: ${{ secrets.ASC_KEY_ID }}
      EXPO_APPLE_APP_STORE_CONNECT_PRIVATE_KEY: ${{ secrets.ASC_PRIVATE_KEY }}
      GOOGLE_SERVICE_ACCOUNT_KEY: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'yarn'

      - name: Install deps
        run: yarn install --frozen-lockfile

      - name: Run tests (optional)
        run: yarn test --if-present

      - name: Install EAS CLI
        run: npm i -g eas-cli

      - name: Build preview (Android + iOS)
        run: eas build --platform all --profile preview --non-interactive

      - name: Submit to Google Play (internal track)
        if: github.ref == 'refs/heads/main'
        run: eas submit -p android --profile internal --non-interactive

      - name: Submit to TestFlight
        if: github.ref == 'refs/heads/main'
        run: eas submit -p ios --profile internal --non-interactive

      - name: List latest builds
        run: eas build:list --limit 5

Distribución para testers: opciones y cuándo usar

Con los builds publicados, elige la vía de distribución interna adecuada. Para Android, el internal track de Play escala bien para equipos de QA. Para iOS, TestFlight es el flujo estándar para pruebas internas/externas. En equipos técnicos, un APK interno (preview) puede ser útil para validaciones rápidas.

Plataforma Perfil Objetivo Artefacto Distribución
Android preview QA interno rápido AAB (predeterminado) o APK (opcional) Link EAS o Google Play (internal track)
iOS preview QA interno IPA TestFlight (grupos internos o externos)
Android/iOS development Dev client para funciones en desarrollo APK/IPA dev client Instalación interna vía link/QR de EAS
Android/iOS production Release público AAB/IPA Tiendas (tracks estables)

Cómo confirmar que funcionó

Tras el workflow, valida que los builds se crearon y que la subida progresó. No compartas links de build fuera del grupo autorizado de testers. Si hay errores de permisos, revisa las credenciales y los roles en las tiendas.

  • Confirma en el panel de EAS que los jobs finalizaron correctamente.
  • Verifica en Google Play Console si el artefacto llegó al internal track.
  • En App Store Connect, comprueba el procesamiento del build y su disponibilidad en TestFlight.
  • Prueba la instalación en un dispositivo real (un Android y un iOS).
# Listar builds recientes y sus estados
npx eas-cli build:list --limit 10 --json

# Status de submissões
npx eas-cli submit:status --platform android
npx eas-cli submit:status --platform ios

Solución de problemas comunes

Los errores recurrentes en este flujo suelen estar relacionados con autenticación, identificadores de la app y permisos en las tiendas. A continuación, algunos síntomas y rutas prácticas de corrección.

  • Autenticación falló (You are not authenticated): verifica EXPO_TOKEN y que el token tenga acceso al proyecto.
  • Android rechazado por Play (permission denied): comprueba que la cuenta de servicio tenga permiso para gestionar artefactos y el aplicativo correcto.
  • Bundle/package ausente: define android.package e ios.bundleIdentifier en app.json/app.config.js.
  • iOS: clave inválida en App Store Connect: revisa issuer ID, key ID y el contenido de la clave privada.
  • Build interno iOS no instala fuera de TestFlight: restricciones de firma; usa TestFlight para testers.
  • Cambios nativos no reflejados: limpia las carpetas nativas generadas y vuelve a ejecutar el build cuando modifiques configuraciones críticas.

Seguridad y buenas prácticas en CI/CD mobile

Protege credenciales y reduce la superficie de ataque. En proyectos sensibles, adopta environments protegidos, revisión en doble para secrets y evita volcados de datos en logs. Consulta también este checklist dedicado: Checklist de seguridad CI/CD.

Si el volumen de jobs crece, merece la pena estudiar el autoescalado de runners self-hosted para etapas de tests pesados (no para builds EAS en sí): autoescalar runners en GitHub Actions.

  • Secrets solo vía GitHub Environments con aprobaciones.
  • Rota claves periódicamente y desactiva las no usadas.
  • Evita imprimir variables sensibles en logs.
  • Restringe quién puede disparar workflows en la rama principal.

Preguntas frecuentes

¿EAS compila en mi runner de GitHub Actions?

No. GitHub Actions solo orquesta. El build se ejecuta en los servidores de EAS. El runner solo necesita Node, EAS CLI y las credenciales para iniciar los jobs remotos y, cuando proceda, someter a las tiendas.

¿Puedo distribuir Android sin pasar por Google Play?

Sí, generando un APK en un perfil interno (preview) y compartiendo el link del build de EAS. Para un flujo de QA más escalable y con control de grupos, prefiere el internal track del Play Console.

¿Cómo versionar correctamente entre builds internos y producción?

Mantén version, android.versionCode e ios.buildNumber consistentes en la configuración de la app. Puedes automatizar el incremento con scripts en CI, garantizando que los builds internos no conflijan con producción.

¿Puedo ejecutar tests E2E (Detox) en este pipeline?

Sí, pero ejecútalos antes de lanzar el EAS Build, en jobs separados, usando simuladores/emuladores en los runners. El build EAS debe dispararse solo después de que la suite pase correctamente.

¿Qué variables de entorno de la app debo exponer en CI?

Solo lo mínimo necesario. Usa secrets de GitHub y, cuando aplique, variables seguras en EAS. Evita commitear archivos .env. Configura lectura por variables de entorno en tiempo de build.

¿Necesito cuenta paga en las tiendas para TestFlight e internal track?

Necesitas cuentas válidas y permisos adecuados en App Store Connect y Google Play Console. Políticas y requisitos pueden cambiar; verifica en el panel de cada tienda antes de configurar el pipeline.

¿Quieres adaptar este pipeline a tu app?

Si necesitas ajustar perfiles, versionado o seguridad del flujo, contacta para analizar tu caso.

Hablar sobre CI/CD mobile

Ver también