GitHub Actions podría ser mucho mejor
(blog.yossarian.net)- GitHub Actions es útil en productividad y funcionalidad, pero al escribir workflows reales, la demora para depurar, los errores de seguridad, la falta de tipos y la escasez de acciones oficiales terminan causando pérdidas de tiempo repetitivas
- Incluso para cambios pequeños hay que repetir
git add; git commit; git pushy revisar logs en el navegador; hubo un caso en el que hasta un workflow simple de release requirió 4 commits y releases fallidos - La expansión
${{... }},pull_request_target, los permisos predeterminados demasiado amplios deGITHUB_TOKENy las referencias SHA de forks son todos puntos de seguridad propensos a errores; en particular, un SHA de fork puede parecer un commit del repositorio esperado - Las entradas de acciones no tienen validación
type:y ademásworkflow_callyworkflow_dispatchdifieren en su soporte, por lo que en vez de entradas de arreglo u objeto hay que procesar manualmente cadenas tipo CSV o entradas aplanadas - La validación al hacer push, chequeos de seguridad en tiempo de ejecución, reducir los permisos predeterminados del token en repositorios personales, verificaciones de tipos más estrictas y más acciones oficiales o semioficiales podrían mejorar la confiabilidad de los workflows
GitHub Actions: útil, pero tropieza una y otra vez
- GitHub Actions es una herramienta que desde 2019 ha ayudado mucho en productividad y sensación de estabilidad, al punto de usarse a diario tanto en proyectos profesionales como personales
- También ha seguido ampliando sus funciones de forma constante
- Aun así, en el desarrollo real también puede convertirse en una fuente de gran frustración y pérdida de tiempo
Depurar es demasiado pesado incluso para errores pequeños
- Hubo un caso en el que, al configurar un workflow de release, se necesitaron 4 commits y 4 releases fallidos solo para atrapar errores pequeños
- No usar
${{ ... }}donde hacía falta - Omitir una relación
needs:
- No usar
- El ciclo actual de depuración tiene demasiados pasos incluso para confirmar un solo error simple
- Hay que cambiar del entorno de desarrollo al navegador
- Hay que encontrar la pestaña correcta
- Hay que entrar haciendo clic en el resumen de Actions y en la pantalla de estado
- Hay que refrescar logs de consola con buffering para encontrar el siguiente error
- Incluso un cambio pequeño puede tardar más de 30 segundos en recorrer todo el proceso
-
Cómo podría mejorar
- Debería ofrecer un shell de depuración interactivo o, al menos, permitir volver a ejecutar un workflow con cambios pequeños sin
git add; git commit; git push - Debería poder rechazarse, desde la configuración del repositorio, un workflow claramente incorrecto al momento del push
- Sintaxis que no puede ejecutarse
- Referencias a jobs o steps que no existen
- Casos en los que un workflow simplemente no corre por YAML inválido
- Debería ofrecer un shell de depuración interactivo o, al menos, permitir volver a ejecutar un workflow con cambios pequeños sin
Los errores de seguridad ocurren con demasiada facilidad
- En GitHub Actions es fácil que un workflow escrito por el usuario termine siendo vulnerable sin intención
- Al usar la expansión
${{ ... }}dentro de un shell u otro contexto de ejecución, si el valor expandido proviene de una entrada controlada por el usuario, puede llevar a inyección de código malicioso- En el ejemplo, se inyecta código mediante
inputs.froby podría filtrarse$MY_IMPORTANT_SECRET
- En el ejemplo, se inyecta código mediante
pull_request_targetes muy difícil de usar de forma segura en workflows que no sean triviales- Su caso de uso previsto parece ser algo muy acotado, como etiquetar PRs de forks o dejar comentarios
- Pero en la práctica queda expuesto como un gran foot-gun
- Los permisos a nivel workflow y job también tienden a configurarse en exceso
- Los permisos predeterminados de acceso del
GITHUB_TOKENnormal son demasiado amplios - En repositorios de cuentas personales, con frecuencia se olvida reducir los permisos predeterminados del token
- Como no siempre se sabe exactamente qué alcance se necesita, se termina concediendo más de lo debido
- Los permisos predeterminados de acceso del
- El modelo de permisos de GitHub también suma confusión al forzarlo todo a un solo eje
read/write/noneid-token: writepermite leer el token OpenID Connect del workflow- Algunas operaciones
GETde security advisory requieren permisowrite, mientras que otras solo requierenread
Las acciones fijadas por SHA pueden confundirse con commits de forks
- Una referencia que se ve como
actions/checkout@c7d749a2d57b4b375d1ebcd17cfbfb60c676f18epuede en realidad no ser un commit del repositorioactions/checkout - Ese SHA puede ser un commit que está en un fork dentro de la red de
actions/checkout, y por el uso de alternates de GitHub puede parecer que pertenece al repositorio padre - La publicación relacionada de Chainguard divide el problema en tres partes
- Una referencia SHA de un fork y una del repositorio objetivo no se distinguen visualmente
- El endpoint
/repos/{user}/{repo}/commits/{ref}de la API REST de GitHub devuelve un JSON que parece referirse solo a{user}/{repo}, aunque{ref}exista únicamente en un fork - Si no se puede distinguir entre SHA de fork y no fork, es posible eludir las restricciones de fuentes confiables en GitHub Actions
- La respuesta de GitHub hasta ahora se ha limitado a añadir texto en la documentación
-
Cómo podría mejorar
- Hace falta un modo de seguridad paranoica para workflows que rechace al hacer push los workflows claramente inseguros
- Igual que con la instrumentación en tiempo de ejecución de herramientas como AddressSanitizer, debería poder fallar un workflow cuando detecte patrones peligrosos de seguridad
- Ej.: fallar si en
pull_request_targetse usaactions/checkoutcon una ref distinta a la del repositorio objetivo
- Ej.: fallar si en
- También podría evaluarse deprecar o eliminar
pull_request_target - Incluso en repositorios personales debería poder configurarse un alcance predeterminado más restringido de
GITHUB_TOKEN, como ya ocurre en organizaciones y enterprise - Una acción fijada por SHA que no exista en el repositorio referenciado y solo exista en un fork debería rechazarse por defecto
El sistema de tipos carece de consistencia y expresividad
- Una GitHub Action personalizada puede definir entradas bajo
inputs:, pero las entradas de acciones no tienen aplicación de tipos - Declaraciones como
type: numberno funcionan en entradas de acciones - Incluso dentro de GitHub Actions, el soporte de tipos no es consistente según el lugar
workflow_call: soportaboolean,number,stringworkflow_dispatch: soportaboolean,choice,number,string- action inputs: sin soporte de tipos
- Incluso las entradas con tipos no soportan estructuras de datos compuestas como arreglos u objetos
- No se puede una entrada de arreglo como
paths: [foo, bar, baz] - No se puede una entrada de objeto con jerarquía bajo
headers:
- No se puede una entrada de arreglo como
- Como resultado, quienes escriben acciones terminan pidiendo formatos improvisados
- Implementar su propio parseo tipo CSV, como
paths: foo,bar,baz - Aplanar estructuras naturales con cosas como
header-foo,header-baz
- Implementar su propio parseo tipo CSV, como
- Ese enfoque no es bueno ni para mantenimiento ni para seguridad
- Hay que administrar manualmente un único espacio plano de nombres para entradas
- Se termina creando un pseudo-lenguaje arbitrario para entradas complejas
-
Cómo podría mejorar
- Quienes escriben actions y workflows deberían poder usar
type:en todas partes choicetampoco debería limitarse solo aworkflow_dispatch, sino estar disponible en todos lados- Cuando sea posible inferir tipos estáticamente, los cambios de workflow con tipos incorrectos deberían rechazarse al hacer push
- Hacen falta
type: objectytype: array - Aunque no sea perfecto porque los tipos internos pueden ser heterogéneos, sería mejor que la situación actual
- Si hace falta, podría pasarse como una cadena serializada en JSON
- GitHub Actions ya tiene la función
fromJSON(...)
- Quienes escriben actions y workflows deberían poder usar
Las acciones oficiales están directamente ligadas a la confianza en la plataforma
- El ecosistema de terceros de GitHub Actions tiene muchas acciones de alta calidad
- Debajo de eso están las acciones oficiales mantenidas por GitHub
- Trabajo central con
git:actions/checkout - Tareas de GitHub y gestión de repositorios:
actions/{upload,download}-artifact,actions/cache,actions/stale - Configuración esencial:
actions/setup-python,actions/setup-node
- Trabajo central con
- Estas acciones tienen mucha importancia y utilidad general, así que una implementación oficial y bien mantenida aporta mucho valor
- Pero la cantidad de acciones oficiales es pequeña, y a veces ni siquiera se ofrecen como acción tareas que conectan directamente funciones de GitHub
- Ej.: una acción para agregar programáticamente un pull request a la merge queue
- GitHub CLI tiene
gh pr merge, pero no está expuesto como acción
-
Casos de acciones oficiales descontinuadas
actions/create-release: sin mantenimiento desde marzo de 2021- Se recomienda a los usuarios migrar a workflows mantenidos por la comunidad
- Un ejemplo mencionado es
softprops/action-gh-release actions/upload-release-asset: marcado como sin mantenimiento en la misma época queactions/create-releaseactions/setup-ruby: sin mantenimiento desde febrero de 2021- Se recomienda migrar a
ruby/setup-ruby - La transición en sí fue relativamente poco dolorosa, pero la impresión de que componentes clave pueden abandonarse perjudica la confianza en la plataforma
- Si faltan acciones oficiales de alta calidad, los usuarios seguirán manipulando directamente la superficie de la API de GitHub y cometiendo errores de seguridad
-
Cómo mejorar el ecosistema
- Valdría la pena ofrecer como acciones oficiales tareas que conectan directamente distintas partes de la infraestructura de GitHub y que hoy requieren llamadas manuales a la API REST o ejecutar
gh - También podría colaborarse con grandes acciones de terceros para crear una organización semioficial como
community-actions- Acciones revisadas por GitHub
- Cumplimiento de buenas prácticas de seguridad en repositorios
- Actualizaciones de versión semántica
- Una ruta de confianza clara para acciones importantes del ecosistema
- Valdría la pena ofrecer como acciones oficiales tareas que conectan directamente distintas partes de la infraestructura de GitHub y que hoy requieren llamadas manuales a la API REST o ejecutar
Herramientas actuales y hoja de ruta de GitHub
- Varias personas recomiendan la herramienta de emulación local de GitHub Actions
nektos/act- Sirve para iterar y depurar rápidamente workflows simples
- Pero se considera una herramienta con pérdidas porque las imágenes y eventos no siempre coinciden por completo con el entorno real de GitHub
rhysd/actionlintse usa para lint local y lint de seguridad- Su limitación es que solo puede hacer lint de workflows, no de actions
- La extensión GitHub Actions para VS Code ofrece lint dentro del editor e integración con workflows del repositorio
- Se basa en el JSON schema público
- Muestra workflows en ejecución, autocompletado de variables y más
- Pero no detecta problemas como typos en secrets o en nombres de outputs generados dinámicamente, así que la depuración con add-commit-push sigue siendo necesaria
- Julian Dunn, responsable de gestión del proyecto GitHub Actions, compartió algunas funciones en curso y prioridades
- La depuración interactiva está en la hoja de ruta pública de GitHub Actions
- También está en la hoja de ruta pública el trabajo para pasar del pinning de workflows por tag/SHA a una forma más inmutable
- GitHub considera prioritaria la salud y calidad del ecosistema de acciones oficiales, y mantiene una estrategia de conservar pequeño el número de acciones oficiales para poder dar mantenimiento y atención suficientes a cada una
1 comentarios
Opiniones de Hacker News
Hay dos formas de usar los workflows de GitHub Actions. 1) “Programar” con GitHub Actions, incluso conectando herramientas del marketplace para cosas como enviar correos, haciendo que el YAML crezca a 500~1000 líneas y se llene de condicionales hasta volverse difícil de entender.
2) Usar GitHub Actions solo como “configuración” y preguntarse constantemente: “¿puedo empujar esta complejidad del YAML a un script?”. Si incluso el envío de correos se mete en un script, el workflow queda en unas 50~60 líneas, se puede depurar el script en local y casi desaparece el ciclo tonto de push-debug-commit. Cada vez que llego a un equipo nuevo digo que 1 es el camino de la locura y que 2 es lo razonable, pero más o menos la mitad sigue eligiendo 1. La falta de herramientas de depuración y el vendor lock-in también se vuelven mucho menos problemáticos si se elige 2.
Dicho eso, muchas veces no alcanza con mover todo a un lenguaje de programación propiamente dicho. Hay casos en los que necesitas usar funciones específicas del proveedor en GHA, marcar dependencias entre tareas o llamar a una REST API que ya está bien abstraída como una action. Puedes reimplementarlo en el lenguaje que quieras, pero eso no elimina la dependencia del proveedor y sigue siendo frágil. Al final, la propuesta de valor del vendor lock-in de GHA es muy fuerte, así que para convencer a la gente de ir por la opción 2 hace falta una ventaja más poderosa.
Esto aplica no solo a GHA, sino a cualquier servidor de build. El servidor de build debería ser un ejecutor de scripts que agrega historial, gestión de artefactos y permisos/auditoría; el proceso real de build debería delegarse al repositorio que se está construyendo.
Como referencia, existe nektos/act, que intenta reproducir el comportamiento de GHA en local: https://github.com/nektos/act
Build/test/firma/despliegue, etc., deberían escribirse como scripts lo más portables posible, y esos scripts siempre deberían funcionar en local. Veo a GitHub solo como el encargado de preparar automáticamente el entorno para ejecutar esos scripts y luego ejecutarlos.
El ciclo de git commit, push, wait es una experiencia de usuario horrible. Los usuarios merecen pipelines portables que se ejecuten en cualquier lugar, incluida su máquina local. Act resuelve este problema en cierta medida, pero por lo general no representa fielmente el entorno real.
Hay muchos pipelines que no pueden ejecutarse en local igual que en producción, pero en etapas de desarrollo menos críticas no hay razón para no capturar esos workflows y ejecutarlos localmente. Garden ofrece pipelines portables y agrega caché sobre todo el grafo de dependencias. Algunos clientes redujeron los tiempos de ejecución en más de 80%, y los desarrolladores ven de inmediato si las pruebas pasan o fallan sin tener que hacer push a git primero. Es open source: https://github.com/nektos/act, https://docs.garden.io
make release.Coincido totalmente con el dolor de depurar ejecuciones de GH Actions. Las únicas herramientas que uno tiene son activar el debug y volver a ejecutar. Tengo demasiados commits basura hechos para arreglar o depurar pipelines, y la mayoría son básicamente tirar cosas a ver si funcionan
Incluso cosas muy básicas, como lógica reutilizable, son innecesariamente complejas o están mal documentadas. Una vez que lo entendí fue bastante fácil, pero la documentación de GitHub era pésima. Parecía que, para obtener reutilización, había que hacer pública una action o ponerla en otro repositorio, pero en realidad se puede crear un nuevo archivo YAML con la lógica reutilizable. Eso sí, tiene que estar en la raíz de la carpeta workflows para que funcione. Trabajar con GH Actions es realmente doloroso, pero una vez que funciona es muy cómodo. Sería bueno tener algo como un runner local para probar las actions antes de hacer commit y push
Tampoco es un problema exclusivo de GitHub. Otras grandes plataformas de CI tampoco son mucho mejores en flujos de trabajo e integraciones. Hoy intento scriptar todo lo más posible
GitHub Actions es un sistema de CI/CD pésimo. No permite ejecutar pasos en paralelo en la misma VM, y los trabajos basados en contenedores son tratados como ciudadanos de segunda
Por el primer problema, no se puede paralelizar la configuración de credenciales locales o dependencias del entorno. Ver que
google-github-actions/setup-gcloudtarda más de un minuto da bronca. Por el segundo problema, es muy difícil configurar el entorno de CI con un Dockerfile dentro del repositorio, hacer que CI reconstruya la imagen cuando cambia su contenido y deje en espera los workflows que dependen de esa imagen, y que, si el contenido no cambió, no la reconstruya y ejecute de inmediato con las dependencias ya instaladas. En GitHub Actions uno termina intentando rellenar la caché cada vez. La caché tiene un límite de 5 GB, no se puede aumentar ni pagando, y se expulsa por FIFO, así que si superas los 5 GB es prácticamente como si no existiera. Extraño mucho Concourse. Permitía trabajos en paralelo, triggers de pipeline personalizados, entrar por SSH al entorno de CI para depurar, ejecutar trabajos puntuales sin pipeline y pasar contenidos de directorios entre trabajos sin crear artefactos. GitHub Actions se parece más a un CI/CD de juguete que se volvió popular por la facilidad de entrada de subir un archivo a.github/workflowsy empezar a usarlo. Sirve para enganchar a la gente, pero cuando vas más allá de las funciones iniciales, no es lo bastante potente como para llamarlo “lo mejor”El debug por SSH y los trabajos puntuales eran realmente un sueño
No entiendo cómo todavía no se resolvió que GitHub no pueda distinguir entre referencias SHA de forks y no forks, lo que permite que un fork eluda la configuración de seguridad de GitHub Actions
Si los controles de seguridad se pueden eludir fácilmente, es un problema de seguridad grave. Es cierto que hay que convencer a alguien de que use tu SHA, pero la ingeniería social suele estar del lado fácil
Sinceramente, es vergonzoso lo malo que es. También es raro que una falla de build no relacionada solo le notifique al maintainer más reciente que hizo merge por casualidad. Podría no enterarme de que mi update/build lleva una semana fallando hasta que un maintainer recién agregado me avise por Matrix
La cache action está visiblemente rota a simple vista y parece no tener mantenedor. Si la UI se rompe o la pestaña se descarga, ni siquiera se pueden seguir en vivo los logs de build de un paso. Prácticamente no hay abstracciones para hacer que un workflow sea portable entre nodos worker. Las imágenes base son casi un desastre. Al principio solo me molestaba que fueran demasiado pesadas, pero cuanto más escarbaba, más pensaba: “esto quedó a cargo de alguien de Microsoft que no conoce Linux”. No hay ningún esfuerzo por ofrecer imágenes más livianas para quienes usan Nix o Docker. Estoy saliendo de GitHub, y la razón principal es que Actions me hizo reaccionar. La razón por la que no programo en YAML es que considero que YAML es una deshonra creada por dinero de VC volcado sobre un ecosistema que no conoce tecnologías modernas y realmente útiles
Recomiendo mucho https://pre-commit.ci. No tengo relación con el proyecto; solo soy un usuario satisfecho.
La idea es escribir hooks que revisen el código antes del commit y lo corrijan si es posible, o usar hooks existentes, y que después del push el CI vuelva a revisar y, si hace falta, incluso haga automáticamente un commit con las correcciones. El caché automático es tan bueno que resulta increíblemente rápido, y como se usa la misma configuración en la máquina local de desarrollo y en CI, se reducen mucho los problemas de depuración. Es gratis para open source. No hace cosas como releases automáticos; solo revisa, pero esa revisión la hace muy bien.
Para revisar el código fuente en sí, también se pueden correr análisis estáticos con
skip_install = True. Basta con ejecutarlo en un contenedor que tenga tox instalado como herramienta global y todas las versiones de Python necesarias. Por ejemplo, existe una imagen como https://github.com/georgek/docker-python-multiversion. No tiene mantenimiento, pero es fácil de actualizar. Alcanza con un boilerplate del estilo[tox] envlist = py{310,311},[testenv], y ponerpre-commit run --all-files --show-diff-on-failureen[testenv:check].Después de sufrir demasiadas veces el ciclo
git commit; git push; repeat, descubrí https://github.com/mxschmitt/action-tmate. Abre un acceso por shell entre pasos, así que no resuelve todos los problemas, pero a veces definitivamente reduce el dolor.Personalmente, me gustaría que se pudieran adjuntar reportes HTML a una ejecución de action sin tener que usar algo como
actions/upload-artifact.Especialmente en builds de prueba, muchas veces uno quiere ver rápidamente el reporte HTML generado. Las formas que conozco hoy son upload-artifact o GH Pages, pero GH Pages no funciona muy bien cuando hay múltiples salidas de reportes en el mismo repositorio o cuando se quiere ver rápidamente un reporte anterior en vez del más reciente. Me gustaría una action simple tipo
attach-report, que agregue un enlace al reporte HTML en el resumen del job y que, al hacer clic, renderice el HTML. Otros sistemas de automatización CI/CD soportan de forma mucho más completa la captura y consulta de reportes HTML de manera predeterminada.$GITHUB_STEP_SUMMARYsin pasar por la subida de artefactos ni cosas similares.Veo a GH Actions, en la práctica, más bien como un rebranding de “Azure Pipelines” de Microsoft. Habiendo usado todas las formas anteriores de pipelines de build y release de TFS/VSTS/AzDO, este equipo no hace bien este tipo de cosas.
Azure Pipelines llegó a ser más o menos usable solo porque todos los enfoques que intentaron antes fracasaron literalmente. También hubo un proyecto para ejecutar pipelines localmente y así poder hacer el ciclo edit-run-debug en un entorno personal sin commits, pero obviamente fue cancelado: https://github.com/microsoft/azure-pipelines-agent/pull/2687/files#diff-570b52bcb927a5365c22d17a21e8e19e1ba3427c7f3ec16ea64b7b3d14e4ee20. Aun así, hay herramientas que mejoran la calidad de vida. Por ejemplo, una extensión de VS Code que entiende la sintaxis: https://marketplace.visualstudio.com/items?itemName=ms-azure-devops.azure-pipelines. Dicho de forma un poco polémica: si se hubiera usado XML en vez de YAML, con solo una declaración xmlns al inicio se podría haber obtenido validación en la mayoría de los editores de código decentes sin intervención adicional del usuario. XML es horrible, pero al tirarlo todo también perdimos muchas funciones útiles.