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) |
sí |
parity-gate |
Re-corre 21 flows legacy transpilados, ≥60 escenarios input→expected | sí |
integration |
LocalStack + test:integration (DDB/S3/SQS/EB/SM/SSM/IAM/KMS/STS) |
sí |
terraform |
terraform fmt -check + validate por módulo + validate envs/dev |
sí |
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¶
- Asume
zen-dev-github-deployervía OIDC. terraform initcon backend S3.- Resolve existing ECR tags: lee el último tag pusheado por
servicio. Esto evita que un
apply-infrainicial (antes debuild-push) destruya Lambdas/integrations/routes conimage_urivacío. Si no hay imagen, pasa""y se crea la infra sin Lambda hasta el primer build. terraform applycon-var=image_tag_<svc>=<tag preservado>.- 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 --forceydocker container prune -f --filter "until=2h". Lección aprendida: los runners self-hosted llenan disco con layers de buildx y aparecengraceful_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: truesi 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¶
Permisos OIDC¶
id-token: write en cd-dev.yml. CI sólo necesita contents: read.
Estados a futuro¶
cd-qa.ymlcon aprobación manual + smoke Playwright.cd-prod.ymlcon canary y rollback automático.tenant-onboard.ymlpara provisionar tenants enterprise (stack dedicado eninfra/tenants/<id>/).