En el siguiente tutorial vamos a explicar como trabajar con la integración continua de GitLab y con el desarrollo de nuestro tema de Ghost.

Actualmente el tema de ./voidNull está siendo desarrollado directamente por mi desde cero. Mi "laboratorio" consta de un contenedor docker con Ghost donde tengo el tema instalado que es mi repositorio local donde trabajo para hacer los cambios. De esta manera, si hago algún cambio a nivel de CSS o JS se aplican los cambios al momento.

Una cosa que hice es que cuando hiciera pull para subir los cambios al repositorio en GitLab, se ejecutaran unas tareas para importar el tema en el servidor y así tener los últimos cambios creados en desarrollo directamente en producción. Sin tener que descargar el tema y tenerlo que subir de nuevo a Ghost. Todo se hace de forma automática.

Como que en su día monté todo esto para el tema de ./voidNull y no documenté mucho, vamos hacer el proceso con otro tema que tengo entre manos. En este caso, es un fork del tema oficial de Ghost llamado Wave y que es para el podcasting.

El fork está creado en GitHub, pero he clonado una copia en GitLab para configurar la CI directamente y ver que procedimiento debemos de seguir.

El plan

El objetivo es utilizar gulp para el desarrollo, de esta forma construimos y empaquetamos el tema con diferentes tareas. Aprovechando gulp, lo que haremos será una tarea más para que se haga el despliegue al servidor, añadir las dependencias necesarias usando yarn y añadiremos una acción de secuencia de comandos en package.json para hacer las tareas correspondientes.

Luego tendremos que configurar GitLab-CI para que llame a todas esas tareas, las de construir, empaquetar y desplegar. Muy fácil. 😄

Crear integración en Ghost

Para subir el tema, necesitamos una clave para poder trabajar con la API de Ghost. Para eso necesitamos crear una integración nueva en Ghost. Iremos a Settings - Advanced: Integrations y haremos clic en Add custom integration.

Le pondremos un nombre:

Y podremos añadir una descripción y tendremos los datos que necesitaremos:

Los datos que nos interesan es el Admin API key y el API URL para el siguiente paso.

💡
La variable Admin API Key se genera automáticamente y el API URL debe de ser el dominio de nuestro Ghost.

Configurar variables para GitLab CI

Ahora tendremos que ir a GitLab donde nuestro repositorio e ir a Configuración - CI / CD en el apartado de Variables.

He creado dos variables GHOST_ADMIN_API_KEY y GHOST_API_URL:

La primera variable tiene de valor la Admin API keyd que habíamos visto antes al crear la integración. La segunda variable contiene el dominio completo (con HTTPS) del Ghost en producción.

Añadiendo dependencias

Ghost nos proporciona un SDK de JavaScript que contiene lo suficiente para gestionar la API de Ghost. La API incluye un endpoint que permite cargar temas de paquetes. Esto es lo que utilizaremos para actualizar el tema en producción.

Así que lo instalaremos con Yarn directamente en la ruta de nuestro tema personalizado:

yarn add tryghost/admin-api --dev

Implementando la tarea "deploy"

Para implementar la tarea de deploy, lo haremos usando una tarea de Gulp añadiendo el siguiente código en nuestro gulpfile.js. Como que en este caso es un fork de un tema oficial de Ghost, ya tendremos este fichero creado con diferentes tareas.

Simplemente deberemos de añadir lo siguiente:

const GhostAdminApi = require('@tryghost/admin-api');

async function deploy(done) {
    try {
        const url = process.env.GHOST_API_URL;
        const admin_api_key = process.env.GHOST_ADMIN_API_KEY;
        const themeName = process.env.THEME_NAME || require('./package.json').name;
        const zipFile = `dist/${themeName}.zip`;
        const api = new GhostAdminApi({
            url,
            key: admin_api_key,
            version: 'v5.0'
        });

        await api.themes.upload({
            file: zipFile
        });
        console.log(`${zipFile} successfully uploaded.`);
        done();
    } catch (err) {
        console.error(err);
        done(err);
    }
}

exports.deploy = deploy;

Luego deberemos de añadir la tarea directamente en el package.json:

/* ... */
    "scripts": {
        "dev": "gulp",
        "test": "gscan .",
        "zip": "gulp zip",
        "deploy": "gulp deploy" // <= Hay que añadir esto
    },
/* ... */

Creando la configuración para GitLab

El siguiente paso será configurar el fichero .gitlab-ci.yml dentro de nuestro repositorio para así configurar la integración continua con GitLab.

Lo que queremos es que la CI pruebe, compile y luego empaquete siempre que haya cambios en el repositorio. Cuando haga todo eso, entonces quiero que despliegue el tema automáticamente a producción con los nuevos cambios de la rama.

Para todo este proceso hay 3 etapas:

stages:
  - .pre
  - build
  - deploy

install

.pre es una etapa incorporada que siempre se ejecuta primero. La usaremos para una tarea que llamaremos install. Esta tarea que ejecuta el yarn install y obtiene todas las dependencias para construir, empaquetar y desplegar el tema. Esto creará un directorio llamado node_modules que va a ser almacenado en caché para siguientes ejecuciones del pipeline.

install:
  stage: .pre
  script:
    - npm install
  artifacts: # cache packages for subsequent _steps_ in the same pipelin run
    paths:
      - node_modules/
    expire_in: 30min
  cache: # caches packages for subsequent _pipeline_ run
    paths:
      - node_modules/

build

En esta otra etapa llamaremos la tarea zip que también viene con el tema. Esto construirá el tema y lo empaquetará como un archivo zip. Este archivo se guardará en GitLab como artefacto que se usará en las tareas posteriores.

Ten en cuenta que esta tarea se ejecutará para todas las ramas y confirmaciones. El paquete estará disponible para su descarga desde la interfaz de usuario de GitLab si es necesario.

build:
  stage: build
  script:
    - npm install
    - ./node_modules/gulp/bin/gulp.js zip
  artifacts:
    expire_in: 1 day
    paths:
      - dist/

deploy

¡La etapa más divertida!

En esta etapa llamamos la tarea yarn deploy en el package.json de antes, que cogerá el archivo zip que creamos anteriormente por la compilación y lo subirá a Ghost.

No quiero que todos los cambios que haga en cualquier rama se desplieguen a mi blog de producción de inmediato, por lo que este trabajo sólo se ejecutará para los cambios en la rama main.

deploy:
  stage: deploy
  script:
    - npm install
    - ./node_modules/gulp/bin/gulp.js deploy
  only:
    refs:
      - main

Cada vez que hagamos un commit a main o se haga un merge de una rama a main, GitLab-CI automáticamente construirá y desplegará el tema en producción. ¡Yuuhuu 🎉!

GitLab-CI al completo:

image: node:16

variables:
  THEME_NAME: wavefoss

stages:
  - .pre
  - build
  - deploy

install:
  stage: .pre
  script:
    - npm install
  artifacts: 
    paths:
      - node_modules/
    expire_in: 30min
  cache:
    paths:
      - node_modules/

build:
  stage: build
  script:
    - npm install
    - ls -lat
    - ./node_modules/gulp/bin/gulp.js zip
  artifacts:
    expire_in: 1 day
    paths:
      - dist/

deploy:
  stage: deploy
  script:
    - npm install
    - ./node_modules/gulp/bin/gulp.js deploy
  only:
    refs:
      - main

Ahora subimos todos los cambios que hemos realizado a nuestro repositorio y podremos hacer la comprobación.

Flujo de trabajo

Ahora que tenemos todo preparado veamos como funciona el flujo de trabajo con toda esta nueva configuración.

Cuando hagamos cambios en nuestro tema, podremos crear los commits necesarios para luego hacer un push. Cuando se hace un push se ejecutará automáticamente la tarea build para construir todo el tema.

Si tras el push vamos a nuestro repositorio en GitLab en el apartado Build - Pipelines podremos ver que se está ejecutando una tarea:

Se encuentra en la primera etapa:

Se irán ejecutando las diferentes etapas:

Cuando finalice, podremos ver las tres etapas en verde:

Y los cambios se habrán publicado en nuestro Ghost directamente. 🥳

Fuente: Wolfgang Rittner Blog


Más sobre ./voidNull

Haz que cada palabra cuente: tu donación nos inspira a seguir creando contenido. Accede al apartado de Donación para hacer tu aportación