fix(ci): condicion de carrera en el chequeo de promocion GitOps
Build and Push Medusa / Escaneo de Secretos (Gitleaks) (push) Successful in 22s
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 26s
Build and Push Docs Portal / Escaneo de Secretos (Gitleaks) (push) Successful in 28s
Build and Push Medusa / Construir y publicar Medusa (push) Successful in 17m19s
Build and Push Frontend / Construir y Subir Imagen (push) Successful in 8m23s
Build and Push Docs Portal / Construir y publicar Docs Portal (push) Successful in 6m18s

Con el runner corriendo 1 job a la vez (capacity: 1), cuando 2+
pipelines comparten el mismo commit disparador, el primero en terminar
deja su propio commit de GitOps en main -- los siguientes veian 'main
ya no es igual a mi commit' y se saltaban su propia promocion, aunque
era seguro promocionar. Visto en vivo: Frontend construyo, escaneo y
firmo v1.0.119 pero el manifiesto seguia en v1.0.104 (Promocion GitOps:
false). Fix en los 3 workflows: git merge-base --is-ancestor en vez de
comparacion exacta de SHA. Bump manual de frontend a v1.0.119 (la
version que ya estaba lista, sin desplegar). Documentado el limite
conocido de este fix en el playbook de la migracion.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011FqYP3Wf1W63Qmh7qgXAAT
This commit is contained in:
devopsandClaude Sonnet 5 committed 2026-08-17 01:47:04 -05:00
1 parent 788505a1bb
commit 29021bff70
5 files changed
+83 -4

No files matched your search

@@ -110,3 +110,63 @@ público correspondiente después del rollout:
Y con el estado del pod nuevo (`kubectl get pods`, sin reinicios,
`1/1 Ready`) antes de dar cada migración por cerrada.
## Actualización (mismo día): 2 bugs más, esta vez en CI, no en K8s
El primer push real después de migrar las 3 apps (justo el commit que
agregó las secciones de arriba a este playbook) expuso dos problemas
más — ninguno de los dos en Kubernetes esta vez, los dos en los propios
pipelines de Gitea Actions.
### 4. `MANIFEST_FILE` apuntando a una ruta que ya no existía
Mover cada `deployment.yaml` suelto a `.../patches/deployment.yaml`
(necesario para el patrón base+overlay) rompió en silencio el paso de
auto-actualización GitOps de los 3 pipelines (`build.yaml`,
`build-medusa.yaml`, `deploy-docs.yaml`) — cada uno tiene su propio
`MANIFEST_FILE` hardcodeado como variable de entorno, y los 3 seguían
apuntando a las rutas viejas. No se notó hasta el primer build real
post-migración: la imagen se construyó, escaneó y firmó bien, pero el
último paso falló con `sed: can't read ...: No such file or directory`,
dejando una imagen firmada y verificada sin desplegar. Fix: actualizar
las 3 rutas de `MANIFEST_FILE`.
**Para la próxima vez que se mueva un manifiesto:** `grep -rn
MANIFEST_FILE .gitea/workflows/` antes de asumir que un `git mv` dentro
de `workloads/` es solo un problema del propio manifiesto — los
pipelines de CI referencian esas rutas exactas en un archivo aparte.
### 5. Condición de carrera en el chequeo de "promoción segura"
El runner de Gitea de este lab corre **un solo job a la vez**
(`capacity: 1` en su config). Cuando el fix del punto 4 disparó los 3
pipelines a la vez para el mismo commit, se ejecutaron en serie. El
chequeo de "promoción segura" de cada uno compara el commit que lo
disparó contra el HEAD actual de `main` (`if [ "$CURRENT_SHA" =
"$REMOTE_SHA" ]`) para evitar que una corrida vieja pise un manifiesto
más nuevo. Problema real: el primer pipeline en terminar (Medusa) dejó
su propio commit automático en `main` — para cuando les tocó el turno
a Frontend y Docs Portal, `main` ya no coincidía con el commit que los
había disparado, así que ambos se saltaron la actualización del
manifiesto (`Promoción GitOps: false`) aunque era perfectamente seguro
promocionar. Frontend quedó con una imagen nueva construida, escaneada
y firmada (`v1.0.119`) pero sin desplegar — el manifiesto seguía en
`v1.0.104`.
Fix aplicado en los 3 workflows: cambiar la comparación exacta de SHA
por `git merge-base --is-ancestor "$CURRENT_SHA" origin/main` — permite
promocionar mientras el commit disparador siga siendo ancestro de
`main` (tolera que otro pipeline haya commiteado en el medio), y solo
bloquea si de verdad hubo un rebase/force-push que lo descartó.
!!! warning "Límite conocido de este fix, aceptado a propósito"
`is-ancestor` es más permisivo que la comparación exacta en un
caso borde distinto: si 2 commits al **mismo** app quedaran en cola
y el más viejo terminara *después* del más nuevo, ahora sí
promocionaría igual — pisando la versión más nueva con la más
vieja. Con un solo mantenedor en este lab, 2 commits casi
simultáneos al mismo app es muy poco probable; se aceptó el
trade-off en vez de una solución más compleja (ignorar commits
`[skip ci]` del propio bot al calcular "el último commit real").
Revisar si esto deja de ser cierto (más de una persona commiteando,
o automatización que dispare builds más seguido).