Saltar al contenido
Plazoleta
· Raúl López

De `main` a Google Play sin tocar nada a mano

En Jornia, cada push a main que toca la app termina en el móvil de un tester sin que nadie abra la consola de Play. Qué se automatizó, qué se dejó manual a propósito, y por qué esa segunda lista importa tanto como la primera.

La pregunta que nos hacen más a menudo sobre esto no es "¿tenéis CI/CD?". Es "¿y quién le da al botón de publicar?". La respuesta incomoda un poco la primera vez que la dices en voz alta: nadie. Cada push a main que toca la app compila, firma y sube el AAB a Play. Publicar no es un evento que decide una persona un martes por la tarde. Es parte del build.

Y aun así hay cosas que siguen siendo manuales, a propósito, con la decisión escrita en el propio pipeline. Esa segunda lista es la parte interesante del post.

Lo que se automatiza

android-release.yml se dispara con cada push a main que toca app/. Corre los tests unitarios primero —no se sube nada con los tests en rojo, esa es la puerta mínima de cualquier CD que se tome en serio a sí mismo—, compila el AAB firmado, y lo sube a Play. Sin artefacto especial, sin paso manual en la consola: el mismo workflow que corre en cada push es el que se lanza a mano cuando hace falta publicar en otro sitio, con un parámetro de qué track.

El versionCode —el número interno que Play usa para saber qué build es más nueva— es el número de ejecución de GitHub Actions. Crece solo, nunca se repite, y si una subida falla y hay que relanzar el workflow, el hueco que deja en la numeración no le importa a nadie. Es un detalle pequeño, pero evita la alternativa habitual: alguien llevando la cuenta a mano en una hoja o, peor, calculándolo a partir del último número que recuerda.

Hay una comprobación menos obvia que vale la pena contar porque no la decide el propio código: antes de subir el artefacto, un script revisa que las librerías nativas de 64 bits del AAB estén alineadas a 16 KB. Play avisa —y en algún momento empezará a rechazar— los artefactos que no lo cumplen, y lo que determina si lo cumples no es nada que tú hayas escrito: es con qué versión del NDK compiló cada dependencia que arrastras. Eso puede cambiar solo con actualizar una librería, sin que nadie lo pidiera. Por eso la comprobación no se hace sobre el código fuente, se hace sobre el artefacto ya compilado, justo antes de subirlo: es el único sitio donde la pregunta tiene una respuesta de verdad.

Y las subidas se encolan, no se pisan: el workflow tiene un grupo de concurrencia que hace esperar a la segunda release si la primera sigue en marcha, en vez de cancelarla. Tiene sentido en cuanto piensas qué es una release: no es un job desechable que puedes tirar y repetir, es una versión que ya le puede haber llegado a alguien.

Por qué el track por defecto es "alpha" y no "interno"

Esto cambió de decisión el 17 de julio de 2026, y el motivo no está en ningún manual de buenas prácticas de CI/CD: está en las reglas de Play para salir de la prueba cerrada. Durante la ventana de doce testers activos catorce días, Google no solo mira si tienes suficiente gente apuntada. También mira si el track cerrado tiene releases activas. Un track congelado —aunque tenga a los doce testers de sobra— alarga el acceso a producción.

Así que el push a main publica en alpha, que es la prueba cerrada, no en el track interno que sería la opción "más segura" de manual de instrucciones. El dogfooding del equipo no pierde nada por el cambio: el mismo AAB llega igual, solo que por el track que además cuenta para el gate de Google. Es la clase de decisión que solo tiene sentido si conoces la letra pequeña del programa al que te estás presentando, y que sin esa letra pequeña parecería al revés de prudente.

Lo que se queda manual, a propósito

Producción está cerrada. No por un descuido ni por que falte tiempo de configurarla: la cuenta de servicio que sube los artefactos no tiene el permiso de lanzar a producción, y esa ausencia es una decisión fechada, no un placeholder. Promocionar una release a producción —la que le llega a cualquiera que se instale la app desde la ficha pública— es un acto humano. Alguien entra a la consola de Play y lo hace mirando, no un job que corre solo un viernes por la noche.

El resto de la maquinaria ya sabe rodar el rollout gradual cuando llegue el momento: el mismo dispatch manual admite lanzar a producción con una fracción de usuarios en vez de al cien por cien de golpe. Está construido y probado en los otros tracks. Lo único que falta es el permiso, y falta porque nadie lo ha concedido todavía a propósito.

Hay un segundo manual con menos ceremonia pero igual de real: la primera subida de una app nueva a Play tiene que hacerse a mano en la consola, sí o sí. La API de publicación automática no puede crear la ficha de la app la primera vez — necesita que la app ya exista del lado de Play para poder hablar con ella. Así que el primer AAB, el que crea a Jornia como entidad en la consola, lo sube alguien desde su máquina o desde un artefacto ya compilado por el CI. A partir de ahí, y solo a partir de ahí, el pipeline puede tomar el relevo. Es un problema del huevo y la gallina que no tiene automatización posible: no puedes automatizar la creación de la cosa que la automatización necesita que exista primero.

Lo que nos llevamos

"CI/CD hasta Play" no es un objetivo binario. Hay un tramo largo que se automatiza sin dudar —tests, firma, subida, notas de versión— y un tramo corto, con nombre y fecha de decisión, que se deja manual porque automatizarlo sería quitarle a una persona una decisión que debe seguir siendo suya.

Las reglas del proveedor externo pueden invertir lo que parece prudente. Publicar en el track "cerrado" en vez del "interno" suena a más expuesto. Aquí es al revés, porque el gate de Google no premia la cautela: premia la actividad. Automatizar sin leer la letra pequeña del programa habría automatizado la decisión equivocada.

Bootstrapea a mano lo que la automatización todavía no puede alcanzar. La primera subida de una app nueva es, por diseño de la propia plataforma, el único paso que no se puede saltar. Aceptarlo cuesta menos que perseguir una automatización que no existe.


Si estás montando el pipeline de publicación de tu app y te has topado con uno de estos huevos-y-gallinas, cuéntamelo: seguro que hay uno más que todavía no hemos visto.

¿Tienes un problema parecido?

Cuéntanoslo. Hablas directamente con quien escribe el código.