Despliegue¶
Responde: ¿dónde corre cada pieza, qué la pone ahí, y qué hay que hacer a mano?
flowchart LR
subgraph repos["1 · Repositorio"]
direction TB
RFE["<b>embolsadora-frontend</b>"]
RCL["<b>embolsadora4.0-cloud</b>"]
RBU["<b>embolsadora-bundle</b>"]
RED["<b>embolsadora-edge</b>"]
RHI["<b>proyecto-embolsadora</b><br/><i>historian · autor externo</i>"]
end
subgraph pone["2 · Qué lo pone ahí"]
direction TB
W2["<b>ci-cd.yml</b><br/><i>push a main → producción<br/>otra rama → preview</i>"]
W1["<b>deploy-cloud-run.yml</b><br/><i>push a main</i>"]
MIG["<b>golang-migrate</b><br/><i>a mano, antes o después<br/>del deploy</i>"]
DC["<b>docker compose up --build</b><br/><i>a mano, por SSH</i>"]
W3["<b>ci.yml del Edge</b><br/><i>compila, vet y linter<br/>no publica artefacto</i>"]
W4["<b>ci.yml del historian</b><br/><i>publica imagen arm64</i>"]
end
subgraph corre["3 · Dónde corre"]
direction TB
PRD["<b>Vercel</b> · Producción"]
PRV["<b>Vercel</b> · Previews<br/><i>una por rama</i>"]
AR["Artifact Registry<br/><i>imagen de la API</i>"]
CR["<b>Cloud Run</b> · us-east1<br/>embolsadora-api"]
ST["<b>Raspberry Pi</b> — planta<br/>historian · InfluxDB · Edge<br/><i>FA³ST opcional</i>"]
GH[("GHCR<br/><i>imagen latest</i><br/><i>nadie la consume</i>")]
SB["Supabase<br/>Auth + Postgres"]
MG[("MongoDB")]
RD[("Redis")]
end
RFE --> W2
W2 --> PRD
W2 --> PRV
RCL --> W1 --> AR --> CR
RCL --> MIG --> SB
RBU --> DC --> ST
RED -.->|"clonado al lado"| DC
RHI -.->|"clonado al lado"| DC
RED --> W3
RHI --> W4 --> GH
PRD --> CR
CR --> SB
CR --> MG
CR --> RD
ST -->|"ingesta"| CR
CR -->|"chequeos"| ST
classDef repo fill:#fff,stroke:#0b4884,stroke-width:2px,color:#0b4884
classDef ci fill:#438dd5,stroke:#2e6295,color:#fff
classDef run fill:#1168bd,stroke:#0b4884,color:#fff
classDef man fill:#c0392b,stroke:#7b241c,color:#fff
classDef ext fill:#999,stroke:#6b6b6b,color:#fff
class RFE,RCL,RED,RHI,RBU repo
class W1,W2,W3,W4 ci
class CR,PRD,PRV,ST run
class MIG,DC man
class AR,SB,MG,RD,GH ext
Se lee de izquierda a derecha: qué repositorio, qué lo pone en producción y dónde termina corriendo. En rojo, los dos pasos que hace una persona a mano, que son los que más pueden romper un despliegue.
Tres cosas que el dibujo deja ver y la tabla de abajo no:
- Nada despliega la Raspberry Pi desde un repositorio. El stack se construye en la Pi desde el código fuente de tres repos clonados lado a lado, así que no hay una versión desplegada identificable.
- El único artefacto publicado para la planta no lo usa nadie: el CI del historian sube una
imagen
arm64a GHCR y el bundle la ignora porque reconstruye desde fuente. - El CI del Edge no produce artefacto alguno: compila, pasa linter y vet, y ahí termina.
Dónde corre cada pieza¶
| Pieza | Plataforma | Qué la despliega | Disparador |
|---|---|---|---|
| Frontend + BFF | Vercel | ci-cd.yml con la CLI de Vercel |
push a main → producción; push a cualquier otra rama → preview |
| Cloud API | Google Cloud Run, región us-east1, servicio embolsadora-api |
deploy-cloud-run.yml |
push a main, o manual |
| Postgres | Supabase, región sa-east-1, vía el pooler |
servicio gestionado; el esquema lo aplica una persona | — |
| Auth y JWKS | Supabase | servicio gestionado | — |
| MongoDB | sin definir: ningún repositorio dice qué proveedor lo hospeda, y el valor por defecto apunta a localhost, que en Cloud Run no resuelve a nada |
— | — |
| Redis | servicio gestionado | — | — |
| historian, InfluxDB, Edge | Raspberry Pi en planta | docker compose up -d --build, a mano por SSH |
una persona |
| FA³ST | Raspberry Pi, override opcional | docker-compose.aas.yml, hay que activarlo |
una persona |
Los dos pipelines automatizados¶
Cloud API → Cloud Run¶
Push a main dispara: build de la imagen, push a Artifact Registry con dos etiquetas (el SHA
del commit y latest), gcloud run deploy con la etiqueta del SHA, y un smoke test que
verifica que el endpoint de salud responde.
Tres propiedades que vale documentar porque no son obvias:
- No hay claves de servicio en GitHub. La autenticación usa OIDC de GitHub contra Workload Identity Federation, y el proveedor solo acepta tokens de ese repositorio.
- El deploy solo actualiza la imagen. Las variables de entorno del servicio se conservan entre despliegues: no están en el workflow ni en ningún archivo del repo.
- Se despliega por SHA, así que el rollback es una operación de Cloud Run, no un revert de git: la revisión anterior sigue existiendo.
Frontend → Vercel¶
El mismo workflow cubre verificación y despliegue, con cuatro jobs: verify (instala y corre
la verificación del proyecto), e2e con Playwright, deploy-preview y deploy-production.
Dos detalles con consecuencias:
- Cada rama que se pushea genera un preview desplegado, no solo las ramas de release.
- El job de E2E está en
continue-on-error, así que un fallo de Playwright no bloquea el despliegue. Los E2E informan, no custodian. Lo que custodia esverify, del que ambos jobs de deploy dependen.
Lo que no está automatizado¶
Las migraciones de base de datos se aplican a mano, y el orden importa
El workflow de despliegue no aplica migraciones. Una persona corre golang-migrate contra
la base de producción, y el repo del Cloud documenta al menos dos migraciones donde el
orden respecto del deploy del binario cambia el resultado:
- Una migración cambia la firma de una función de Postgres que el código Go llama. Si el
binario nuevo sale antes que la migración, las rutas de asignación de roles devuelven
500. Y el rollback es simétrico: volver al binario anterior sin revertir la migración rompe igual, en la dirección contraria. - Otra migración borra permisos del catálogo que el frontend desplegado todavía usa para filtrar su navegación. Entre esa migración y el deploy del frontend, los administradores pierden acceso a pantallas de administración.
El procedimiento, las consultas de auditoría previas y el orden correcto están en el
README de migraciones del repo del Cloud, que es su fuente de verdad. No se replican
acá.
Hay un segundo camino que esta página no registraba: el binario puede aplicar las
migraciones pendientes al arrancar si el servicio tiene activada la variable
RUN_MIGRATIONS_ON_BOOT. Está apagada por defecto, la imagen trae las migraciones, y si
una falla el proceso sale con código 1. No se sabe si producción la tiene activada
el incidente del 2026-08-18 se resolvió corriendo migrate a mano. La documentación
operativa del Cloud presenta los dos caminos como válidos.
El runbook operativo está en despliegues, con el incidente del 2026-08-18 como caso de estudio.
La configuración vive fuera de los repositorios¶
Es la característica más importante del despliegue y la que más riesgo de traspaso concentra:
| Dónde viven los valores reales | Qué hay |
|---|---|
| Servicio de Cloud Run | todas las variables de entorno de la API, incluida la cadena de conexión a Postgres |
| Gestor de secretos de Google Cloud | la clave de servicio de Supabase |
| Proyecto de Vercel | las variables del frontend y del BFF |
| Secretos del repositorio en GitHub | credenciales de Vercel y de los usuarios de prueba de E2E |
Archivo .env en cada Raspberry Pi |
identificador de máquina, clave de API del dispositivo, token de InfluxDB |
En los repositorios solo hay archivos de ejemplo con marcadores. Eso es correcto, pero tiene un costo: ningún repositorio permite reconstruir un ambiente. La lista de qué variable va en cada plataforma, y quién puede leerla, es configuración y secretos.