Skip to content

Runbook: Deploy de Staging — de cero a andando

Date: 2026-07-25 Status: 🔴 Nada de esto está creado todavía (ni Firebase ni AWS) — es la guía completa, en orden

URLs correctas (reemplazan cualquier placeholder anterior mal definido)

SitioURL
App (frontend)https://soterex.stag.cislatam.net
API (backend)https://api.soterex.stag.cislatam.net
Documentación funcional (VitePress)https://docs.soterex.stag.cislatam.net
Referencia de API (ReDoc)https://docs.api.soterex.stag.cislatam.net

Patrón: {servicio}.soterex.stag.cislatam.net — el stag va después de soterex, no antes de cislatam. Para producción, mismo patrón sin el segmento stag (a confirmar cuando llegue el momento: soterex.cislatam.net, api.soterex.cislatam.net, etc.).

Qué ya se corrigió en el código (este mismo paquete)

  • [x] doc/.vitepress/config.mts — el link "Referencia de API" ahora sale de una variable de entorno de build (DOCS_API_URL), no está hardcodeado.
  • [x] firebase.json (raíz, nuevo) — coordina los 3 sitios estáticos (app, docs, docs-api) como "hosting targets" de un mismo proyecto Firebase, en vez de configs sueltas sin coordinar.
  • [x] .firebaserc (raíz, nuevo) — mapea los targets a los Site IDs reales de Firebase Hosting (placeholders — completar en el paso 2).
  • [x] infra/terraform/environments/staging/terraform.tfvars.examplecors_allowed_origins corregido a https://soterex.stag.cislatam.net.
  • [x] .github/workflows/deploy-staging.yml — se agregaron los jobs deploy-docs y deploy-docs-api (antes no se deployaba ninguna de las 2 webs de documentación, solo la app y el backend).

Paso 1 — Crear el proyecto de Firebase (si no existe)

bash
firebase login
firebase projects:create soterex-staging --display-name "Soterex — Staging"

(o usar la consola de Firebase si preferís — mismo resultado)

Anotar el Project ID real que Firebase asigna — reemplaza REEMPLAZAR-PROJECT-ID-STAGING en .firebaserc.

Paso 2 — Crear los 3 Hosting Sites dentro de ese proyecto

Un proyecto de Firebase puede tener varios "sites" de Hosting — necesitamos 3:

bash
firebase hosting:sites:create soterex-stag-app --project soterex-staging
firebase hosting:sites:create soterex-stag-docs --project soterex-staging
firebase hosting:sites:create soterex-stag-docs-api --project soterex-staging

Actualizar .firebaserc con el Project ID real (reemplazando el placeholder) — los nombres de sites (soterex-stag-app, etc.) ya coinciden con lo que dejé preparado.

Paso 3 — Habilitar Firebase Authentication (Email/Password)

En la consola de Firebase → Authentication → Sign-in method → habilitar Email/Password. Crear al menos 1 usuario real de prueba (o coordinarlo con el DevUserSeeder del backend, si corre contra este mismo proyecto de Firebase).

Del proyecto, sacar los 6 valores de config (Configuración del proyecto → General → tus apps → Web app → SDK config): apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId — van a los GitHub Secrets del Paso 7.

Paso 4 — Conectar los dominios custom en Firebase Hosting

Para cada uno de los 3 sites, en la consola de Firebase → Hosting → ese site → Agregar dominio personalizado:

  • soterex-stag-appsoterex.stag.cislatam.net
  • soterex-stag-docsdocs.soterex.stag.cislatam.net
  • soterex-stag-docs-apidocs.api.soterex.stag.cislatam.net

Firebase te va a dar registros DNS específicos para cada uno (normalmente un registro TXT para verificar propiedad, y después un A/CNAME) — pasarle esos registros a quien administre el DNS de cislatam.net para que los cargue. Firebase valida solo apenas detecta el DNS propagado (puede tardar unos minutos a unas horas).

Paso 5 — Certificado ACM para el backend (api.soterex.stag.cislatam.net)

El backend no va a Firebase — va a AWS (ALB), y necesita su propio certificado TLS:

bash
aws acm request-certificate \
  --domain-name api.soterex.stag.cislatam.net \
  --validation-method DNS \
  --region us-east-1

Esto devuelve un CertificateArn y los detalles del registro CNAME que hay que agregar al DNS para validarlo (aws acm describe-certificate --certificate-arn <arn> para verlos). Pasarle ese CNAME también a quien administre el DNS. El certificado queda en estado PENDING_VALIDATION hasta que el DNS se propague — recién ahí se puede usar en Terraform.

Paso 6 — Terraform: bootstrap (si no se hizo nunca) + Staging

bash
cd infra/terraform/bootstrap
terraform init && terraform apply   # una sola vez por cuenta de AWS, si no se hizo ya

cd ../environments/staging
cp terraform.tfvars.example terraform.tfvars
# completar terraform.tfvars con el ARN real del certificado del Paso 5
terraform init
terraform plan   # revisar antes de aplicar
terraform apply

Al final, terraform output te da: alb_dns_name, el nombre del cluster ECS, los repos ECR, etc. — el alb_dns_name es lo que necesita quien administre el DNS para apuntar api.soterex.stag.cislatam.net ahí (un CNAME al DNS name del ALB, o un registro ALIAS si el DNS de cislatam.net está en Route53).

Paso 7 — Cargar los GitHub Secrets

Lista completa (Settings → Secrets and variables → Actions, en el repo):

Generales / AWS:AWS_REGION, AWS_TERRAFORM_APPLY_ROLE_ARN, AWS_STAGING_DEPLOY_ROLE_ARN, INFRA_ALERT_EMAIL

Firebase (Staging):STAGING_FIREBASE_API_KEY, STAGING_FIREBASE_AUTH_DOMAIN, STAGING_FIREBASE_PROJECT_ID, STAGING_FIREBASE_STORAGE_BUCKET, STAGING_FIREBASE_MESSAGING_SENDER_ID, STAGING_FIREBASE_APP_ID, STAGING_FIREBASE_SERVICE_ACCOUNT (JSON de la service account — Firebase Console → Configuración del proyecto → Cuentas de servicio → Generar nueva clave)

URLs (Staging) — usar las de la tabla de arriba:STAGING_API_BASE_URL = https://api.soterex.stag.cislatam.netSTAGING_DOCS_URL = https://docs.soterex.stag.cislatam.netSTAGING_DOCS_API_URL = https://docs.api.soterex.stag.cislatam.net

Infra (Staging, salen de terraform output del Paso 6):STAGING_ACM_CERTIFICATE_ARN, STAGING_ECR_REPOSITORY, STAGING_ECS_CLUSTER, STAGING_ECS_WEB_SERVICE, STAGING_ECS_WEB_TASK_FAMILY, STAGING_ECS_QUEUE_SERVICE, STAGING_ECS_QUEUE_TASK_FAMILY, STAGING_PRIVATE_SUBNET_IDS, STAGING_ECS_SECURITY_GROUP_ID

Paso 8 — Cargar los secrets del backend real en AWS Secrets Manager

Las credenciales que el backend necesita en runtime (Firebase Admin SDK, credenciales de Soterex — todavía en modo MOCK) se cargan directo en Secrets Manager, no pasan por GitHub:

bash
aws secretsmanager put-secret-value \
  --secret-id soterex-staging-firebase-credentials \
  --secret-string file://firebase-service-account-staging.json

# Soterex sigue en MOCK — no hace falta cargar credenciales reales todavía
# (ver ADR y Configuración → Soterex Settings, editable desde Admin).

(Los nombres exactos de los secrets salen de terraform output del módulo secrets — confirmar ahí antes de correr esto.)

Paso 9 — Primer deploy

bash
git checkout staging
git merge main
git push origin staging

Esto dispara deploy-staging.yml — los 4 jobs (deploy-frontend, deploy-docs, deploy-docs-api, deploy-backend) corren en paralelo. Revisar la pestaña Actions de GitHub.

Paso 10 — Verificación post-deploy

  • [ ] https://soterex.stag.cislatam.net carga el login real.
  • [ ] https://api.soterex.stag.cislatam.net/api/health (o el endpoint de health check que exista) responde 200.
  • [ ] https://docs.soterex.stag.cislatam.net carga el sitio VitePress, y el link "Referencia de API ↗" del nav apunta a https://docs.api.soterex.stag.cislatam.net (no a un placeholder).
  • [ ] https://docs.api.soterex.stag.cislatam.net carga el portal ReDoc con login.
  • [ ] Login real contra el usuario de prueba creado en el Paso 3, y navegación básica de la app.

Orden resumido (para no perderse)

Firebase: crear proyecto → crear 3 sites → habilitar Auth → agregar dominios custom (Paso 1-4)
       ↓ (en paralelo)
AWS: solicitar cert ACM → validar DNS (Paso 5)

AWS: terraform bootstrap + staging apply, usando el cert ya validado (Paso 6)

DNS: cargar todos los registros que fueron saliendo (Firebase x3 + ACM + ALB) — un solo pase

GitHub: cargar todos los secrets (Paso 7) + Secrets Manager (Paso 8)

git push origin staging → primer deploy automático (Paso 9) → verificar (Paso 10)

Documentación viva — se actualiza junto con el código, no es un anexo aparte.