CI/CD con GitHub Actions

Jorge Hernández Ríos
4 min readNov 15, 2020

Una de las ventajas que encontraba de usar GitLab frente a GitHub era la herramienta de CI/CD que GitLab te proporciona (https://docs.gitlab.com/ee/ci/). Es cierto que existen miles de herramientas para poder automatizar tareas de integración y despliegue continuo, pero el poder tenerlo integrado en tu propio software de repositorio de código lo hace bastante interesante. Esto no era posible con GitHub, que ha sido con la que he trabajado prácticamente siempre. Sin embargo, un buen día aparecería GitHub Actions (https://github.blog/2019-08-08-github-actions-now-supports-ci-cd/) y esto cambiaría.

Con GitHub Actions podemos diseñar todo nuestro flujo de trabajo de manera automatizada. Podemos especificar cuándo, cómo y qué tareas lanzar. Por ejemplo, podemos determinar que se lance nuestra batería de tests, herramientas de revisión de código o incluso desplegar nuestra aplicación, todo desde GitHub. Lo que quieras. Aquí se puede encontrar todas las características de esta herramienta: https://github.com/features/actions.

Recientemente la utilicé en un proyecto personal y me gustó bastante. Se trata de una aplicación Symfony que se despliega en DigitalOcean. Para poder cubrir todo el flujo del desarrollo de la aplicación, definí lo siguiente:

Continuous Integration

Ejecutadas a la hora de realizar un pull request, son tareas de control y mantenimiento de la calidad del código. Para ello, se utilizan las siguientes herramientas:

Normalmente utilizo este script para reunir el lanzamiento de todas a la vez: https://github.com/JorgeHRJ/qualitify. La idea es que hasta que todas las comprobaciones no sean exitosas, no se mezcle el pull request creado. De esta manera nos aseguramos que cada funcionalidad se va añadiendo según los estándares acordados.

Continuous Deployment/Delivery

Estas tareas van centradas en el despliegue de la aplicación una vez un pull request ha sido mezclado o se ha hecho un push en una determinada rama. De esta manera cumplimos todo el ciclo: desde el desarollo de los cambios hasta su incorporación en producción.

Los archivos

Para implementar este tipo de GitHub Actions en un proyecto, es necesario crear en la raíz del proyecto el directorio .github/workflows. Dentro de este directorio, se definen los workflows que queremos que GitHub reconozca y ejecute. En base a los dos puntos explicados anteriormente, estos serían algunos ejemplos para utilizar:

continuous-integration.yml

name: Continuous Integration

on: [pull_request]

jobs:
quality:
name: Quality checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1

- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest

- name: PHP Code Sniffer
if: ${{ always() }}
run: vendor/bin/phpcs --standard=PSR2 src

- name: PHP Mess Detector
if: ${{ always() }}
run: vendor/bin/phpmd src/ text codesize,controversial,phpmd.xml

- name: PHP Stan
if: ${{ always() }}
run: vendor/bin/phpstan analyse -c phpstan.neon

- name: PHP Unit tests
if: ${{ always() }}
run: bin/phpunit

Como se puede ver, este workflow se va a lanzar cuando se crea un pull request. Tiene un único job, que consiste en las comprobaciones de calidad que comentaba anteriormente. Básicamente lo que realiza, como se puede ver en cada step es ir lanzando los comandos que le especificamos: primero hacemos un checkout del código utilizando una acción que nos proporciona ya GitHub, luego instalamos las dependencias de composer y después vamos lanzando los comandos de cada una de nuestras herramientas. De cada una de ellas, hay que destacar la existencia de esos ifs, ya que determinan que, pese a que un determinado paso falle, se siga ejecutando. De esta manera nos aseguramos que cada herramienta ha sido ejecutada y comprobaremos los resultados de todas al finalizar. A continuación un ejemplo gráfico de cómo se ejecuta en GitHub:

Ejemplo de la acción ‘Continuous Integration’

continuous-deployment.yml

name: Continuous Deployment
on:
push:
branches:
- master

jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Deploy
uses: appleboy/ssh-action@master
env:
GITHUB_USERNAME: ${{ secrets.GH_USERNAME }}
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
with:
host: ${{ secrets.HOST }}
port: ${{ secrets.PORT }}
username: ${{ secrets.USER }}
key: ${{ secrets.KEY }}
envs:
script: |
cd /home/app/bielatv
bin/deploy.sh

Por otro lado, tendríamos el workflow dedicado al despliegue de la aplicación. Como vemos, se ejecuta cuando se realiza un push sobre la rama master (por tanto, cuando mezclamos un pull request también). De esta manera, cada vez que haya cambios en master pasaremos a realizar un despliegue en producción. Otro caso interesante sería en el que tuvieramos, por ejemplo, un entorno de preproducción o desarrollo, de manera que podemos especificar que cuando los cambios sean en otra determinada rama, se ejecute el despliegue a ese otro entorno.

En este caso, se realizan los siguientes pasos: se hace un checkout del código (como en el caso anterior) y luego hacemos uso de la acción “ssh-action” (https://github.com/appleboy/ssh-action). Ella nos permite realizar conexiones ssh y ejecutar los comandos necesarios. Para ello, hacemos uso de los GitHub Secrets (https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) que determinamos en el repositorio de nuestro proyecto, para así poder realizar la conexión a nuestro servidor y, en mi caso, lanzar un script de deploy.

Ejemplo de la acción ‘Continuous Deployment’

Y aquí terminaría todo el flujo: ya tendríamos nuestra nueva feature validada y funcionando en producción.

--

--