Saltar a contenido

Workflows CI/CD

Dos workflows orquestan el ciclo de vida. Ambos corren en self-hosted runners (ver Setup runners). Decisión y trade-offs en ADR-0006.

.github/workflows/ci.yml

Dispara en pull_request y push a main. Cinco jobs paralelos:

flowchart LR
    PR[PR / push main] --> node
    PR --> parity[parity-gate]
    PR --> integ[integration
LocalStack] PR --> tf[terraform
fmt+validate] PR --> sec[security
tfsec+checkov+semgrep]
Job Qué hace Bloqueante
node npm ci + typecheck + test:unit + lint (400 tests)
parity-gate Re-corre 21 flows legacy transpilados, ≥60 escenarios input→expected
integration LocalStack + test:integration (DDB/S3/SQS/EB/SM/SSM/IAM/KMS/STS)
terraform terraform fmt -check + validate por módulo + validate envs/dev
security tfsec (soft-fail dev), checkov con .checkov.yaml, semgrep p/owasp-top-ten p/nodejs (informativo dev) parcial — bloqueante en qa/prod

El parity-gate es el cutover gate del legacy. Ver Parity Gate y ADR-0012.

.github/workflows/cd-dev.yml

Dispara en push a main (auto) y workflow_dispatch (manual).

flowchart TD
    Trigger[push main / dispatch] --> infra[plan-apply-infra
+ preserva tags ECR] infra --> build[build-push
matrix 11 servicios
linux/arm64
max-parallel: 2] build --> apply[apply-services
tf apply con nuevos image_tag] apply --> smoke[smoke-tests
integration tests]

Job plan-apply-infra

  1. Asume zen-dev-github-deployer vía OIDC.
  2. terraform init con backend S3.
  3. Resolve existing ECR tags: lee el último tag pusheado por servicio. Esto evita que un apply-infra inicial (antes de build-push) destruya Lambdas/integrations/routes con image_uri vacío. Si no hay imagen, pasa "" y se crea la infra sin Lambda hasta el primer build.
  4. terraform apply con -var=image_tag_<svc>=<tag preservado>.
  5. Exporta los ARNs de los 11 ECR repos como outputs.

Job build-push (matrix de 11 servicios)

  • fail-fast: false → si un servicio falla, los demás completan.
  • max-parallel: 2 → buildx con qemu (x86_64 emulando arm64) satura RAM/CPU del host si corren 3+ concurrentes.
  • Free runner cache antes del build: docker buildx prune --keep-storage 2gb --filter unused-for=1h --force y docker container prune -f --filter "until=2h". Lección aprendida: los runners self-hosted llenan disco con layers de buildx y aparecen graceful_stop EOF.
  • ECR login fresh (aws-actions/amazon-ecr-login@v2) antes del build y antes del retry. Lección: el prune previo puede tardar y dejar credenciales próximas a expirar.
  • Retry con no-cache: true si la primera build falla (buildx flaky en concurrent).
  • Tag de imagen: ${GITHUB_SHA::12}.

Job apply-services

terraform apply con -var=image_tag_<svc>=${{ build-push.outputs.image_tag }} para los 11. Esto actualiza las Lambda functions a la imagen recién pusheada.

Job smoke-tests

Integration tests (LocalStack opcional en dev). En qa/prod se ampliará a golden-paths Playwright.

Lecciones históricas (preservar)

Issue Fix
apply-infra destruía Lambdas en primer run Leer último tag ECR y pasarlo como -var
graceful_stop EOF en builds concurrentes max-parallel: 2 + buildx prune antes
ECR auth token expired después del prune ecr-login justo antes de build/retry
Buildx flaky con QEMU ARM64 Retry con no-cache: true

Triggers manuales

gh workflow run cd-dev.yml
gh workflow run ci.yml

Permisos OIDC

id-token: write en cd-dev.yml. CI sólo necesita contents: read.

Estados a futuro

  • cd-qa.yml con aprobación manual + smoke Playwright.
  • cd-prod.yml con canary y rollback automático.
  • tenant-onboard.yml para provisionar tenants enterprise (stack dedicado en infra/tenants/<id>/).