Dev Life

Plantillas privadas de GitHub Actions para tu organización

Tras muchos años usando Jenkins, decidimos migrar a GitHub Actions, pero nos encontramos con algunos obstáculos. Por eso, hemos ideado una solución alternativa que permite a los usuarios de GitHub sin planes Enterprise, pero con repositorios privados, actualizar sus plantillas de flujo de trabajo.
Imagen para Plantillas privadas de GitHub Actions para tu organización

GitHub Actions es la novedad en el mundo del CI/CD, pero está superando rápidamente a los servidores de automatización tradicionales de código abierto. Desde hace un tiempo, el equipo de desarrollo de Sinch Mailgun sabe que algunas de las herramientas y tecnologías con las que configuramos esto tienen una comunidad cada vez más pequeña y corren el riesgo de volverse demasiado específicas para poder mantenerlas. Por estos motivos, nos encontramos inmersos en una migración de nuestro CI/CD basado en Jenkins al CI/CD de GitHub Actions. Para que quede claro: a veces no está todo incluido, a menos que tengas el plan GitHub Enterprise como cliente…

Digo esto porque, hasta hace muy poco, las plantillas de flujo de trabajo en los repositorios privados no eran compatibles, por lo que tuvimos que buscar una alternativa. Por suerte, GitHub ha publicado hace poco una actualización que ya incluye asistencia para esta función en sus planes Enterprise. Sin embargo, para quienes utilizan los planes Free o Team de GitHub, el problema persiste. No sabemos si GitHub lanzará esta actualización para otros planes ni cuándo lo hará, así que, de momento, aquí te contamos nuestra experiencia y cómo lo solucionamos.

El reto de reutilizar los flujos de trabajo

Mi experiencia previa con GitLab CI me acortó la curva de aprendizaje con GitHub Actions, pero al final los pequeños detalles resultaron ser diferentes. Por ejemplo, incluir una serie de pasos de un proyecto en otro es muy distinto. Tanto GitHub Actions como GitLab CI permiten reutilizar pasos de un repositorio en otro, pero los planes Free y Team de GitHub Actions exigen que los pasos que quieras incluir sean de acceso público. En GitLab CI, se puede usar la autenticación para importar tareas, etapas, variables, etc. de un repositorio privado utilizando la directiva includes .

En el momento de redactar este artículo, GitHub Actions tenía incidencias (bueno, dos) para hacer el seguimiento de esta esperada función en los repositorios privados. Estas incidencias llevaban abiertas casi 18 meses y hace poco que se ha incorporado la solución para los planes Enterprise. No tengo claro cuándo (o si) esta función estará disponible para todo el mundo, pero me parece que es una parte fundamental para habilitar la reutilización privada de flujos de trabajo.

Sincronización de plantillas de flujo de trabajo 

Leí un sinfín de publicaciones de Stack Overflow e indagué en los hilos de comentarios de GitHub Issues, pero no encontré ninguna buena alternativa a la imposibilidad de incluir flujos de trabajo de otros repositorios privados de la misma organización. 

Pensé que los submódulos de git podrían ser una posible solución. Al fin y al cabo, permiten que un repositorio incluya a otro. ¿Podríamos incluir nuestras plantillas de flujo de trabajo como submódulo en nuestro repositorio y utilizar la gestión de submódulos de git para mantener las plantillas actualizadas? Sí, podríamos, pero no si queríamos tener plantillas que no fueran relevantes para ciertos repositorios en el repositorio de plantillas. Git trata un repositorio como la unidad más pequeña en la gestión de submódulos, así que nuestra solución debía pasar por tener un repositorio solo con los flujos de trabajo relevantes o por evitar los submódulos por completo.

Optamos por no usar submódulos porque queremos que un único repositorio de flujos de trabajo contenga plantillas de Go y Python (entre otras), pero he sacado en claro información muy valiosa de toda esta reflexión. Algunos hay quien utiliza enlaces simbólicos para incluir partes de un repositorio en otro en lugar de submódulos.

Así que intentamos vincular nuestra plantilla al flujo de trabajo de nuestra aplicación. ln $DIR/workflows/go/build_and_push.yml $DIR/my-app/.github/workflows/build_and_push.yml funciona para mantener nuestra plantilla de flujo de trabajo en sincronización con nuestra aplicación… hasta que ejecutamos un comando git que modifica la plantilla (git cambia los inodos de los archivos que modifica). Dado que queremos poder ejecutar git pull para actualizar las plantillas, tendríamos que buscar algo que pudiera aplicar automáticamente los cambios del repositorio de plantillas al archivo de una aplicación.

Plantillas de GitHub Actions: una alternativa aceptable para los planes que no son Enterprise

Al no contar con la capacidad nativa de incluir plantillas privadas en nuestros flujos de trabajo y querer tener un único repositorio en el que almacenarlas, optamos por esta sencilla solución que optimiza la facilidad de aplicar las actualizaciones de las plantillas. Hasta que los planes Free y Team de GitHub incluyan asistencia para plantillas privadas, puedes migrar, pero esta es una posible solución que hemos probado y verificado.

Incluimos muestras de hooks de git en nuestro repositorio de plantillas (por ejemplo, hooks/post-checkout.sample) porque los comandos de git que vayan a romper enlaces deben restablecerlos. Los hooks de post-checkout y post-merge tienen este aspecto:

                                

                                    #!/bin/sh
#
# An example hook script to keep your application workflow up-to-date with this
# template. For example, this updates my-app's workflow with the go template.
#
# To activate, this must have execute permission and be copied like so:
#
# $ cp hooks/post-checkout.sample .git/hooks/post-checkout
# $ chmod +x .git/hooks/post-checkout

# setup our project directory, assumes my-app is sibling
export PROJ_DIR=..
# remove existing workflow for clean install
rm $PROJ_DIR/my-app/.github/workflows/build_and_push.yml
# link app workflow to template
ln -v $PROJ_DIR/workflows/go/build_and_push.yml $PROJ_DIR/my-app/.github/workflows/build_and_push.yml
                                
                            

git merge y git checkout son operaciones habituales que rompen los enlaces, así que ejecutamos este script después de esas acciones.

Por tanto, para aplicar plantillas de flujo de trabajo en las aplicaciones, hay que hacer lo siguiente:

  1. Clonar nuestro repositorio workflows.
  2. Editar hooks/post-checkout.sample y hooks/post-merge.sample para que coincidan con tus aplicaciones.
  3. Copiarlos a sus respectivas ubicaciones .git/hooks y darles permisos de ejecución.
  4. Llevar a cabo un git checkout o git pull (git checkout master desde la rama principal funciona).

Por desgracia, esto no mantiene actualizado por sí solo el repositorio de plantillas y exige cierta interacción por parte de la persona responsable del proyecto para adoptar las actualizaciones; al fin y al cabo, los cambios deben aplicarse al remoto. Esto se puede hacer a través de los siguientes comandos:

                                

                                    # from the workflow templates repo
$ git pull
# from the project repo
$ git add .github/workflows/
$ git commit -m "upgrade workflow template"
$ git push
                                
                            

Una vez hecho esto, el flujo de trabajo se ejecutará con los cambios más recientes del repositorio de plantillas.

Simplifica la configuración mediante un gestor de paquetes basado en Git

Dado que lo optimizamos todo para que fuera más fácil aplicar las plantillas, tuvimos que hacer otras concesiones con nuestra solución alternativa. La más evidente es la configuración necesaria. Estamos pidiendo a los usuarios que organicen sus repositorios de una forma particular y copien y peguen los archivos a mano. Estos requisitos contribuyen a que exista cierta fragilidad, ya que el sistema puede fallar si se mueven los archivos o si los inodos cambian sin que nos demos cuenta.

Sin embargo, esta alternativa nos aporta un beneficio muy interesante. Hemos descubierto un conjunto de herramientas y scripts que pueden funcionar en un contexto genérico. Creamos un gestor de paquetes genérico basado en git que utiliza enlaces y hooks de git para actualizar archivos entre repositorios de git:

                                

                                    #!/bin/sh

# set paths/files (relative to workflows repo)
export TEMPLATE_DIR=.
export APP_DIR=../my-app
export DOCKER_TEMPL=go/Dockerfile
export WKFLW_TEMPL=go/build.yml

#remove files for clean install
rm $APP_DIR/Dockerfil $APP_DIR/.github/workflows/build.yml

# link app workflow to template
ln -v $TEMPLATE_DIR/$DOCKER_TEMPL $APP_DIR/Dockerfile
ln -v $TEMPLATE_DIR/$WKFLW_TEMPL $APP_DIR/.github/workflows/build.yml

                                
                            

Pros y contras de GitHub Actions

Entonces, ¿por qué GitHub Actions? Bueno, nuestro sistema actual exige modificaciones, solicitudes de incorporación de cambios (pull requests), implementaciones y mucho más para integrar un nuevo servicio. Además, como usuario de GitHub Enterprise, tenemos una gran asignación de minutos de ejecución en GitHub Actions, de modo que decidimos evaluarlo como alternativa. 

Ahora que hemos pasado algún tiempo con GitHub Actions, estas son mis conclusiones:

Pros de GitHub Actions Contras de GitHub Actions
Que los runners estén alojados significa que no tenemos que preocuparnos por su mantenimiento. Cobra por minutos de ejecución, por lo que desincentiva la creación de pruebas de larga duración en los runners alojados en GitHub.
Ofrece algunas ventajas de seguridad (aunque acaban de tener una brecha). Todavía le faltan algunas características que ofrece Jenkins.
Tiene una sintaxis de CI/CD más común definida por YAML. En Actions, GitHub utiliza unas metáforas y un lenguaje similares a los de la competencia, pero las metáforas no siempre coinciden. Esto dificulta un poco la migración y el aprendizaje.
Es más fácil integrar tareas públicas o de código abierto (OSS). Trabajar con secretos puede ser peligroso y el equipo de desarrollo puede filtrar datos sin darse cuenta.
La integración con la interfaz de usuario de GitHub facilita la visualización de los resultados de CI.

Como puedes ver, no es oro todo lo que reluce. Pero pronosticamos que GitHub, con los infinitos recursos que le proporciona Microsoft, superará a Jenkins a largo plazo. 

Pasos siguientes

Aunque nuestra alternativa no es la más elegante, es una solución que funciona hasta que GitHub admita plantillas privadas en los planes que no son Enterprise. Si esto sucede, podrás migrar las plantillas a un único repositorio. 

Dicho esto, contamos con una cuenta de GitHub Enterprise, pero todavía no tenemos la capacidad de activar la función de repositorios privados que ha añadido GitHub. Aún estamos intentando averiguar el origen de este problema. Hasta entonces, utilizaremos la alternativa y volveremos a comprobarlo en el futuro para ver si logramos acceder a esta nueva función. ¡No te pierdas las novedades!