Pipelines que piensan, infra que no rompe: Kiro headless mode en acción (GitLab)
Build with fire, deploy with power 🔥

Soy Roxs 👩💻| Software Developer | DevOps | DevSecOps | en @295DevOps 🖼 Content Creator. No se puede crecer si no estas dispuesto a saltar a la zona de peligro 🔥
Por @roxsross · roxs.dev
Introducción
¿Cuántas veces mergeaste un cambio de infraestructura y te enteraste del problema en producción? ¿Cuántos security groups abiertos al mundo, cuántos IAM con * en Action, cuántos secrets hardcodeados llegaron a main porque nadie revisó el Terraform con suficiente detalle?
La revisión manual de infraestructura como código no escala. Los equipos crecen, los Merge Requests se acumulan, y la presión por deployar rápido termina ganándole al rigor. Lo que necesitamos es que el pipeline piense por nosotros — y exactamente eso es lo que construimos en esta demo.
En este artículo te muestro cómo armé un flujo completo de revisión automática de IaC con Kiro headless mode, integrado directamente en GitLab CI/CD, que analiza cada MR de Terraform, postea una note con hallazgos de seguridad, calidad y buenas prácticas, y además mantiene un issue Dashboard vivo con todos los findings. Sin intervención humana, sin herramientas externas, sin configuración compleja.
Si venís de la versión GitHub Actions de esta demo: el concepto es idéntico, pero acá todo está portado a
.gitlab-ci.ymly a la API REST de GitLab. Más abajo tenés una tabla de equivalencias.
¿Qué es Kiro headless mode?
Kiro CLI 2.0 introdujo el modo headless (sin interfaz gráfica), una forma de ejecutar kiro-cli de manera programática dentro de cualquier entorno automatizado.
La idea es simple: generás una API key en kiro.dev/settings/api, la agregás como variable de entorno, y Kiro CLI se ejecuta sin necesidad de que haya alguien sentado frente a una terminal.
Lo que lo hace poderoso:
Acceso completo a todos los agentes, herramientas y capacidades de una sesión interactiva.
Sin intervención humana: podés generar MRs, ejecutar reviews, troubleshootear o documentar sin que nadie esté monitoreando.
Multiplataforma: funciona nativamente en macOS, Linux y Windows.
Una línea para instalar:
curl -fsSL https://cli.kiro.dev/install | bash
La demo: review automático de Terraform en cada MR
El escenario
Tenemos un repositorio con infraestructura en Terraform para un servicio ECS Fargate en AWS. El archivo main.tf tiene bugs intencionales — security groups abiertos, IAM demasiado permisivo, CIDR blocks inválidos, secretos expuestos — diseñados para que Kiro los detecte.
El objetivo: que cada MR que toque terraform/ reciba automáticamente una note con un análisis profundo de seguridad, calidad e infraestructura, sin que nadie tenga que pedirlo.
Estructura del proyecto
Una diferencia clave con GitHub: los dos workflows de GitHub Actions (kiro-review.yml y kiro-docs.yml) se consolidan en un solo .gitlab-ci.yml con dos jobs (kiro:review y kiro:docs), separados por reglas (rules).
Cómo funciona el pipeline de review
El job kiro:review es el corazón de la demo. Veamos cada parte.
1. Workflow rules: cuándo corre el pipeline
En GitLab, en vez de on: pull_request, controlamos los disparadores con workflow:rules a nivel del pipeline completo:
workflow:
rules:
# MR abierto/actualizado -> pipeline de merge request
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# Push a la rama default -> pipeline de rama (para docs)
- if: '\(CI_COMMIT_BRANCH == \)CI_DEFAULT_BRANCH'
# Disparo manual desde la UI (Run pipeline)
- if: '$CI_PIPELINE_SOURCE == "web"'
Esto, además, evita el clásico problema del doble pipeline (branch + MR corriendo a la vez) que sufre quien arranca con GitLab CI sin reglas.
2. El job y sus reglas de ejecución
kiro:review:
stage: review
image: node:20-bookworm-slim
timeout: 15m
interruptible: true
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
- terraform/**/*
- if: '\(CI_PIPELINE_SOURCE == "web" && \)REVIEW_MODE'
Tres cosas a destacar:
changes: terraform/**/*es el equivalente apaths:de GitHub: el review solo corre cuando el MR toca infraestructura.interruptible: truereemplaza alconcurrency: cancel-in-progressde GitHub. Si pusheás de nuevo al mismo MR, el run anterior se cancela. Ahorra créditos de Kiro y evita notes duplicadas.El segundo
rulehabilita el review manual on-demand desde Run pipeline, pasando la variableREVIEW_MODE(focusedofull).
3. Ejecución de Kiro CLI
El paso clave es la invocación de kiro-cli chat con el agente code-reviewer:
kiro-cli chat \
--no-interactive \
--agent code-reviewer \
--trust-all-tools \
"Realiza una revisión completa de seguridad, calidad y buenas prácticas
de infraestructura sobre el directorio terraform/..."
Los flags importantes:
| Flag | Qué hace |
|---|---|
--no-interactive |
Modo headless, sin esperar input del usuario |
--agent code-reviewer |
Usa el agente especializado en review de código |
--trust-all-tools |
Permite al agente usar todas las herramientas disponibles |
El prompt le pide a Kiro que lea todos los archivos .tf (no solo los modificados), analice seguridad, IAM, secretos, red, cifrado, tags y costos, organice los hallazgos por severidad (Crítico / Alto / Medio / Bajo), incluya archivo afectado, líneas, impacto y fix accionable, y genere un resumen ejecutivo con priorización.
4. Agentes custom: que el warning no te agarre desprevenido
Acá hay un detalle que en GitHub a veces pasa inadvertido: los agentes code-reviewer y doc-generator no existen por defecto en Kiro. Son agentes custom que se definen con archivos JSON. Si no los tenés, Kiro tira:
Error: no agent with name code-reviewer found. Falling back to user specified default
…y corre con el agente genérico. Funciona, pero perdés el comportamiento afinado. Por eso los definí como agentes locales en .kiro/agents/, que Kiro descubre automáticamente al correr desde la raíz del repo:
| Agente | Rol | Permisos |
|---|---|---|
code-reviewer |
Audita Terraform | Read-only sobre el código; solo escribe kiro-findings.json |
doc-generator |
Genera docs | Escribe solo en docs/** y README.md |
El code-reviewer es read-only por diseño: puede leer todos los .tf y correr comandos de solo lectura, pero tiene terraform apply, git push y rm explícitamente denegados. Un reviewer no debería poder tocar nada.
Otro warning benigno que vas a ver en CI:
Failed to retrieve MCP settings; MCP functionality disabled. Ignoralo. En modo headless con API key no hay perfil de usuario ni servidores MCP configurados, y el review no los necesita — usa las herramientas nativas de filesystem.
5. Limpieza de salida
La salida cruda de kiro-cli incluye secuencias ANSI, spinners, logs internos y fences de código rotos. El script clean-kiro-output.js la transforma en Markdown limpio en 7 fases:
| Fase | Descripción |
|---|---|
| 1 — ANSI | Elimina secuencias de escape, spinners, box-drawing |
| 2 — Content start | Detecta dónde empieza el contenido real |
| 3 — Noise | Remueve logs de herramientas y meta-comentarios del agente |
| 4 — Loose fences | Convierte marcadores de lenguaje sueltos en code fences |
| 5 — Fence repair | Fusiona bloques HCL partidos, cierra fences huérfanos |
| 6 — Cleanup | Normaliza saltos de línea, asegura paridad de fences |
| 7 — Write | Escribe el resultado final |
Cada fase está envuelta en try/catch individual — si una falla, las demás siguen ejecutándose.
6. Note en el MR
Acá está la diferencia técnica más grande con GitHub. En GitHub usás actions/github-script para postear el comentario. En GitLab no hay marketplace de actions, así que escribimos post-mr-note.js, que usa la API REST de GitLab (solo el módulo https nativo de Node, cero dependencias).
El script:
Lee el Markdown limpio.
Busca si ya existe una note previa del bot (por un marcador
## 🤖 Kiro Code Review).Si existe, la actualiza in-place (
PUT). Si no, la crea (POST).
Esto elimina el spam de notes cuando iterás sobre el mismo MR.
Usa las variables predefinidas de GitLab CI_API_V4_URL, CI_PROJECT_ID y CI_MERGE_REQUEST_IID, más un token de API.
⚠️ Detalle clave de auth: el
CI_JOB_TOKENque GitLab inyecta en cada job no puede escribir notes en MRs ni pushear ramas. Por eso se necesita un Project Access Token con scopeapi(ywrite_repositorypara el job de docs), guardado como variable CI/CDGITLAB_API_TOKEN.
Resultados en acción
Esto es lo que Kiro genera automáticamente en cada MR. Cada hallazgo incluye título claro del problema, archivo y líneas afectadas, explicación de por qué es un problema, impacto real en la infraestructura, y fix accionable con ejemplo de código corregido.
Al final del review, Kiro genera un resumen con la severidad y cantidad de hallazgos, más una priorización recomendada. Y el issue Dashboard queda como punto único de verdad para trackear el progreso de los fixes.
Bonus: documentación automática con Kiro
El repo incluye un segundo job (kiro:docs) que demuestra otra capacidad de Kiro headless: generación automática de documentación.
Cómo funciona:
Se dispara con cada push a la rama default que modifique código fuente (ignora
docs/,*.md,.gitlab/**y.gitlab-ci.yml).Kiro escanea el repo completo con el agente
doc-generator.Actualiza
docs/yREADME.mdsegún el estado real del código.Si hay cambios, los pushea a una rama
docs/auto-update-<timestamp>.
kiro:docs:
stage: docs
rules:
- if: '\(CI_COMMIT_BRANCH == \)CI_DEFAULT_BRANCH'
changes:
- "**/*"
- "!docs/**/*"
- "!**/*.md"
- "!.gitlab/**/*"
- "!.gitlab-ci.yml"
El push de la rama de docs usa la URL autenticada con oauth2:
git push -o ci.skip \
"https://oauth2:\({GITLAB_API_TOKEN}@\){CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" \
"HEAD:${BRANCH}"
Dos detalles que aprendí a los golpes: el username canónico para pushear con un Access Token es oauth2 (un username arbitrario te da HTTP Basic: Access denied), y el flag -o ci.skip evita que el push dispare un pipeline en cadena.
Esto muestra que Kiro no es solo para reviews puede ser parte activa del mantenimiento de un proyecto, manteniendo la documentación al día como parte del flujo CI/CD.
Setup: cómo replicar esto en tu repo
Requisitos previos
Cuenta de GitLab con acceso a CI/CD.
API key de Kiro (
kiro.dev/settings/api), requiere plan Pro, Pro+ o Power.
Paso a paso
1. Fork / importá el repo
https://gitlab.com/roxsross/kiro-iac-review
2. Creá un Project Access Token
Settings → Access tokens → Add new token
Role:
Developer(o superior)Scopes:
api+write_repository
3. Configurá las variables CI/CD
Settings → CI/CD → Variables → Add variable:
| Variable | Masked | Descripción |
|---|---|---|
KIRO_API_KEY |
✅ | API key de Kiro para auth headless |
GITLAB_API_TOKEN |
✅ | El Project Access Token del paso 2 |
4. Abrí un MR que toque Terraform
git checkout -b feat/test-review
# Hacé un cambio en terraform/main.tf
git add terraform/main.tf
git commit -m "feat(terraform): test kiro review"
git push -u origin feat/test-review \
-o merge_request.create \
-o merge_request.target=main
5. Esperá ~60 segundos
La note de Kiro aparece automáticamente en el MR, y el issue Dashboard se crea/actualiza.
Protección de la rama default recomendada
Para que el review sea enforzable:
Settings → Repository → Protected branches → rama main, con push restringido (solo vía MR).
Y en Settings → Merge requests:
✅ Pipelines must succeed
✅ All threads must be resolved
✅ Squash commits when merging
Tabla de equivalencias GitHub → GitLab
Si ya conocías la versión GitHub, esto te ubica rápido:
| GitHub Actions | GitLab CI/CD |
|---|---|
.github/workflows/kiro-review.yml |
job kiro:review en .gitlab-ci.yml |
.github/workflows/kiro-docs.yml |
job kiro:docs en .gitlab-ci.yml |
on: pull_request (paths) |
rules: merge_request_event + changes: |
on: workflow_dispatch |
rules: $CI_PIPELINE_SOURCE == "web" + var |
concurrency: cancel-in-progress |
interruptible: true |
secrets.GITHUB_TOKEN |
GITLAB_API_TOKEN (Project Access Token) |
actions/github-script (PR comment) |
post-mr-note.js (API REST de GitLab) |
github.event.pull_request.number |
CI_MERGE_REQUEST_IID |
| Pull Request | Merge Request |
| Comentario en PR | Note en MR + issue Dashboard |
Lecciones aprendidas
Lo que funcionó bien
El prompt es todo. La calidad del review depende directamente del prompt que le das a Kiro. Ser explícito sobre qué analizar, en qué formato reportar y qué priorizar hace una diferencia enorme.
La limpieza de salida es necesaria. La salida cruda de
kiro-cliincluye ruido del CLI que rompe el Markdown. El script de 7 fases resilientes lo resuelve sin fragilidad.Note update > note create. Actualizar la misma note en vez de crear una nueva mantiene el MR limpio cuando iterás. El mismo principio aplica al issue Dashboard.
interruptibleahorra créditos. Clave cuando el equipo pushea seguido al mismo MR.
Lo que hay que tener en cuenta
Auth de GitLab: el
CI_JOB_TOKENno alcanza para escribir notes ni pushear. Necesitás un Access Token con scopeapi. Y para pushear, usernameoauth2.Créditos: cada run consume créditos de Kiro. Usá
changes:con paths específicos para evitar runs innecesarios.Timeout: 15 minutos alcanza para repos chicos. Para repos grandes, ajustá.
El JSON de findings puede fallar: los LLMs a veces se desvían del formato pedido. El script es tolerante, y si el JSON sale roto, la note del MR igual se postea — no se cae el pipeline.
El review no reemplaza al humano: Kiro encuentra problemas técnicos, pero las decisiones de arquitectura y los trade-offs siguen siendo humanos.
Conclusión
Lo que construimos acá es simple en concepto pero poderoso en práctica: un pipeline que piensa antes de deployar. Kiro headless mode corre dentro de GitLab CI/CD, revisa cada MR de Terraform de forma automática, postea una note estructurada con hallazgos accionables, y mantiene un Dashboard de issues que se va limpiando a medida que arreglás la infra.
No reemplaza la revisión humana — la potencia. Te da un primer pase automatizado que atrapa los problemas obvios (y no tan obvios) antes de que lleguen a producción. Y como bonus, puede mantener tu documentación actualizada sin esfuerzo extra.
El código está disponible para que lo forkees, lo pruebes y lo adaptes:
https://gitlab.com/roxsross/kiro-iac-review
Referencias
🔥 Build with fire, deploy with power 🔥
@roxsross · roxs.dev





