Binary Coffee

Integraci贸n continua con Github actions (NodeJS)

github nodejs

A continuaci贸n les presento una manera sencilla de como configurar la integraci贸n y despliegue continuo del proyecto por medio de GitHub actions, luego de una larga reticencia por mi parte para finalmente probar esta tecnolog铆a.

example of github actions

No hace mucho fue que los probl茅, en un peque帽o proyecto de fin de semana, del cual habl茅 en el art铆culo Creando un jurado online (parte 1). Ah铆 fue donde vi el potencial y las cosas que se pod铆an hacer. Resulta, que al igual que en Gitlab se pueden utilizar de manera completamente gratuita y pueden probarlo en sus proyectos personales sin ning煤n problema.

Estructura b谩sica de la configuraci贸n

La configuraci贸n de los Action de GitHub puede ser muy sencilla de crear. Puede hacerse de manera manual creando el archivo en el proyecto o si es la primera vez, hacer uso de las plantillas iniciales que GitHub provee con una amplia gama de tecnolog铆as.

En mi caso, que ya cuento con proyectos utilizando la integraci贸n de GitHub relacionada a Nodejs, pues todo se resume a crear la carpeta .github en el proyecto y luego dentro otra carpeta con el nombre de workflow, quedando la ra铆z de esta manera: .github/workfow.

Dentro de la carpeta workflow se crea el archivo que describe la integraci贸n continua en formato yml. Una de las primeras cuestiones que hay que tener en cuenta antes de entrar en materia, es la estructura b谩sica de la configuraci贸n de nuestro GitHub action. A continuaci贸n muestro una configuraci贸n b谩sica y previamente explicar茅 como funciona:

# ci-cd.yml
name: CI/CD
on: [ push, pull_request ]
jobs:
  job1:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 12.x, 14.x ]
    steps:
      - name: Clone project from the repository
        uses: actions/checkout@v2
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install project dependencies
        run: npm ci

En el archivo anterior lo que se puede ver, es un pipeline que instala las dependencias del proyecto, primeramente con node12 y luego con node14.

Explicando detalladamente la configuraci贸n anterior, ser铆a lo siguiente:

  • name: CI/CD: Se define el name del action
  • on: [ push, pull_request ]: se definen las acciones que dar谩n inicio al actual pipeline, en este caso cuando se realice un push o un pull request en el proyecto.
  • jobs: Esto define el listado de jobs que se ejecutar谩n en el action, cada job, puede ser un conjunto de acciones que pueden estar relacionadas entre ellas.
  • runs-on: ubuntu-latest: En este punto definimos el contenedor de Docker en que se ejecutar谩 el presente job (job1).
  • strategy.matrix: node-version: [12.x, 14.x]: Esta configuraci贸n es muy interesante, dado que permite definir diferentes variables a partir de las cuales se ejecutan los steps del job, con la combinaci贸n de dichas variables. En el presente caso, se puede ver un ejemplo en que se prueba tanto con node12, como con node14.
  • steps: Aqu铆 se definen los pasos que se ejecutar谩n en el action.
  • uses: actions/checkout@v2: Este es el primer paso l贸gico en el pipeline, con este paso se clona el proyecto en el entorno de trabajo en que se ejecuta el job actual.
  • user: actions/setup-node@v2: Aqu铆 se utiliza este paso que b谩sicamente nos configura la versi贸n de Node deseada en el entorno del jobs actual.
  • run: npm ci: Se instalan las dependencias del presente proyecto.

El caso anterior es un ejemplo bien sencillo de la configuraci贸n de un pipeline, con solo agregar la ejecuci贸n de los tests del proyecto podr铆a ser perfectamente un pipeline v谩lido y utilizable en cualquier proyecto.

Construir el proyecto y ejecutar las pruebas

Como primer paso, tiene todo el sentido del mundo determinar en la integraci贸n continua: el comprobar la validez de nuestro c贸digo antes de hacer ninguna operaci贸n de despliegue que pueda destruir lo que debidamente se encuentra funcionando en producci贸n o cualquier otro environment.

A continuaci贸n, un ejemplo de como quedar铆a el archivo de configuraciones:

name: CI/CD
on: [ push, pull_request ]

jobs:
  # BUILD PROJECT AND DEPENDENCIES
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 12.x ]
    steps:
      - uses: actions/checkout@v2
      - name: Cache project node-modules
        uses: actions/cache@v2
        with:
          path: 'node_modules'
          key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }}
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}

      # pipeline actions
      - run: npm ci
      - run: npm run build

  # RUN TESTS AND LINT
  test:
    needs: [ build ]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 12.x ]
    steps:
      - uses: actions/checkout@v2
      - name: Cache project node-modules
        uses: actions/cache@v2
        with:
          path: 'node_modules'
          key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }}
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install codecov dependency
        run: npm install -g codecov

      # pipeline actions
      - run: npm run lint
      - run: npm test
      - run: codecov
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

En el ejemplo anterior se presentan 2 jobs, el primero permite instalar las dependencias y construir el proyecto, y en el segundo ejemplo se ejecutan el chequeo de estilo (lint), los tests y luego se sube el coverage a codecov.io.

Explicando el job build

  • uses: actions/cache@v2: este paso permite guardar en cach茅 la carpeta node_modules, para evitar tener que instalar las dependencias nuevamente en el job test.
  • run: npm run build: se ejecuta el build del proyecto y se identifica si funciona o no.

Explicando el job test

  • needs: [ build ]: en este paso se garantiza que el job test solo se ejecute luego del job build.
  • uses: actions/cache@v2: en este paso se reutiliza el node_modules almacenado en cach茅 en el paso anterior.
  • run: npm install -g codecov: se instala el plugin para integrar codecov con el coverage del proyecto.
  • run: npm run lint: ejecutar el chequeo del c贸digo
  • run: npm test: ejecutar los test y generar el coverage del proyecto.
  • run: codecov: se sube el coverage a codecov. Para esta integraci贸n se utiliza la variable de entorno secrets.CODECOV_TOKEN.

Desplegar proyecto v铆a SSH

Para el despliegue del proyecto en un hosting, se har谩 uso de algunos actions que por medio de SSH permiten copiar el proyecto a donde se determine y previamente ejecutar los mismo.

A continuaci贸n, un ejemplo de c贸mo puede quedar la configuraci贸n de nuestro job para desplegar el proyecto:

name: Deployment CI
on: [ push, pull_request ]

jobs:

  ...

  # DEPLOY BLOG (PRODUCTION)
  deploy:
    needs: [ test ]
    if: github.ref == 'refs/heads/master'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Copy project to the hosting
        uses: easingthemes/ssh-deploy@v2.1.5
        env:
          SSH_PRIVATE_KEY: $ {{ secrets.SERVER_SSH_KEY }}
          ARGS: "-rltgoDzvO --delete"
          SOURCE: ""
          REMOTE_HOST: $ {{ secrets.REMOTE_HOST }}
          REMOTE_USER: $ {{ secrets.REMOTE_USER }}
          TARGET: $ {{ secrets.PROD__REMOTE_TARGET }}
      - name: Deploy project with docker in hosting
        uses: garygrossgarten/github-action-ssh@release
        with:
          command: cd $ {{ secrets.PROD__REMOTE_TARGET }} && docker-compose up -d
          host: $ {{ secrets.REMOTE_HOST }}
          username: $ {{ secrets.REMOTE_USER }}
          privateKey: $ {{ secrets.SERVER_SSH_KEY}}

Explicando el job deploy

  • needs: [ test ]: se define que solo se ejecute el build, luego del job test.
  • if: github.ref == 'refs/heads/master': solo se ejecuta el job, cuando el action fue ejecutado en la rama master.
  • uses: easingthemes/ssh-deploy@v2.1.5: en este paso se copia el proyecto a una carpeta determinada del hosting. Para esto se utilizan las variables de entorno necesarias para establecer la conexi贸n v铆a SSH.
  • uses: garygrossgarten/github-action-ssh@release: luego de que el proyecto fue copiado v铆a SSH, resta levantar el proyecto en el hosting, para esto se utiliza este step, que se conecta v铆a SSH y luego ejecuta un comando dentro del hosting. En el ejemplo se puede ver, que se levanta un contenedor de Docker con el proyecto.

Crear secrets en GitHub

En las configuraciones anteriores se hace referencia en varias ocasiones a los secrets de GitHub en los jobs. Para configurar nuevos secrets en el proyecto, hay que ir a las configuraciones del proyecto:

github settings button

Dentro de las configuraciones se elegir el 谩rea destinada a los secrets:

github secrets settings

Y por 煤ltimo, crear un nuevo secret.

create new secret

De esta manera pueden crear todos los secrets que necesiten en el proyecto.

Referencias

Para m谩s referencias sobre el tema, pueden revisar el repositorio de Binary Coffee en Github:

Tambi茅n pueden revisar la documentaci贸n oficial de GitHub actions:

Conclusiones

Espero que la experiencia compartida sirva para que de forma m谩s eficiente y r谩pida puedan configurar la integraci贸n continua de sus proyectos con GitHub Actions.

Happy Coding!!!

Opiniones