Saltar al contenido
Development

Probar un gate de deploy terminó desplegando por accidente lo que estaba probando

Por Victor Da Luz
astrocloudflarecidev-logsite

El pipeline de deploy de imperfectsystems.com tenía exactamente un gate: “compiló o no.” Eso era cierto cuando lo corría GitHub Actions, y siguió siendo cierto después de que moví el deploy a Cloudflare Workers Builds hace un par de semanas. Nada verificaba tipos, nada verificaba formato, y el auto-merge de dependabot que solía apoyarse en esa misma señal débil se acababa de apagar junto con el resto de Actions. Momento de agregar gates que de verdad signifiquen algo: astro check y Prettier, corridos antes de cada deploy.

La configuración fue la parte fácil

Copiar el .prettierrc de vdaluz.com al pie de la letra, agregar @astrojs/check y prettier-plugin-astro como devDependencies, conectar los scripts check / format / format:check. Sin ESLint esta vez, el repositorio es lo bastante chico como para que no valiera el costo de configurarlo, la misma decisión que ya había tomado el spike que definió el alcance de este issue.

Después corrí astro check de verdad en vez de asumir que pasaría, porque “muy probablemente pasa hoy” en la descripción de un issue es una suposición, no un resultado.

El gate encontró algo real en su primera corrida

Un error: astro.config.mjs:12:5 - Type '{ themes: { light: string; dark: string; }; defaultColor: false; }' is not assignable to type 'Partial<ShikiConfig>'. Venía del objeto compartido shikiConfig que tanto este sitio como vdaluz.com importan de @vdaluz/astro-blog.

Mi primera sospecha fue desfase de versiones. Este repositorio seguía fijado a una versión más vieja de Astro; vdaluz.com corre una más nueva. Subí la versión, reinstalé, volví a correr el check. Mismo error, palabra por palabra. Teoría equivocada.

Así que fui a ver de verdad cómo maneja el propio astro.config.mjs de vdaluz.com la misma importación, en vez de seguir adivinando. Ya tenía el arreglo, con un comentario que lo explicaba:

// The package types `themes` values as plain strings, so cast to satisfy astro check.
shikiConfig: /** @type {Partial<import('astro').ShikiConfig>} */ (shikiConfig),

El paquete compartido tipa themes.light/themes.dark como string genérico, no como la unión literal de nombres de tema que espera el propio tipo de Astro. No era un problema de versión para nada, solo un tipo que nunca estuvo del todo bien, aceptado en silencio porque acá nunca se había corrido astro check antes. Copié el mismo cast. Error resuelto. Igual dejé la subida de versión de Astro porque reduce el desfase entre los dos sitios y nada se rompió, pero nunca fue el arreglo real.

Verificar el gate significaba probarlo contra algo, y ahí es donde se puso interesante

No quería solo configurar el nuevo comando de build en el dashboard de Cloudflare y asumir que funcionaba. Quería verlo correr de verdad. La API de Workers Builds tiene un endpoint para disparar manualmente un build contra cualquier rama que se nombre, así que apunté uno a mi rama de feature para revisar el nuevo comando con gate antes de fusionar nada a main.

Volvió en verde. Bien. Excepto que el log tenía una línea más de la que esperaba:

Executing user deploy command:  npx wrangler deploy --config wrangler.toml dist/server/entry.mjs
✨ Success! Uploaded 7 files (23 already uploaded) (0.91 sec)
Success: Deploy command completed

El paso de deploy del disparador no verifica qué rama se le indicó construir. Simplemente corre, sin condición, después de cualquier build exitoso. Apuntar el disparador de producción a una rama de feature para “probar el comando de build” también desplegó esa rama de feature directo a producción, antes de cualquier fusión, antes de cualquier revisión. wrangler deployments list lo confirmó: un deploy nuevo, con marca de tiempo de mi llamada manual a la API, no de un git push.

El daño real acá fue casi cero. La rama solo tocaba tooling y formato, nada que un visitante fuera a notar, y ya había revisado en diff cada cambio de formato antes de ese punto. Pero el mecanismo es la parte que vale la pena recordar: una acción de “solo voy a probar esto” llegó a producción sin ningún paso de confirmación en el medio, porque asumí que un build disparado manualmente era más seguro de lo que era. No era un dry run. No existe un dry run.

Una vez que lo supe, el arreglo fue solo poner a main al día con lo que ya estaba en vivo: fusionar la rama, verificar que el build real disparado por push (no uno manual) también quedara en verde, listo. Y así fue, con hash de commit y todo, ligado a un git push real, con los gates pasando de verdad esta vez.

Qué haría diferente

Probar los cambios de comando de build fusionando primero, no apuntando el disparador de deploy de producción a código sin fusionar y esperando que el parámetro “branch” signifique lo que suena que significa. Que una API permita especificar un destino no es lo mismo que esa API respete un límite. Preguntar qué pasa cuando algo tiene éxito antes de descubrirlo viéndolo pasar.

Lecturas relacionadas