Tutorial: configurar pipeline CI para Android con GitHub Actions, proteger el keystore y publicar en la track internal — paso a paso
Apps mobile · Por Kevin Marcondes · Publicado el · Actualizado el · 9 min de lectura
Este es el tutorial para configurar un pipeline CI para Android con GitHub Actions, proteger el keystore y publicar en la track internal. Prepararás secrets, ajustarás Gradle, escribirás el workflow YAML y validarás la publicación. Es una guía directa para firmar la app de forma segura y automatizar el envío a testers internos.
Prerequisitos
Necesitas preparar varios elementos antes de automatizar el build y la publicación. Aseguran que el pipeline se ejecute sin interrupciones y con seguridad.
Si tu proyecto aún no publica manualmente, revisa el flujo de firmado y la versión local antes de automatizar. Para conocer el envío manual, consulta la guía sobre la Play Store en Publicar una app Android en Google Play.
- Proyecto Android con el plugin Android Gradle funcionando localmente.
- Cuenta en Google Play Console con la app ya creada (packageName definido).
- Cuenta de servicio en el Play Console con clave JSON descargada y permiso de publicador.
- Keystore de firma de la app (JKS o PKCS12) y contraseñas asociadas.
- Repositorio en GitHub con GitHub Actions habilitado.
- Java 17 en el build (AGP recientes lo requieren).
Visión general del pipeline CI/CD
Vamos a montar un pipeline que: firma la app, genera el bundle (.aab) y publica en la track internal. Tendrás un workflow único con pasos encadenados. Esto puede publicar una versión para testers, así que usa ramas protegidas.
Si quieres ver un pipeline de deploy genérico con Actions, revisa también este pipeline de despliegue automático con GitHub Actions y complementa tu lectura.
- Checkout del código y caché de Gradle.
- Instalación de Java y del Android SDK necesario.
- Restauración del keystore a partir de secrets.
- Generación del app bundle con Gradle (bundleRelease).
- Publicación automática con Gradle Play Publisher en la track internal.
- Upload del artefacto como evidencia del build.
Paso 1 — Configurar firma y Play en Gradle
Prepara tu módulo app para leer variables de entorno en signingConfig y habilita la publicación vía el plugin de Play. Sustituye la versión del plugin por la actual y ajusta minify/proguard según tu proyecto.
El archivo JSON de la cuenta de servicio se creará en el workspace del Actions durante el job. Mantenemos la ruta simple (service-account.json).
// app/build.gradle (Groovy DSL)
plugins {
id 'com.android.application'
id 'com.github.triplet.play' version 'x.y.z' // substitua pela versão atual
}
android {
namespace "com.seu.pacote" // ajuste para o seu package
compileSdk 34 // ajuste para seu projeto
defaultConfig {
applicationId "com.seu.pacote"
minSdk 24
targetSdk 34
versionCode 1 // incremente a cada release
versionName "1.0.0"
}
signingConfigs {
release {
storeFile file(System.getenv("ANDROID_KEYSTORE_PATH") ?: "keystore.jks")
storePassword System.getenv("ANDROID_KEYSTORE_PASSWORD")
keyAlias System.getenv("ANDROID_KEY_ALIAS")
keyPassword System.getenv("ANDROID_KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
play {
// O arquivo será escrito pelo workflow na raiz do repo
serviceAccountCredentials.set(file("service-account.json"))
track.set("internal")
defaultToAppBundles.set(true) // publica .aab
// releaseStatus.set("completed") // opcional: completed/draft
}
Paso 2 — Crear cuenta de servicio en Play Console
Crea una cuenta de servicio vinculada a la app en el Play Console y genera la clave JSON. Concede solo los permisos necesarios para publicar en la track internal.
Guarda el JSON con cuidado. Si se filtra, revoca y genera una nueva clave inmediatamente.
Anota el packageName exacto de la app. Se usará el plugin para localizar la aplicación.
- Ve a Acceso a la API en el Play Console y vincula al Google Cloud.
- Crea una cuenta de servicio y genera la clave JSON.
- Asigna permisos de publicación a la app objetivo.
- Descarga el archivo JSON y almacénalo en un lugar seguro.
Paso 3 — Añadir secrets en GitHub con seguridad
Vamos a almacenar todo como secrets. Para el keystore, usa base64 para evitar problemas binarios. En el workflow decodificaremos a un archivo .jks.
En el repositorio, accede a Settings > Secrets and variables > Actions y crea los siguientes secrets.
| Nombre del secret | Contenido | Consejo |
|---|---|---|
| ANDROID_KEYSTORE_BASE64 | Keystore (.jks o .p12) en base64 | Usa base64 en tu SO; evita pegar archivos binarios directamente. |
| ANDROID_KEYSTORE_PASSWORD | Contraseña del keystore | No reutilices contraseñas entre entornos. |
| ANDROID_KEY_ALIAS | Alias de la clave de firma | Confirma el alias con keytool si tienes dudas. |
| ANDROID_KEY_PASSWORD | Contraseña del alias | Puede ser igual a la del keystore, pero no es obligatorio. |
| PLAY_SERVICE_ACCOUNT_JSON | Contenido del JSON de la cuenta de servicio | Pega el JSON completo; el workflow creará el archivo. |
| PLAY_PACKAGE_NAME | Package name de la app (ej.: com.seu.pacote) | Mantén el valor explícito para validaciones. |
Paso 4 — Workflow YAML de GitHub Actions
Abajo tienes un workflow funcional. Instala Java 17, prepara el Android SDK, restaura el keystore, genera el .aab y publica en la track internal. Esto ejecuta la publicación, así que restringe el trigger según tu política (ej.: solo tags).
name: android-ci-internal
on:
push:
branches: [ main ]
# Para maior segurança, você pode publicar só em tags:
# tags: [ 'v*' ]
jobs:
build-and-publish:
runs-on: ubuntu-latest
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
PLAY_PACKAGE_NAME: ${{ secrets.PLAY_PACKAGE_NAME }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: |
platform-tools
platforms;android-34
build-tools;34.0.0
- name: Gradle cache
uses: gradle/gradle-build-action@v3
- name: Restaurar keystore e credenciais do Play
run: |
echo "Decodificando keystore"
echo "$ANDROID_KEYSTORE_PASSWORD" > /dev/null
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > keystore.jks
echo "Gravando service-account.json"
echo "$PLAY_SERVICE_ACCOUNT_JSON" > service-account.json
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
- name: Build AAB (release)
run: |
export ANDROID_KEYSTORE_PATH=$GITHUB_WORKSPACE/keystore.jks
./gradlew --no-daemon clean bundleRelease
- name: Publicar na track internal
run: |
export ANDROID_KEYSTORE_PATH=$GITHUB_WORKSPACE/keystore.jks
./gradlew --no-daemon publishRelease
- name: Upload do artefato (.aab)
uses: actions/upload-artifact@v4
with:
name: app-release-aab
path: app/build/outputs/bundle/release/*.aab
Paso 5 — Publicar en el rollout internal y buenas prácticas
La track internal libera la versión a testers rápidamente y sin exposición pública. Crea grupos y listas de emails en Play Console para que reciban las builds internas.
Puedes mantener el status como draft para revisión manual y luego promover a completed. Esto da control y reduce riesgos.
Si tu proyecto tiene otros despliegues automatizados, consulta también patrones de CI/CD en la guía CI/CD con GitHub Actions para PHP/Laravel y adapta ideas al Android.
- Define grupos de test internos antes de la primera publicación.
- Usa status draft inicialmente y automatiza la promoción según la madurez.
- Evita publicar desde PRs de forks; limita el workflow a ramas protegidas.
- Guarda el keystore fuera del repo; usa solo secrets.
- Ajusta targetSdk y build-tools en el setup del SDK según tu proyecto.
Cómo confirmar que funcionó y solución de problemas comunes
Valida la publicación e investiga fallos rápidamente. Abajo tienes una lista de verificación y errores comunes con sus causas probables.
Para estandarizar infra de runners a escala, consulta prácticas de autoescalar runners self-hosted en GitHub Actions y complétalas con tu contexto.
- En GitHub Actions, verifica el job "Publicar na track internal" sin errores.
- Confirma el artefacto .aab en el paso de upload.
- En Play Console, comprueba la versión en Testes internos con el versionCode correcto.
- Prueba la instalación vía el enlace de test interno enviado a los grupos.
- Errores de keystore: alias incorrecto o archivo corrupto (base64 mal generado).
- Permisos insuficientes de la cuenta de servicio: ajusta el rol en Play Console.
- Conflicto de versionCode: incrementa versionCode en Gradle.
- SDK faltante: alinea platforms/build-tools en el setup con el compileSdk del proyecto.
Preguntas frecuentes
¿Necesito usar runners self‑hosted para Android?
No. ubuntu-latest cubre la mayoría de los casos cuando instalas el Android SDK en el job. Los runners self-hosted solo tienen sentido si necesitas builds mucho más rápidos, caché intensivo o acceso a recursos de red internos.
¿Cómo generar el base64 del keystore con seguridad?
Usa la herramienta nativa de tu sistema. En Linux y macOS: base64 < tu-keystore.jks > keystore.b64. En Windows PowerShell: [Convert]::ToBase64String([IO.File]::ReadAllBytes('tu-keystore.jks')). Guarda el .b64 fuera del repositorio.
¿El Gradle Play Publisher es obligatorio para publicar?
No. Puedes usar otras herramientas como Fastlane o la API directamente. El plugin facilita en Gradle porque integra assemble y publish en una única pipeline. El manejo de secrets y el keystore es similar.
¿Puedo publicar solo en tags para evitar releases accidentales?
Sí. Ajusta el trigger del workflow a on: push: tags: ['v*']. Otra opción es exigir revisión de código y proteger la rama principal para evitar ejecuciones no planificadas de publicación.
¿Cuánto tarda en aparecer para testers en la track internal?
Suele ser rápido, desde pocos minutos hasta algo más, según verificaciones automáticas. Asegúrate de que los testers estén añadidos correctamente a los grupos y que la app cumpla políticas y requisitos mínimos del Play.
¿Cómo revertir una versión enviada por error?
En la track internal puedes crear rápidamente una nueva versión con la corrección y un versionCode mayor. Para evitar más publicaciones accidentales, cambia el status a draft en Gradle o restringe el trigger del workflow.
¿Quieres adaptar el pipeline a tu proyecto Android?
Si necesitas discutir ajustes finos (matriz de módulos, múltiples flavors, tracks distintas), háblame y montamos un plan seguro.
Hablar sobre CI AndroidVer también
- Tutorial: implementar Canary releases con Traefik en Docker Compose usando headers y weighted routing para despliegues graduales de aplicaciones PHP
- Tutorial: configurar HAProxy con Let's Encrypt y healthchecks para balanceo de tráfico entre contenedores Docker en VPS (paso a paso)
- Tutorial práctico: implementar mTLS entre Nginx y microservicios usando smallstep CA en Docker Compose
- Tutorial: configurar cgroup v2 y systemd ResourceControl para limitar CPU, memoria e I/O de contenedores Docker en Debian