5 playbooks nuevos en docs/playbooks/ del portal: - SQLite en modo DELETE causando SQLITE_BUSY, por que WAL lo resuelve. - Anchors YAML no soportados por el parser de Gitea Actions, incluido el diagnostico equivocado inicial (indentacion) que se corrigio despues -- documentado con honestidad como leccion de proceso. - Docker-in-Docker vs sibling containers, y por que Semgrep corre nativo en un venv en vez de como container aparte. - El SNI/realm HTTP del tunel rompiendo especificamente a Cosign (docker push funcionaba, cosign sign no), conectado con el incidente ya documentado del 504 -- misma capa de ingress, dos fallas distintas. - Contencion de recursos entre Gitea y su runner: causa raiz real confirmada en vivo durante esta misma sesion (ambos contenedores reiniciados a mitad de un build, sin limites de CPU/memoria reales), con fix propuesto (ver rama fix/gitea-runner-resource-limits en scripts/) pendiente de aplicacion manual. Se actualiza el playbook del 504 para linkear hacia el nuevo hallazgo de contencion, cerrando el loop que habia quedado abierto ahi. cosign.md: nueva seccion "Limitaciones conocidas y mejoras futuras" con los dos TODO reales -- firma por tag en vez de digest (con el warning real de Cosign visto en los logs) y transparency log de Rekor deshabilitado, explicado en simple. index.md: tabla resumen de los 3 pipelines (imagen + adaptacion de Semgrep por stack), diagrama generalizado para reflejar que es el mismo patron en los 3 workflows, y "que falta" actualizado. Validado con mkdocs build --strict (0 errores, 0 warnings) antes de commitear.
4.9 KiB
Incidente: anchors YAML no soportados por Gitea Actions — y un diagnóstico equivocado en el camino (2026-08)
| Ventana del incidente | 2026-08-15, desde el primer commit que agregó Gitleaks a build.yaml |
| Servicio afectado | Gitea Actions — el workflow build.yaml (frontend) |
| Impacto | build.yaml no generaba ningún action_run, ni en push ni en pull_request — el pipeline entero era invisible para Gitea, sin ningún error visible en la UI |
Qué es un anchor/alias YAML, para quien no lo conoce
YAML tiene una forma de evitar repetir el mismo bloque dos veces:
paths: &frontend_paths
- 'workloads/ecommerce/**'
- '.gitea/workflows/build.yaml'
on:
push:
paths: *frontend_paths
pull_request:
paths: *frontend_paths
&frontend_paths es un anchor (marca ese nodo con un nombre).
*frontend_paths es un alias (dice "poné acá una copia de lo que
marcó ese anchor"). Es YAML 100% estándar — cualquier parser que siga
la especificación lo resuelve sin problema, y es una forma común de no
duplicar listas idénticas en el mismo archivo.
Síntoma
build.yaml (con Gitleaks recién agregado, usando anchors para no
repetir la lista de paths entre push: y pull_request:) no
generaba ningún run en Gitea Actions. Ni en el push directo a la rama
del PR, ni al abrir el pull request. Sin mensaje de error visible en
la UI de Gitea — el workflow simplemente no aparecía en la lista de
Actions, como si no existiera.
Primer diagnóstico (equivocado)
La primera sospecha fue una indentación rota específicamente en el
step de Semgrep: el bloque python3 -c "..." embebido dentro de un
run: | tenía código pegado a columna 0, por debajo de la
indentación esperada del block scalar de YAML. Eso sí era un
problema real — cortaba el block scalar ahí mismo y en teoría podía
hacer que Gitea rechazara el archivo con un error de parseo genérico
(could not find expected ':').
Se corrigió la indentación, se validó con un parser YAML real y
bash -n sobre los 20 steps del archivo, se mergeó a main como fix
(fix(ci): corregir indentación YAML rota en el step de Semgrep, PR
#19)... y build.yaml siguió sin generar runs.
!!! warning "Por qué vale la pena contar el diagnóstico que no era"
El primer fix no estaba mal — la indentación efectivamente estaba
rota y corregirla era necesario. El error fue asumir que esa era
la única causa sin confirmarlo con una corrida real después del
fix. La lección de proceso: un fix que "tiene sentido" y que pasa
la validación local (parser YAML, bash -n) todavía necesita una
confirmación en vivo contra el sistema real antes de darlo por
cerrado — sobre todo cuando el síntoma es "no pasa nada" en vez de
un error explícito, que es exactamente el tipo de fallo más fácil
de dar por resuelto sin verificar.
Causa raíz real
deploy-docs.yaml (sin anchors) corría normal. build.yaml (con
anchors, agregados junto con Gitleaks) no generaba ningún run — ni
en push ni en pull_request, desde el primer commit que los
introdujo. El log del propio Gitea lo confirmó:
unknown on type: &yaml.Node{...Value:"frontend_paths"...}
El parser propio de Gitea Actions para el bloque on: de un workflow
no resuelve &anchor/*alias antes de inspeccionar el tipo de
nodo — encuentra un anchor donde esperaba un valor ya resuelto, no
sabe qué tipo de dato es, y descarta el archivo completo en
silencio. pyyaml y cualquier parser YAML estándar sí resuelven el
alias primero (por eso el archivo "parseaba bien" en cualquier
herramienta de validación genérica) — es una limitación específica del
parser de workflows de Gitea Actions, no un YAML inválido.
Esto explica por qué el síntoma no tenía ningún error visible: no es que el job fallara, es que Gitea nunca llegaba a registrar el workflow como existente.
Fix aplicado
Se eliminaron los anchors/alias. Las dos listas de paths (una para
push:, otra para pull_request:) quedan duplicadas
literalmente, sin referencia compartida:
on:
push:
branches: [main]
paths:
- 'workloads/ecommerce/**'
- '.gitea/workflows/build.yaml'
pull_request:
branches: [main]
paths:
- 'workloads/ecommerce/**'
- '.gitea/workflows/build.yaml'
Es más repetitivo, pero es el precio de que el parser de Gitea
Actions lo entienda. Este mismo criterio (sin anchors, paths
duplicados) se replicó a propósito en build-medusa.yaml y
deploy-docs.yaml al agregarles el resto del pipeline de seguridad,
para no repetir el mismo problema.
Validación
Confirmado en vivo: tras mergear el fix, build.yaml generó su
primer action_run real. Validado también con un parser YAML +
bash -n sobre los 20 steps del archivo, igual que en el intento
anterior — la diferencia esta vez fue confirmarlo contra una corrida
real antes de cerrar el incidente.