2026-08-05 · lectura de 3 minutos
Exit 0 con todos los vendors inalcanzables
El peor hallazgo de nuestra auditoría de readiness de agosto fue una salida limpia: scan y diff devolvían 0 con cero vendors legibles, y plan escribía un archivo de plan real sin acciones y con la línea 'Required gates: none'.
Lo que encontró la auditoría
LaunchOps es la parte de nuestra plataforma que lee la configuración del día de lanzamiento de vendors como Vercel y Stripe, la compara contra un manifiesto declarado y gobierna los cambios. Durante una verificación end-to-end este agosto ejecutamos toda la superficie desde una instalación limpia, antes de configurar credenciales de ningún vendor. Todos los vendors eran inalcanzables, y los comandos seguían saliendo con 0.
Cada vendor inalcanzable imprimía una línea [err], así que un humano mirando la terminal veía el problema al instante. El código de salida del proceso decía lo contrario. CI no mira terminales; lee códigos. Cualquier pipeline construido como 'escanea, y sigue si pasó' habría seguido.
# before the fix # since 4.16.0 $ es launch scan $ es launch scan [err] vercel Missing credentials [err] vercel Missing credentials [err] github Missing credentials [err] github Missing credentials $ echo $? $ echo $? 0 1
El comando plan era peor. Construir un plan desde un scan que no había leído nada producía un archivo de plan sintácticamente perfecto con la lista de acciones vacía, persistido a disco, declarando que no se requerían gates. Aguas abajo de ese archivo, 'no pude leer' y 'leí, y no encontré nada' son el mismo artefacto. Esa conversión es la parte peligrosa, más que cualquier código de salida equivocado.
El contrato de códigos de salida
Desde 4.16.0 los códigos de salida son la interfaz: scan y diff salen con 1 cuando algún vendor fue ilegible, y plan se niega de plano a construir desde una lectura parcial. La negativa nombra los vendors que no pudo alcanzar y las variables de entorno que le faltaban a cada uno, para que el operador arregle las credenciales y reintente.
Un caso necesitó cuidado. Un operador que excluye un vendor con --skip-vendors lo hizo a propósito, así que los skips deliberados se muestran como [skip] y mantienen la salida neutral. Si los skips contaran como fallos la puerta estaría roja en la mayoría de ejecuciones, y un check rojo en la mayoría de ejecuciones acaba desactivado.
De dónde salen los defaults fail-open
Nadie escribe 'devuelve éxito ante el fallo' a propósito. El nuestro se acumuló desde cuatro pasos ordinarios: el bucle de scan captura errores por vendor para que un vendor malo no aborte al resto; los errores capturados se vuelven objetos de resultado; los resultados se muestran como líneas [err]; y la ejecución llega al final de main(), donde el estado de salida por defecto es 0. Nada en ese camino convierte un error de vendor en una salida distinta de cero.
Incluso vivíamos con la consecuencia en nuestra propia automatización. El smoke semanal de vendors no podía confiar en el código de salida, así que su workflow buscaba con grep los marcadores de fallo en la salida capturada, y el grep llevaba un comentario explicando por qué. En retrospectiva, ese comentario era un bug report contra nuestros propios códigos de salida; nadie lo había archivado como tal.
Lo que nos llevamos
- CI lee códigos de salida, no salida de terminal. Si una herramienta imprime [err] y aún así sale con 0, el error es invisible para cada pipeline construido encima.
- 'No pude leer' debe seguir siendo distinguible de 'leí, y no encontré nada'. Cualquier artefacto que borre la distinción, como un archivo de plan vacío, acabará siendo creído por algo.
- Mantén los skips deliberados con salida neutral. Si las decisiones rutinarias del operador ponen una puerta en rojo, la gente deja de tratar el rojo como señal.
Fuentes: el arreglo se publicó el 2026-08-05 como @enterprise-skills/launchops-core · cómo funcionan nuestras release gates · La guía de listo para producción
El grep de salida en el workflow del smoke sigue ahí, una capa por debajo de los códigos de salida que ahora también fallan cerrados; lo mantuvimos como una segunda comprobación independiente. La auditoría que encontró todo esto, y los otros siete hallazgos clasificados con ella, dieron forma al diseño v2 esa misma semana.
Todas las notas de ingeniería →