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 Kevin Marcondes · Publicado el · 9 min de lectura
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 mobileVer también
- Tutorial: configurar mTLS entre Traefik y servicios en Docker Compose usando smallstep CA y certificados automáticos — paso a paso
- Tutorial: integrar Sentry en el pipeline de deploy con GitHub Actions para subir source maps automáticamente y generar issues (PHP y JavaScript) — paso a paso
- Configurar retención y compresión de logs de contenedores Docker con journald + logrotate + rsyslog en VPS con poco disco
- Tutorial: configurar GitHub Actions para tests de integración con PostgreSQL en contenedores efímeros y rollback automático de migrations — guía práctica