Hasta la semana pasada, cada uno de mis repos públicos llevaba su propia copia del CI, de la configuración de Dependabot, de la auditoría de seguridad y del workflow de releases. Cuatro repos, cuatro copias, y se habían separado como se separan siempre las copias: uno fijaba las actions por tag y otro por commit, uno auditaba cada semana y otro nunca, y en uno de ellos un push nuevo podía cancelar una release a mitad de publicar la imagen.
Así que el 25 de septiembre lo junté todo en un sitio: KN990x/.github. Pasó de la v1.0 a la v1.2 en dos días, y PullPilot, vuzon y EasyPages ya publican con él. Glassys, el más nuevo, también usa el chequeo de CI compartido.
Qué vive ahí ahora
validate-config es el primer paso de cada CI: actionlint (con shellcheck) sobre los workflows y una validación del esquema de dependabot.yml. Lo segundo parece exagerado hasta que recuerdas que un dependabot.yml inválido no hace fallar nada. Dependabot simplemente deja de funcionar en silencio, y te enteras meses después.
majors-report es una auditoría de seguridad mensual que reescribe un único issue de «majors pendientes» por repo: los majors que Dependabot no va a abrir por su cuenta, los paquetes que se retienen a propósito y cada runtime frente a su fin de soporte. Un solo issue, siempre al día, en vez de un montón de PRs que acabaría cerrando sin leer.
ghcr-publish es el pipeline de releases. Comprueba que el tag coincide con la versión, que el CI pasó de verdad para ese commit exacto, prueba la imagen en amd64 y en arm64, y solo entonces publica una imagen multiarquitectura con procedencia y SBOM. Una vez al mes corre en seco, sin publicar nada: si algo se pudre en el pipeline, me entero antes de una release y no en medio de una.
dependabot-merge deja que las PRs rutinarias de Dependabot se mergeen solas cuando el CI está en verde y hay una revisión aprobada sobre el último commit. Un major, una PR que toque algo más que ficheros de dependencias o una con un commit añadido encima nunca entran.
fleet-check abre cada mes un issue de «estado de la flota» con cómo está cada repo público: CI, auditoría, release en seco y qué versión de las piezas compartidas fija cada uno.
Cada repo fija una release de este repo por SHA de commit y la mueve con su propia PR de Dependabot, así que un cambio en las piezas compartidas pasa por el CI de cada repo antes de que ese repo lo use. Y el repo compartido se prueba a sí mismo: el informe de majors corre contra un proyecto de prueba desactualizado a propósito, y el pipeline de releases entero construye una imagen mínima para las dos arquitecturas en cada cambio.
Las reglas pequeñas que vinieron con él
Juntarlo todo me obligó a tomar algunas decisiones que llevaba tiempo aplazando:
- Todas las GitHub Actions van fijadas por SHA de commit, y todas las imágenes base de Docker por digest. Sus parches llegan ahora como PRs de Dependabot que puedo leer, no como cambios silenciosos bajo un tag que se mueve.
- Dependabot pasa de semanal a mensual, con las actualizaciones de seguridad en su propio grupo para que nunca esperen al lote.
- Los permisos de las releases van por job, y un run más nuevo ya no puede cancelar una publicación que está en marcha.
Menos ruido, y cuando algo aparece merece la pena mirarlo. De eso iba todo.
Y lo que salió justo antes
El pipeline llegó justo después de una ronda de fixes que salió el 3 de septiembre como PullPilot 2.2.5, vuzon 2.0.5 y EasyPages 2.1.4. El hilo común da un poco de vergüenza: sitios donde el panel decía que todo iba bien cuando no era así.
- PullPilot. Crear una programación podía reiniciar la siguiente ejecución de todas las demás, así que el cron de ese día sencillamente no pasaba. Un fallo al guardar el estado después de un despliegue correcto lo marcaba como fallido. Y la interfaz solo seguía las actualizaciones lanzadas desde la misma pestaña.
- vuzon. Un listado vacío o malformado de Cloudflare se tomaba como una página completa y vacía, así que borrar un destino podía saltarse alias que todavía lo usaban. Ahora falla en cerrado. También reforcé cómo se revocan las sesiones tras un cambio de contraseña.
- EasyPages. En Docker sobre HTTP plano, la cookie de sesión se marcaba como Secure, el navegador no la guardaba y el login no se mantenía. Las respuestas de Cloudflare con
success: falseparecían listas vacías, la búsqueda de cuentas se paraba en la primera página, y el compose recomendado apuntaba a un tag de imagen que nunca se publicó.
Si usas cualquiera de los tres, actualiza. Y si mantienes un puñado de repos pequeños, la idea merece la pena copiarla: un repo para las piezas compartidas, fijado por SHA en todas partes y probado contra fixtures. Da menos trabajo del que parece, y muchísimo menos que mantener cuatro copias sincronizadas a mano.
Comentarios