Implementando un flujo de CI/CD con GitHub Actions (1/3)

En el post de hoy vamos a hablar sobre qué es GitHub Actions y cómo podemos utilizarlo para implementar un flujo de CI/CD de manera rápida y después como se puede personalizar para adaptarlo a nuestras necesidades y probar las funcionalidades que nos ofrece.
Esta entrada corresponde a la primera de tres partes donde daremos un repaso completo a GitHub Actions y a lo que nos puede ofrecer tanto a nivel teórico como práctico creando un repositorio y un sistema CI/CD completo desde cero.

¿Qué es GitHub Actions?

Es una plataforma de CI/CD que forma parte de GitHub que nos permite crear workflows de nuestros repositorios almacenados en esta plataforma de control de versiones. Esto nos permite mediante diferentes eventos (como crear una rama nueva o hacer push al repositorio a una determinada hora) que se desencadene un flujo de CI/CD que hayamos establecido.
¿Y cómo establecemos estos flujos? Pues creando ficheros yaml con la configuración que nos indica GitHub y guardandolos en nuestro repositorio dentro de la carpeta .github/workflows.

¿Por qué utilizar GitHub Actions?

Si tenemos nuestro código almacenado en GitHub y queremos empezar a automatizar nuestro flujo de CI/CD con un par de clicks podemos integrar una de las plantillas de workflows que ya tiene predefinidas GitHub sobre la que podemos empezar a trabajar. Además, podemos ejecutar nuestro flujo sobre diferentes versiones de Ubuntu, Windows Server y Mac OS soportadas sin ningún tipo de configuración adicional si utilizamos la versión cloud, aparte de poder ejecutar directamente también si lo necesitamos sobre contenedores con Docker.

Otro punto importante es que dispone de una cuota gratuita bastante generosa, pudiendo disponer de hasta 2000 minutos gratis al mes según la plataforma seleccionada y si el repositorio es público. En el caso de repositorios privados ofrece 400 minutos gratis, que pueden ser más que suficientes para proyectos de pequeña escala.
Aún así también dispone de planes de pago a nivel enterprise que nos pueden permitir alojar la plataforma de GitHub Actions en nuestros propios servidores y gestionarlo como necesitemos, a parte de otras características propias de la versión cloud como crear grupos de reviewers de pull request, gestión de organizaciones, permisos, etc.
GitHub Actions nos ofrece una cuota gratuita 2000 minutos gratis al mes para proyectos públicos y 400 minutos para proyectos privados.
Y sin duda creo que la utilidad estrella son los Actions, que son steps reutilizables ya definidos por la comunidad e incluso por las propias empresas que nos permiten en pocas líneas de un archivo yaml podamos realizar acciones como desplegar en Artifactory o en diferentes servicios de GCP, AWS, Azure, etc y que podemos encontrar de manera gratuita en el marketplace. También es posible definir nuestros propios Actions utilizando Docker, NodeJS o diferentes scripts.

Ejemplo de uso utilizando un repositorio de código propio:

Vamos a suponer que tenemos un frontal en Vue.js que queremos alojar en GitHub y automatizar nuestro flujo de CI/CD.

Con este código de ejemplo desarrollaremos un pipeline completo con sus fases de construcción, tests y despliegue.

Inicialización del proyecto en nuestro entorno local

Los requisitos antes de continuar son los siguientes:
  • Tener instalado Node.js la versión utilizada es la LTS 16.14.2 a la hora de redactar este artículo.
  • Tener instalado git.
  • Una cuenta creada en GitHub para poder subir nuestro repositorio y usar GitHub Actions.
  • Una cuenta en Firebase para poder tener un hosting gratuito donde alojar nuestra aplicación web.
Utilizando como referencia su página de Quick Start vamos a crear un proyecto desde cero abriendo una terminal y utilizando la cli, con npm lanzamos el siguiente comando:
				
					npm init vue@latest
				
			
Nos pedirá confirmación para instalar la librería, indicamos que sí y nos aparecerá ya el menú de configuración del proyecto, el cual configuraremos de la siguiente manera:
Salida de consola tras crear proyecto vue.js
Las opciones importantes a instalar son la de utilizar Cypress como runner para los tests unitarios y End2End y ESLint para configurar posteriormente nuestro flujo de CI.
Para finalizar y comprobar que nuestro proyecto está listo para utilizarlo lanzamos el siguiente comando para instalar las dependencias:
				
					npm i
				
			
Esto nos generará un fichero package-lock.json que necesitaremos mas adelante para instalar nuestras dependencias en el flujo de CI.
Ahora que ya tenemos creado nuestro proyecto en nuestro entorno local es hora de crear un repositorio en GitHub y subir nuestra primera versión.

Inicialización del proyecto en GitHub

A la hora de crear el repositorio la pantalla de creación quedará de esta manera:
Creación de proyecto en GitHub
Nos mostrará una pantalla con diferentes pasos para agregar nuestro proyecto, es importante que nos quedemos con la url que nos indica:
Por comodidad utilizo SSH ya que tenía una clave creada, pero se pueden utilizar igualmente la opción HTTPS indicada a la izquierda.
Ahora que lo tenemos  creado volvemos a nuestro proyecto y lanzamos los siguientes comandos reemplazando {URL_ANTERIOR} con la url indicada anteriormente desde nuestra consola para inicializar git en el proyecto y subir los cambios al repositorio:
				
					git init
git add -A
git commit -m 'Added my project'
git remote add URL_ANTERIOR
git push --set-upstream origin master

				
			
Con esto si volvemos a actualizar la página de GitHub de nuestro repositorio ya deberíamos tener nuestro proyecto subido:
Primer commit

Construir nuestro Workflow base de CI

Vamos a empezar a incluir nuestro flujo de CI, para ello nos vamos a nuestro proyecto en GitHub y vamos a la pestaña Actions donde podremos empezar eligiendo algunas plantillas predefinidas:
Pestaña Actions. Actions de ejemplo predefinidos.
Seleccionamos la plantilla Node.js para empezar y nos llevará a una pantalla para modificar el template inicial. Debemos modificar la parte que indica npm test por npm run test:unit:ci ya que si miramos nuestro fichero package.json que se generó automáticamente veremos que no existe el script npm test como tal, y debemos reemplazarlo por el existente de nuestro fichero:
Fichero package.json en la raíz del proyecto
Con esto nuestra versión inicial del fichero debería quedar de la siguiente manera:
				
					# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [12.x, 14.x, 16.x]
        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
    steps:
    - uses: actions/checkout@v3
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build --if-present
    - run: npm run test:unit:ci

				
			
Resumiendo: lo que va a realizar nuestro Workflow son las siguientes acciones:
  • Cada vez que se haga push a la rama master o si creamos una pull request a la rama master se lanzará nuestro flujo de CI, esto es lo que se indica en los campos on.push.branches y on.pull_request.branches.
  • Se va a lanzar en una máquina con Ubuntu, esto es lo indicado en el campo jobs.build.runs.on.
  • De manera simultánea se utiliza una matrix que va a lanzar el mismo flujo para la versión 12, 14 y 16 de Nodejs en paralelo para verificar que nuestra aplicación funciona entre ese rango de versiones establecidas. Esto se indica en el campo jobs.build.strategy.matrix.node_version, aunque node_version es el nombre de la variable que nosotros vamos a utilizar en el resto de steps, GitHub se encarga de que el valor de está variable tenga solo uno de los valores especificados para cada ejecución.
  • El primer paso que va a ejecutar nuestro Workflow es utilizar un Action ya definido por GitHub y que podemos encontrar en el marketplace para hacer un fetch y checkout del commit agregado. Esto se indica en el campo jobs.build.steps.uses con el valor actions/checkout@v3.
  • El segundo paso utiliza este otro Action para setear la versión de Nodejs que vamos a utilizar. Esto se indica en el campo jobs.build.steps.uses con el valor actions/setup-node@v3 y también se le indica un nombre para identificar este step con el campo  jobs.build.steps.name. Además, a este actions le estamos pasando dos variables para configurarlo:
    • jobs.build.steps.with.node_version: configura la versión de node que vamos a utilizar, y con la variable matrix.node-version le indicamos que en cada ejecución instale una de las versiones de NodeJS que hemos indicado arriba.
    • jobs.build.steps.with.cache: Indicamos el gestor del que queremos cachear las dependencias (en nuestro caso npm). Esto nos permite que futuras ejecuciones no tengan que descargar todas las dependencias del proyecto permitiendo agilizar la fase de instalación varios minutos.
  • El tercer paso lanza el comando npm ci para instalar las dependencias del proyecto
  • El cuarto paso lanza el comando npm run build –if-present para generar los ficheros necesarios para construir la aplicación y desplegarla a futuro en donde alojaremos nuestra aplicación.
  • El quinto y último paso lanza el comando npm run test:unit:ci que ejecuta los tests unitarios que tenemos definidos con Cypress y genera un reporte con el resultado.
Si ahora le damos al botón Start Commit -> Commit new file y deberíamos ver iniciarse nuestro flujo si volvemos a la pestaña Actions:
Ejecución del Workflow
Si seleccionamos el Workflow en ejecución nos llevará a una pantalla donde podremos ver que hay tres jobs lanzándose de manera simultánea, debido a que indicamos que se iba a lanzar en tres versiones de NodeJS diferentes:
Pestaña Actions con la ejecución paralela de 3 jobs
Si seleccionamos cualquiera de los 3 jobs, podremos ver el resultado en tiempo real del output que van generando nuestros comandos al irse ejecutando.

Conclusión

Con esto ya tenemos configurado un flujo de CI básico sin apenas haber tenido que bucear mucho en configuraciones ni muchos conocimientos del funcionamiento de GitHub Actions.
En resumen, hemos visto que es GitHub Actions, cuando y para qué podemos utilizarlo, hemos inicializado un proyecto con Vue y lo hemos subido a un repositorio de GitHub, y mediante la interfaz del mismo hemos configurado los ficheros que contienen nuestro flujo de CI.
Para los futuros pasos vamos a utilizar características más avanzadas para seguir personalizando nuestro flujo, de momento lo que tenemos hasta ahora lo podemos encontrar en la rama de este repositorio de GitHub, donde iremos subiendo también los cambios y actualizaciones que hagamos a futuro.
Compartir:

Artículos relacionados