https://d226lax1qjow5r.cloudfront.net/blog/blogposts/setting-up-ci-cd-with-github-actions/Blog_GitHub-Desktop_1200x600.png

Configuración de CI/CD con acciones de Github

Publicado el February 17, 2021

Tiempo de lectura: 8 minutos

La integración continua y el despliegue continuo son imprescindibles para las organizaciones que desean escalar y entregar software de alta calidad a gran velocidad. Este artículo te guía a través del proceso de uso de acciones de github para construir flujos de CI/CD.

¿Qué es CI/CD?

CI/CD es un proceso que conecta el desarrollo con el despliegue a través de un proceso de integración automatizado. La idea es permitir a los desarrolladores introducir cambios en el código como único paso necesario para desplegar esos cambios.

La canalización CI permite que el trabajo en equipo progrese sin problemas automatizando las normas y garantizando la calidad del software. Herramientas como los linters y las pruebas automatizadas proporcionan información que permite integrar los cambios en la rama principal. Estos cambios integrados acaban enviándose a los usuarios finales (producción).

El proceso de CD recibe el código probado y aprobado. Confirma que todos los artefactos necesarios se despliegan en el lugar correcto. Algunos ejemplos: desplegar una aplicación web en un servidor, publicar una biblioteca en el repositorio de un gestor de paquetes o publicar una aplicación móvil en la tienda de aplicaciones.

La automatización de estos procesos garantiza dos cosas importantes: el proceso se realiza con rapidez y es mucho menos propenso a errores.

¿Quiere saber cómo los equipos aceleran sus procesos de desarrollo, integración y despliegue? ¿Preparado para crear una nueva canalización de CI/CD? Póngase en marcha.

Flujo básico de CI/CD

  1. Empuje un cambio a una rama de características.

  2. Crear una Pull Request para este cambio

  3. El CI se pone en marcha y ejecuta lo siguiente:

  4. Lint, probar, construir

  5. Una vez que el CI finaliza, marca el PR como válido y comienza el proceso de Revisión del Código.

  6. Cuando se aprueba el PR, el código se fusiona en master

  7. Tras la fusión, se inicia el proceso de CD:

  8. Lint, Test, Bump version, Deploy

Construiremos un CI/CD para una aplicación sencilla, que puedes clonar aquí.

Añadir un flujo de trabajo de Github a su proyecto

Crea una carpeta .github/workflows. Vamos a añadir nuestras acciones github dentro de esta carpeta. Las acciones se establecen en archivos yaml en una estructura bastante sencilla que consta de tres partes:

nombre: el nombre del flujo de trabajo, en nuestro caso Test and Build

en: el disparador que inicia el flujo de trabajo cuando se cumplen las condiciones. En nuestro caso, queremos que el flujo de trabajo se ejecute cuando se cree y envíe un pull request. Veremos ejemplos de otros disparadores más adelante en el tutorial.

trabajos: los comandos reales que se ejecutan en el flujo. Pueden ser varios trabajos que se ejecutan en paralelo o trabajos que dependen unos de otros. En este ejemplo hay un trabajo llamado build-testque sólo se ejecuta en ubuntu-latest OS.

name: Test and Build
on:
 pull_request:
   branches:
     - main
jobs:
 build-test:
   runs-on: ubuntu-latest
   steps:
     - name: Checkout
       uses: actions/checkout@v2
       with:
         fetch-depth: 0
     - name: Setup NodeJS 14
       uses: actions/setup-node@v1
       with:
         node-version: 14
     - name: Install yarn
       run: npm install -g yarn
     - name: Install dependencies
       run: yarn install
     - name: Test
       run: yarn test

El primer paso comprueba el repositorio, utilizando una acción pre-hecha actions/checkout@v2 del mercado de acciones. Pasamos la variable fetch-depth: 0 usando la variable con que indica que el proceso de comprobación obtendrá el repositorio con todo su historial. Los siguientes pasos son para la instalación y ejecución de comandos bash.

Creación de un flujo de trabajo de integración continua

  1. Activar el flujo con una Pull Request

  2. Poner en marcha la máquina

  3. Instalar nodeJs en la máquina

  4. Consultar el repositorio

  5. Instalar dependencias

  6. Test, Lint, Build

  7. Mostrar los resultados en la página PR

Primeros pasos

  • Si aún no has clonado el repositorio, hazlo ahora y crea una rama "add-ci".

  • Cree un archivo .github/workflows/ci.yml y copia el contenido de este fichero al nuevo archivo.

  • Confirme los cambios y envíelos a la nueva rama. Abre un pull request desde la nueva rama a la rama principal.

Si miras el pull request en github, deberías ver las pruebas ejecutándose:

running tests

Verás que github ya conoce el nombre del flujo de trabajo, el nombre del trabajo y el disparador. Una vez completado, debería verse así:

all checks have passed

¿Puede detectar algún problema en el proceso?

Añadir reglas de fusión

El primer problema es que el Fusionar pull request ya estaba disponible durante el proceso CI, cuando sólo queremos que la fusión esté disponible si se han completado todos los flujos de trabajo requeridos.

Podemos arreglar esto en la configuración del repositorio yendo a: Configuración>Regiones>Añadir regla

branch settings

Aquí seleccionaremos Requerir que las comprobaciones de estado pasen antes de fusionar y comprobaremos todo lo que hay debajo. Verá todos los flujos de trabajo que se requieren para permitir la fusión - en nuestro caso sólo tenemos build-test.

require status checks

Para Patrón de nombre de rama inserte main y cree la regla. Y si vuelves a la página de pull request, verás que no se pueden fusionar pull requests antes de que pasen las pruebas, a no ser, claro, que tengas privilegios de administrador.

cannot merge yet

Fusión tras la revisión del código

Antes de seguir adelante, también tenemos que decidir cómo manejar el paso de Pull Requests. Hay tres enfoques principales:

  1. Permitir a cualquiera fusionar una vez superadas las pruebas

  2. Fusión automática una vez superadas las pruebas

  3. Exigir una revisión del código para fusionar

Optaremos por la primera opción, que es la más común, y que podemos cambiar editando la regla de bifurcación que acabamos de crear. Al principio de la lista marcamos Requerir revisiones de pull request antes de fusionar y pulsa guardar.

Se acabó la integración continua. ¡Ahora todos los nuevos Pull Requests serán probados y revisados antes de ser fusionados en el main! El siguiente paso es el despliegue.

Creación de un flujo de trabajo de despliegue continuo

El despliegue puede ser cualquier cosa, desde subir archivos estáticos a github, a publicar paquetes npm, a desplegar una malla completa de microservicios. Este es el proceso que seguiremos para nuestra aplicación una vez que el código se fusione con el repositorio principal:

  1. Instalar dependencias

  2. Prueba

  3. Versión Bump

  4. Construya

  5. Etiquetar el comunicado

  6. Publicar en npm

  7. Despliegue de la aplicación de demostración

Examinemos diferentes partes del archivo yaml que gestiona las acciones:

El desencadenante

Estas son las condiciones en las que se ejecutará nuestro código de despliegue. Se activará en un pull request que haya sido cerrado y fusionado.

name: Test, Build and Deploy

on:
 pull_request:
   types: [closed]

jobs:
 build-test-release:
   if: github.event.action == 'closed' && github.event.pull_request.merged == true
   runs-on: ubuntu-latest

Permisos de acciones Git

En el paso de comprobación, añadiremos una línea extra que no teníamos en el archivo yaml anterior:

steps:
  - name: Checkout
    uses: actions/checkout@v2
    with:
      fetch-depth: 0
      token: ${{ secrets.CI_REPOSITORY_ACCESS_TOKEN }}

Necesitamos establecer explícitamente el token para poder hacer cosas para las que el token por defecto, secrets.GITHUB_TOKENno tiene permisos. En nuestro caso, vamos a querer enviar un cambio a main sin una revisión de código, por lo que necesitamos un token con privilegios de administrador. Establecemos este token en Configuración -> Secretos.

Aumentar las versiones de los paquetes

Este paso difiere de un proyecto a otro. La idea es subir la versión del paquete que pretendes publicar en el repositorio de paquetes. Haremos esto repasando todas las librerías que han cambiado en esta rama, y subiremos su versión NPM:

- name: Raise version of affected libraries
  run: |
    LATEST_TAG=$(git tag -l "v*" --sort=-version:refname | head -n 1)
    LIBS=$(yarn nx affected:libs --base=$LATEST_TAG --head=HEAD --plain | awk 'NR > 2 && $1 != "Done" { print $1 }')
    for LIBRARY in $LIBS
    do
      cd ./libs/$LIBRARY
      npm version minor --no-git-tag-version --no-push
      echo "Bumping $LIBRARY"
      cd ..
      cd ..
    done
    npm version minor --no-git-tag-version --no-push

Tenga en cuenta que estamos utilizando NX, un marco de gestión monorepo, que ayuda a hacer construcciones y desarrollo mucho más rápido. Utilizamos la función affected que nos indica qué bibliotecas se han visto "afectadas" por el PR.

Construya

El proceso de compilación también utiliza la función affected también. Construye las bibliotecas modificadas que han cambiado desde la rama principal con preajustes de producción:

- name: Build components
  run: yarn nx affected:build --prod --with-deps --base=main

Etiquetar el comunicado

El etiquetado de liberación se realiza en dos pasos:

La primera, get-npm-versionutiliza una acción del mercado que extrae la versión del paquete principal. El segundo paso confirma los cambios realizados en el paso Raise version mencionado anteriormente, crea una etiqueta con la versión del nuevo package.json y envía el cambio.

¿Recuerdas el CI_REPOSITORY_ACCESS_TOKEN secreto? Aquí es donde entra en juego. Debido a que impedimos la fusión a maestro sin una revisión pull request, necesitamos un token de administrador para esta parte, lo que nos permitirá eludir la regla y empujar los cambios de forma automática.

- name: get-npm-version
  id: package-version
  uses: martinbeentjes/npm-get-version-action@master

- name: Tag the release
  run: |
    git fetch
    git config user.email "unicorn.ci@yonatankra.com"
    git config user.name "Unicorn CI"
    git add --all
    git commit -m "update versions to ${{ steps.package-version.outputs.current-version }}"
    git push

- name: Tag release
  run: |
    git tag -a v${{ steps.package-version.outputs.current-version }} -m "tag release v${{ steps.package-version.outputs.current-version }}"
    git push --follow-tags

Despliegue de la aplicación de demostración

Por último, creamos nuestra aplicación y la desplegamos:

- name: Build Demo
  run: yarn build:deploy

- name: Deploy 🚀
  uses: JamesIves/github-pages-deploy-action@3.7.1
  with:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    BRANCH: gh-pages # The branch the action should deploy to.
    FOLDER: dist/apps/unicorn-hunt # The folder the action should deploy.
    CLEAN: true # Automatically remove deleted files from the deploy branch

Puede encontrar el código yaml completo aquí

Optimización del flujo CI/CD

Uso de la caché en las acciones de github

Si has seguido a lo largo y creado el flujo de CI / CD, usted puede notar que está tomando un tiempo para ejecutarse. Una solución fácil para eso es utilizar la caché y ahorrar en tiempos de instalación de dependencias, algo que también podemos hacer con las acciones de github.

El almacenamiento en caché funciona comprobando la existencia de una coincidencia en la caché y, si se encuentra, omite el paso de instalación de dependencias. Este es el aspecto del código:

- name: Install yarn
  run: npm install -g yarn

- name: Get yarn cache directory path
  id: yarn-cache-dir-path
  run: echo "::set-output name=dir::$(yarn config get cacheFolder)"

- name: Cache yarn dependencies
  uses: actions/cache@v2
  id: yarn-cache
  with:
    path: |
      ${{ steps.yarn-cache-dir-path.outputs.dir }}
      **\node_modules
    key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
    restore-keys: |
      ${{ runner.os }}-yarn-

- name: Install dependencies
  if: steps.yarn-cache.outputs.cache-hit != 'true'
  run: yarn install

El primer paso instala yarn globalmente. El segundo obtiene la ruta de la carpeta de caché de yarn. A continuación, el paso de caché utiliza la acción de caché y pasa las rutas a la caché (node_modules y la carpeta de caché de yarn) y le da una clave de caché mediante el hash del archivo yarn.lock. De esta forma, si instaláramos una nueva dependencia o actualizáramos una dependencia, se produciría un cache miss. El paso final establece una condición en el paso install-dependencies utilizando el parámetro cache-hit establecida por la acción de caché.

Modularizar el proceso

Así que el CI/CD está funcionando. Está utilizando la caché. Antes mencionamos que también nos gustaría publicar nuestras dos bibliotecas en la carpeta libs en npm. Técnicamente podemos añadir otro paso a nuestro proceso de CD, pero eso podría hacerlo engorroso y difícil de mantener.

En su lugar, dividimos el proceso en flujos de trabajo separados que son activados por el proceso principal del CD. El CD levantaría entonces una versión, que activaría los otros procesos, causando el despliegue y la publicación en NPM.

Además de una mejor mantenibilidad, esto también permite una mejor gestión de errores. Porque supongamos que el versionado y la publicación han tenido éxito, pero el despliegue ha fallado. Esto nos permite ejecutar el despliegue de nuevo y depurar hasta que tenga éxito.

El código completo de la solución puede encontrarse aquí.

Resumen

En este artículo hemos construido un proceso CI/CD usando acciones de github.

Empezamos añadiendo el proceso CI que ejecuta las pruebas y dice a nuestros revisores de código que el Pull Request está listo para revisión. También aprendimos cómo bloquear la fusión en una rama sin pasar las pruebas y una revisión. Hemos visto un código CI simplificado, pero puedes añadir más comandos bash a la fase de pruebas, o incluso añadir más pasos como linting o prettier.

A continuación, creamos un proceso de CD que bate las versiones y despliega la demo. Aprendimos a almacenar en caché la instalación de dependencias para ahorrar tiempo y a modularizar el proceso de CD para tener más control y facilidad de mantenimiento.

Hay mucho más que se puede hacer con las acciones de github. Esperamos que este tutorial te haya ayudado a crear un proceso de CI/CD útil y significativo para tu equipo.

Compartir:

https://a.storyblok.com/f/270183/400x400/7bf76cb05c/yonatankra.png
Yonatan KraArquitecto de software de Vonage

Yonatan ha estado involucrado en algunos proyectos impresionantes en la academia y la industria - desde C / C ++ a través de Matlab a PHP y javascript. Fue director de tecnología en Webiks y arquitecto de software en WalkMe. Actualmente es arquitecto de software en Vonage e instructor de egghead.