En el vertiginoso mundo del desarrollo web, la integración continua y el despliegue continuo (CI/CD) se han convertido en prácticas indispensables para entregar software de alta calidad de forma eficiente. CI/CD permite a los desarrolladores automatizar el proceso de creación, prueba y despliegue de los cambios de código, reduciendo el riesgo de error humano y permitiendo iteraciones más rápidas.

Este artículo explica la importancia de CI/CD, cómo crear un canal de CI y cómo configurar el despliegue continuo en tu canal de CI con la API de Kinsta mediante programación, todo ello con Acciones de GitHub en tu repositorio de GitHub.

¿Por Qué Usar CI/CD?

La plataforma de Alojamiento de Aplicaciones de Kinsta siempre ha ofrecido una opción para el despliegue automático, que se activa cada vez que hay un cambio en una rama específica de tu repositorio Git alojado. Sin embargo, esto puede no ser ideal para grandes proyectos con múltiples miembros del equipo. Muchos desarrolladores tienden a evitar activar el despliegue automático por diversas razones.

Una razón es que, en un entorno colaborativo en el que varios desarrolladores trabajan en el mismo proyecto, los despliegues automáticos activados por el cambio de un desarrollador en el repositorio pueden provocar inestabilidad y problemas imprevistos. Sin las pruebas y la validación adecuadas, incluso un pequeño cambio en el código podría alterar el sitio en producción, causando potencialmente tiempos de inactividad y experiencias negativas para los usuarios.

Aquí es donde entra en juego un pipeline de CI/CD. Creando un flujo de trabajo CI/CD cuidadosamente orquestado, los desarrolladores pueden asegurarse de que los cambios de código se someten a pruebas y validación antes de desplegarse en el sitio en producción. Hay muchas herramientas disponibles para implementar CI/CD en el desarrollo de software, utilizaremos GitHub Actions para este tutorial.

¿Qué Es GitHub Actions?

GitHub Actions es una potente herramienta de automatización proporcionada por GitHub. Ofrece a los desarrolladores la posibilidad de automatizar varias tareas, procesos y flujos de trabajo dentro de sus proyectos de desarrollo de software. Se integra con los repositorios de GitHub, lo que facilita su uso.

Con las Acciones de GitHub y la API de Kinsta, puedes definir flujos de trabajo personalizados que se adapten a los requisitos de tu proyecto. Puedes configurar un pipeline CI que pruebe tu aplicación y active el despliegue en Kinsta.

Primeros Pasos con GitHub Actions

Las Acciones de GitHub se basan en el concepto de flujos de trabajo, que son conjuntos de tareas automatizadas que se activan por eventos específicos o se programan a intervalos regulares. Estos eventos pueden incluir envíos de código, pull requests, creación de incidencias, etc. Cuando se produce uno de estos eventos, GitHub Actions ejecuta automáticamente un flujo de trabajo asociado, ejecutando una serie de pasos predefinidos.

Cada paso del flujo de trabajo representa una acción concreta, como construir el código, ejecutar pruebas, desplegar o enviar notificaciones. Creemos un flujo de trabajo con tres tareas:

  1. Comprobar la sintaxis con ESLint
  2. Ejecutar pruebas
  3. Despliega de nuevo tu aplicación

Paso 1: Configura Tu Repositorio de GitHub

Para empezar con las Acciones de GitHub, necesitas un repositorio de GitHub.

Aquí, estamos utilizando este repositorio de GitHub, desarrollado para el tutorial Cómo Construir y Desplegar una Aplicación Clonada de ChatGPT con React y la API OpenAI.

Siéntete libre de utilizar el repositorio tú mismo navegando hasta él en GitHub y seleccionando: Use this template > Create a new repository (Usar esta plantilla > Crear un nuevo repositorio).

En esta aplicación React, se crean pruebas unitarias para probar cada componente. También se utiliza ESLint para que la sintaxis y el formato del código sean perfectos. El pipeline de CI bloqueará un despliegue si un pull request o código fusionado enviado al repositorio no supera las pruebas del flujo de trabajo

Paso 2: Crear un Archivo de Flujo de Trabajo

Define tu flujo de trabajo creando un archivo YAML en el directorio .github/workflows de tu repositorio. Este directorio debe estar en el nivel root de tu repositorio. La convención de nomenclatura para los archivos de flujo de trabajo es nombre-del-flujo-de-trabajo.yml.

  1. En tu repositorio, crea un directorio .github.
  2. Dentro del directorio .github, crea un nuevo directorio llamado workflows.
  3. Dentro del directorio workflows, crea un nuevo archivo con un nombre como build-test-deploy.yml.

Paso 3: Escribe el Flujo de Trabajo CI/CD

Ahora que has creado tu archivo workflow, define un flujo de trabajo con los pasos necesarios para comprobar la sintaxis con ESLint, ejecutar las pruebas y desplegar la aplicación.

Crear Evento CI

Al crear un flujo de trabajo CI, el primer paso es dar un nombre al flujo de trabajo y, a continuación, establecer el evento que activaría el flujo de trabajo. Para este ejemplo, los dos eventos son una solicitud de pull y un push a la rama principal.

name: Build, Test, and Deploy

on:
  push:
    branches: "main"
  pull_request:
    branches: "main"

Si quieres programar trabajos periódicos (CRON jobs) para tareas específicas, puedes añadirlos al flujo de trabajo. Por ejemplo, puede que quieras ejecutar ciertas tareas como copias de seguridad de la base de datos, limpieza de datos u otras tareas de mantenimiento periódicas.

Aquí tienes un ejemplo de cómo puedes añadir un CRON job al flujo de trabajo:

on:
  # Existing event triggers for push and pull_request

  # Add a schedule for CRON jobs
  schedule:
    - cron: "0 0 * * *"

El ejemplo anterior activará el flujo de trabajo todos los días a medianoche (hora UTC), ya que la programación cron está configurada en 0 0 * * *. Puedes personalizar la programación cron para adaptarla a tus necesidades específicas.

Como otro ejemplo, supongamos que quieres programar el flujo de trabajo CI/CD para que se ejecute todos los lunes a las 8 a.m. Podemos configurar un CRON job utilizando el evento schedule:

name: Build, Test, and Deploy

on:
  push:
    branches: "main"
  pull_request:
    branches: "main"

  # Schedule the workflow to run every Monday at 8 a.m. (UTC time)
  schedule:
    - cron: "0 8 * * 1"

jobs:
 # Add jobs

La sintaxis de programación utilizada en el evento schedule para los flujos de trabajo de las Acciones de GitHub se basa en la sintaxis cron de UNIX. Te permite definir horas o intervalos específicos para que tu flujo de trabajo se ejecute automáticamente. La sintaxis consta de cinco campos que representan diferentes aspectos de la programación. Cada campo está separado por un espacio. El formato general de la sintaxis de programación es el siguiente:

* * * * *
┬ ┬ ┬ ┬ ┬
│ │ │ │ │
│ │ │ │ └─ Day of the week (0 - 7) (Sunday to Saturday, where both 0 and 7 represent Sunday)
│ │ │ └─── Month (1 - 12)
│ │ └───── Day of the month (1 - 31)
│ └─────── Hour (0 - 23)
└───────── Minute (0 - 59)

Ahora, vamos a desglosar cada campo:

  • Minuto (0 – 59): El minuto en el que se activará el cron job. Por ejemplo, 15 significa que el flujo de trabajo se activará en el minuto 15 de la hora.
  • Hora (0 – 23): La hora a la que se activará el cron job. Por ejemplo, 8 significa que el flujo de trabajo se activará a las 8 de la mañana.
  • Día del mes (1 – 31): El día del mes en el que se activará el cron job. Por ejemplo, 1 significa que el flujo de trabajo se activará el día 1 del mes.
  • Mes (1 – 12): El mes en el que se activará el cron job. Por ejemplo, 6 significa que el flujo de trabajo se activará en junio.
  • Día de la semana (0 – 7): El día de la semana en el que se activará el cron job. Aquí, 0 y 7 representan el domingo, mientras que 1 representa el lunes, y así sucesivamente. Por ejemplo, 4 significa que el flujo de trabajo se activará el jueves.

Caracteres especiales:

  • * (asterisco): Coincide con cualquier valor de ese campo. Por ejemplo, * en el campo minuto significa que el flujo de trabajo se activará cada minuto.
  • */n (barra): Especifica un intervalo. Por ejemplo, */5 en el campo minuto significa que el flujo de trabajo se activará cada 5 minutos.
  • , (coma): Especifica varios valores específicos. Por ejemplo, 1,15,30 en el campo minutos significa que el flujo de trabajo se activará en los minutos 1, 15 y 30 de la hora.
  • - (guión): Especifica un rango de valores. Por ejemplo, 1-5 en el campo día de la semana significa que el flujo de trabajo se activará de lunes a viernes (del 1 al 5).
  • ? (signo de interrogación): Se utiliza para no especificar ningún valor concreto. Se suele utilizar en el campo día de la semana cuando se especifica el día del mes. Por ejemplo, ? en el campo día de la semana y 15 en el campo día del mes significa que el flujo de trabajo se activará el día 15 del mes, independientemente del día de la semana.

Crear una Tarea CI para Comprobar la Sintaxis con ESLint

Para configurar el proceso CI, crearemos los trabajos o tareas necesarios. Cada trabajo debe tener un nombre claro y comprensible. Vamos a llamar al primer trabajo eslint, ya que implicará la comprobación de la sintaxis del código utilizando ESLint.

Además, podemos proporcionar una descripción legible por humanos, aunque esta parte es opcional. A continuación, especificamos que el trabajo debe ejecutarse en un entorno Ubuntu y utilizar una estrategia matricial para probar el código contra dos versiones de Node.js: 18.x y 20.x.

jobs:
  eslint:
    name: Check Syntax with ESLint
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x]

A continuación, define los pasos que ejecutará el trabajo «ESLint». Estos pasos incluyen la comprobación del código, la configuración de la versión de Node.js especificada para ejecutar ESLint, el almacenamiento en caché de los paquetes npm, la instalación de las dependencias del proyecto y, por último, la ejecución de ESLint para comprobar la sintaxis del código.

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Use Node.js ${{ matrix.node-version }} to Check Lint
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install Dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

En el flujo de trabajo anterior, a cada paso se le da una descripción con un nombre para facilitar la identificación del origen de los errores o fallos al inspeccionar el flujo de trabajo desde Acciones de GitHub. En particular, en el tercer paso, utilizamos el comando npm ci para instalar las dependencias, que es preferible a npm install, ya que realiza una instalación limpia. Además, el último paso, ejecutar ESLint utilizando npm run lint, supone que has configurado este comando en tu archivo package.json.

A continuación se muestra el trabajo completo para comprobar la sintaxis del código con ESLint:

jobs:
  eslint:
    name: Check Syntax with ESLint
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x]

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Use Node.js ${{ matrix.node-version }} to Check Lint
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install Dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

Crear Tarea CI para Ejecutar Pruebas

Para añadir la tarea CI para ejecutar pruebas, empieza por definir el trabajo y darle un nombre descriptivo, como tests. También especificaremos que este trabajo depende del trabajo eslint, lo que significa que el trabajo eslint se ejecutará primero antes de que se ejecute el trabajo tests. Esta dependencia garantiza que se compruebe el código en busca de errores de sintaxis antes de ejecutar las pruebas.

  tests:
    name: Run Tests
    needs: eslint
    runs-on: ubuntu-latest

A continuación, define los pasos para la tarea tests. De forma similar al trabajo anterior, comprobaremos el código, configuraremos la versión de Node.js 18.x para ejecutar las pruebas, instalaremos las dependencias del proyecto utilizando npm ci, y después ejecutaremos las pruebas utilizando el comando npm run test.

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Use Node.js 18.x to run Test
        uses: actions/setup-node@v3
        with:
          node-version: 18.x
          cache: 'npm'

      - name: Install Dependencies
        run: npm ci

      - name: Run Tests
        run: npm run test

Crear Tarea CI para Desplegar con la API de Kinsta

Para crear la tarea CI para desplegar en Kinsta utilizando la API de Kinsta, definiremos el trabajo y lo llamaremos deploy. Este trabajo dependerá de los trabajos eslint y tests, asegurando que el despliegue se ejecute sólo después de que el código haya sido comprobado en busca de errores de sintaxis y haya superado las pruebas. Configuraremos el trabajo para que se ejecute en un entorno Ubuntu utilizando la última versión disponible.

  deploy:
    name: Re-Deploy Application
    needs: [eslint, tests]
    runs-on: ubuntu-latest

A continuación, define los pasos. En este caso, ejecutarás un comando cURL para interactuar con la API de Kinsta de forma programática y activar un nuevo despliegue. Comprendamos primero la API de Kinsta, la diversa información necesaria para interactuar con la API, y cómo obtener/almacenar información importante asociada a la API — como la clave de la API — de forma segura en GitHub.

Comprender la API de Kinsta

La API de Kinsta es una potente herramienta que te permite interactuar con los servicios de Kinsta mediante programación. Para utilizar la API, debes tener una cuenta con al menos un sitio WordPress, una Aplicación o una Base de Datos en MyKinsta. También necesitas generar una clave API para autenticarte y acceder a tu cuenta a través de la API.

Para generar una clave API:

  1. Ve a tu panel de MyKinsta.
  2. Ve a la página Claves API (Tu nombre > Configuración de la empresa > Claves API).
  3. Haz clic en Crear Clave API.
  4. Elige una fecha de caducidad o establece una fecha de inicio personalizada y un número de horas para que caduque la clave.
  5. Dale a la clave un nombre único.
  6. Haz clic en Generar.
Crear Clave API en MyKinsta.
Crear clave API en MyKinsta.

Después de crear una clave API, cópiala y guárdala en algún lugar seguro (recomendamos utilizar un gestor de contraseñas), ya que es la única vez que se revela dentro de MyKinsta.

Cómo Activar el Despliegue con la API de Kinsta

Para desplegar una aplicación en Kinsta utilizando la API, necesitas dos parámetros obligatorios: el ID de la aplicación y la rama. Puedes recuperar mediante programación el ID de tu aplicación consultando primero la lista de tus aplicaciones, que te proporcionará detalles sobre cada aplicación, incluido su ID.

Una vez obtenida la información necesaria, puedes hacer una solicitud POST al endpoint /applications/deployments de la API. Para la canalización CI, utilizaremos cURL, una herramienta de línea de comandos para interactuar con URLs.

curl -i -X POST 
  https://api.kinsta.com/v2/applications/deployments 
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>' 
  -H 'Content-Type: application/json' 
  -d '{
    "app_id": "<YOUR_APP_ID>",
    "branch": "main"
  }'

Activar el Despliegue con cURL en el Pipeline CI/CD

Para activar el despliegue con la API Kinsta, añade el comando cURL al comando run de tu Pipeline de CI. Sin embargo, es importante que almacenes tu clave API y tu ID de aplicación de forma segura.

Para almacenar claves secretas (secrets) en GitHub y utilizarlas en GitHub Actions, sigue estos pasos:

  1. Dirígete al repositorio en el que quieres establecer el «secret»
  2. Accede a la pestaña «Settings» en el menú principal del repositorio.
  3. En la barra lateral a la izquierda, elige la opción «Secrets» dentro de la sección «Options«.
  4. Pulsa el botón «New repository secret«.
  5. Asigna un nombre a tu secreto, por ejemplo: KINSTA_API_KEY, e introduce la clave API de Kinsta en el espacio designado como «Value«.
  6. Una vez añadidos el nombre y el valor, pulsa «Add secret» para guardarlos.
  7. Repite estos pasos para cualquier otro secreto que desees añadir.
Almacenando secrets en GitHub.
Almacenando secrets en GitHub.

Una vez que hayas añadido los secrets, puedes hacer referencia a ellos en tu flujo de trabajo de GitHub Actions utilizando la sintaxis ${{ secrets.SECRET_NAME }}.

Ahora vamos a completar el trabajo deploy para tu pipeline de CI/CD de GitHub Actions. Define los pasos igual que antes, con un único paso para desplegar en Kinsta. Primero, define los secrets en el comando env, y luego añade el comando cURL para ejecutar el despliegue.

    steps:
      - name: Deploy to Kinsta
        env:
          KINSTA_API_KEY: ${{ secrets.KINSTA_API_KEY }}
          APP_ID: ${{ secrets.APP_ID }}
        run: |
          curl -i -X POST 
            https://api.kinsta.com/v2/applications/deployments 
            -H "Authorization: Bearer $KINSTA_API_KEY" 
            -H "Content-Type: application/json" 
            -d '{
              "app_id": "'"$APP_ID"'",
              "branch": "main"
            }'

En el comando cURL, observarás que las variables de entorno se añaden dentro del comando, lo que permite acceder de forma segura a los secrets durante el proceso de despliegue.

Este es el aspecto que tendrá tu flujo de trabajo CI/CD final:

name: Build, Test, and Deploy

on:
  push:
    branches: "main"
  pull_request:
    branches: "main"

jobs:
  eslint:
    name: Check Syntax with ESLint
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x]

    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        
      - name: Use Node.js ${{ matrix.node-version }} to Check Lint
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
          
      - name: Install Dependencies
        run: npm ci
        
      - name: Run ESLint
        run: npm run lint

  tests:
    name: Run Tests
    needs: eslint
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        
      - name: Use Node.js 18.x to run Test
        uses: actions/setup-node@v3
        with:
          node-version: 18.x
          cache: 'npm'
          
      - name: Install Dependencies
        run: npm ci
        
      - name: Run Tests
        run: npm run test

  deploy:
    name: Re-Deploy Application
    needs: [eslint, tests]
    runs-on: ubuntu-latest

    steps:
      - name: Deploy to Kinsta
        env:
          KINSTA_API_KEY: ${{ secrets.KINSTA_API_KEY }}
          APP_ID: ${{ secrets.APP_ID }}
        run: |
          curl -i -X POST 
            https://api.kinsta.com/v2/applications/deployments 
            -H "Authorization: Bearer $KINSTA_API_KEY" 
            -H "Content-Type: application/json" 
            -d '{
              "app_id": "'"$APP_ID"'",
              "branch": "main"
            }'

Copia el flujo de trabajo dado y pégalo en tu archivo build-test-deploy.yml. A continuación, inicia un pull request para añadir este archivo a la rama principal de tu repositorio. Recuerda que este pull request activará automáticamente el flujo de trabajo.

Esto te permite revisar los cambios realizados en tu repositorio y asegurarte de que cualquier nuevo cambio en el pull request cumple las comprobaciones especificadas antes de decidir si lo fusionas en tu código base.

Almacenando secrets en GitHub.
Almacenando secrets en GitHub.

Cuando fusiones la pull request Ve a la pestaña Actions de tu repositorio de GitHub y verás cómo se ejecuta el flujo de trabajo CI/CD.

Resumen de GitHub Actions
Resumen de GitHub Actions

Puedes hacer clic en cada tarea para ver más detalles sobre ella (por eso debes dar a cada paso de tu tarea una descripción significativa).

Detalles de los pasos de CI.
Detalles de los pasos de CI.

Hacer Cumplir el Flujo de Trabajo de las Pull Request en GitHub

Para garantizar una gestión eficaz del código y la colaboración en los repositorios de GitHub, es útil aplicar un flujo de trabajo de solicitudes de extracción y bloquear los commits directos a la rama principal. Este enfoque establece un proceso de desarrollo controlado y organizado, exigiendo que todos los cambios se sometan a pull requests y revisiones antes de fusionarse en la rama principal.

Adoptando esta práctica, los equipos de desarrollo pueden mejorar la calidad del código, minimizar el riesgo de introducir errores y mantener un historial transparente de los cambios.

A continuación te explicamos cómo configurar el cumplimiento del flujo de trabajo de las pull requests:

  1. Haz clic en la pestaña Settings de tu repositorio de GitHub.
  2. En Code and Automation, selecciona Branches en las opciones de la barra lateral.
  3. Si no existen reglas, haz clic en Add branch protection rule.
  4. Proporciona un nombre para la regla y, a continuación, marca la casilla  Require a pull request before merging. Esto mostrará más opciones de configuración.
  5. Marca también la casilla Require status checks to pass before merging.
  6. Personaliza las opciones adicionales en función de tus preferencias y requisitos.
  7. Haz clic en el botón Create para guardar la regla.
Aplicación del flujo de trabajo de las solicitudes de extracción en GitHub
Aplicación del flujo de trabajo de solicitudes de extracción en GitHub.

Siguiendo estos pasos, habrás configurado correctamente una regla para aplicar el flujo de trabajo del pull request en tu repositorio de GitHub. Esto garantiza que todos los cambios se sometan a revisión y a comprobaciones automatizadas antes de fusionarse en la rama principal, fomentando un entorno de desarrollo más fiable y colaborativo.

Resumen

Combinando la potencia de las Acciones de GitHub y la API de Kinsta, puedes agilizar tu flujo de trabajo de desarrollo y fomentar un entorno colaborativo y eficiente para tu equipo de desarrollo.

Los desarrolladores pueden aportar código con confianza, sabiendo que se probará exhaustivamente antes de llegar a producción, y las partes interesadas pueden estar tranquilas sabiendo que el proceso de despliegue está bien controlado y es resistente a errores.

¿Cómo utilizas la API Kinsta? ¿Qué endpoints te gustaría que se añadieran a la API? ¿Qué tutorial relacionado con la API Kinsta te gustaría leer a continuación?

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.