Automatiza la publicación y versionado de tus proyectos JavaScript

por Ricardo Vega el 08 de octubre, 2020
Etiquetas:
ProgramaciónJavascript
8 minutos.

Hace relativamente poco escribía sobre por qué creo que automatizar tareas es una buena inversión y, continuando en parte ese tema, hoy me gustaría enseñarte un ejemplo práctico sobre cómo aplico personalmente esa automatización a mis proyectos JavaScript dentro de Github.

TLDR

Empleo una configuración basada en diferencias herramientas open source como commitlint, husky y semantic-release junto con una convención a la hora de escribir los commits con la que consigo de forma automatizada compilar, probar y publicar nuevas versiones de mis librerías en NPM cada vez que hago un push en la rama master.

Además, genero una versión nueva empleando la convención de semver y un changelog con los cambios más relevantes también de forma automatizada, lo cual también se publica en Github tanto como etiqueta como release.

Escribiendo commits de forma estándar

Todo este proceso se apoya fundamentalmente en emplear una convención semántica a la hora de escribir las versiones de la aplicación. Concretamente, se emplea semver o versionado semántico que, muy a grandes rasgos, define que los números de versión serán X.Y.Z donde:

  • X es la versión MAJOR. Versión principal que presenta unas funcionalidades y no tiene por qué guardar continuidad con otras versiones MAJOR.
  • Y es la versión MINOR. Versión menor fruto de añadir funcionalidad adicional a la versión MAJOR. Esta funcionalidad no "rompe" lo que había antes de forma que si antes usabas la 1.1.0 y hay una versión 1.2.0, ésta no debería tener cambios en la forma de ser usada, simplemente cosas nuevas que podrás usar o no.
  • Z es la versión PATCH. Correcciones de bugs detectados. La 1.0.0 y la 1.0.1 principalmente son la misma versión, con las mismas funcionalidades y API pero la última presenta una serie de correcciones sobre la anterior.

y todas ellas son un número mayor o igual que 0, siendo la primera versión estable de nuestra aplicación la 1.0.0. Es importante destacar que estos número no acaban en 9, es decir, que es perfectamente posible encontrarnos con versiones 2.45.123.

Siguiendo esta convención conocida, una persona externa al proyecto puede comparar dos versiones de nuestra librería y saber qué implicaciones tiene a nivel de integración en su aplicación / producto, detectando qué cambios de versión son "seguros" (puede simplemente cambiar de versión y no necesita modificar nada en su código porque la librería funciona igual) [MINOR y PATCH] y cuáles no [MAJOR].

Como mantenedores de una librería debemos conocer aquellos cambios que hemos realizado desde la última versión para poder marcar correctamente la nueva versión. Aunque no es un proceso excesivamente complejo si se hace con cuidado, podemos automatizarlo a través del formato de los commits que hacemos.

Si no sabes lo que es un "commit", este puede ser un buen punto de partida.

Para ello, debemos seguir una estructura concreta a la hora de escribir nuestros commits, etiquetando cada cambio de forma que una herramienta automatizada pueda catalogarlos correctamente y generar la nueva versión. En mi caso, la estandarización que empleo a la hora de escribir mis commits es Conventional Commits en su versión por defecto.

Además, uso un "linter" que se encarga de comprobar, cada vez que hago un commit, que dicho mensaje de commit sigue dicha estructura. Para ello empleo commitlint como "linter" y husky para ejecutar la comprobación automáticamente en cada commit.

Para ello, instalo las siguientes dependencias:

1yarn add --dev @commitlint/{config-conventional,cli}
2yarn add --dev husky

Creo el fichero commitlint.config.js en la raíz de mi proyecto.

1module.exports = { extends: ["@commitlint/config-conventional"] }

y modifico mi fichero package.json añadiendo lo siguiente:

1"husky": {
2 "hooks": {
3 "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
4 }
5 },

Versionado y publicación automática con Integración Continua.

A muy grandes rasgos, la integración continua es una práctica de ingeniería del mundo del "software" donde se emplea un servicio o servidor para ejecutar de forma automatizada una serie de acciones a nivel de proyecto e independientemente del desarrollador.

Es el lugar ideal donde realizar nuestras acciones automatizadas de versionado y publicación. En mi caso, empleo Github Actions por lo que los pasos que voy a describir son para este servicio, pero verás que los puedes adaptar de forma sencilla si empleas otro sistema de integración continua.

Para todo el proceso de versionado y publicación automática voy a emplear la herramienta semantic-release.

Empezamos ejecutando los siguientes comandos:

1npx semantic-release-cli setup
2yarn add --dev @semantic-release/{changelog,git}

Estoy suponiendo que el proyecto ya lo tienes publicado en Github.

El primero, nos pedirá una serie de datos como nuestros credenciales de NPM y Github y, a continuación nos pedirá que indiquemos qué sistema de integración continua empleamos. Como comentaba arriba, en mi caso, Github Actions. Estas operaciones generarán un token de NPM y lo añadirá al proyecto en Github como secreto disponible al ejecutar las acciones. Todo este proceso lo lleva a cabo la herramienta de forma automática.

Posteriormente, deberemos crear el fichero .releaserc en la raíz de nuestro proyecto para indicar la configuración de semantic-release.

En mi caso, con los plugins que tengo, la configuración es la siguiente:

1{
2 "plugins": [
3 "@semantic-release/commit-analyzer",
4 "@semantic-release/release-notes-generator",
5 "@semantic-release/changelog",
6 "@semantic-release/npm",
7 [
8 "@semantic-release/git",
9 {
10 "assets": ["package.json", "CHANGELOG.md"],
11 "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
12 }
13 ],
14 "@semantic-release/github"
15 ]
16}

Estos plugins, analizan los commits, generan las notas notas de la nueva versión y el registro de cambios (CHANGELOG) y, por un lado publican el artefacto generado en NPM y por otro como nueva release en Github.

Además, los ficheros package.json y CHANGELOG.md que son editados de forma automática por semantic-release, son añadidos al repositorio.

Como último paso, nos faltaría configurar Github Actions para que ejecute esta operación de forma automáticamente cada vez que hacemos push al repositorio. Esta configuración será muy específica de tu proyecto pero te dejo como ejemplo la que estoy usando yo en un proyecto personal: scribere.

Aunque tengo pendiente hablar de este proyecto en el blog, puedes echarle un ojo si te interesa aquí

1name: Release
2
3on:
4 push:
5 branches: [master]
6 pull_request:
7 branches: [master]
8
9jobs:
10 build:
11 runs-on: ubuntu-latest
12
13 steps:
14 - uses: actions/checkout@v2
15 - name: Use Node.js ${{ matrix.node-version }}
16 uses: actions/setup-node@v1
17 with:
18 node-version: 12
19 - name: Install dependencies
20 run: yarn install --frozen-lockfile
21 - name: Build
22 run: yarn build
23 - name: Tests
24 run: yarn test
25 - name: Release
26 env:
27 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
29 run: yarn semantic-release

Esta sería la configuración de mi fichero .github/workflows/ci.yaml. Como verás, emplea yarn en vez de npm y sólo se ejecuta en unas ramas en concreto.

Aunque no es específico del versionado y publicación automatizada, un posible error con el que te puedes encontrar si es la primera vez que trabajas con Github Actions es que no te deje subir el fichero YAML que acabamos de ver si no tienes permisos para ello.

Esto puede ocurrir cuando el repositorio lo has clonado por HTTP y estás empleando de forma directa tus credenciales en formato de usuario / password en Github. Para solventarlo, necesitarías o bien clonar el repositorio por SSH y añadir la clave SSH de tu equipo a Github (lo cual te recomiendo) o bien loguearte con usuario / token y que este token tenga permisos para editar acciones.

Conclusiones

Automatizar la publicación y versionado de aplicaciones puede simplificarte mucho la vida y ahorrarte gran número de errores (y tiempo) si eres disciplinado a la hora de generar tus commits. Sin duda, es una práctica que te recomiendo seguir y, si bien la primera vez puede resultar algo confuso, una vez que lo has hecho una vez, es simplemente repetir el proceso en tus diferentes proyectos.

Aunque en este caso lo hemos hecho para un proyecto open source, el proceso sería prácticamente idéntico si tu repositorio es privado con las restricciones que te marcarían tus proveedores (por ejemplo Github y NPM).

Espero que esta pequeña guía te facilite su implementación en tus proyectos si no lo estás usando ya. Si en vez de Github y NPM eres usuario de Gitlab y Artifactory (por ejemplo) el proceso es prácticamente idéntico pero reemplazando los plugins correspondientes (por ejemplo el de Github por Gitlab).

Puedes encontrar más información en el github de semantic-release. También te dejo la publicación que seguí yo inicialmente como guía (🇬🇧) por si te puede servir de ayuda adicional.

Un saludo,

Ricardo

Discutir en Twitter

Compartir artículo
Ricardo Vega es un desarrollador "full-stack" al que le gusta "cacharrear con todo" pero está especializado sobre todo en tecnologías Javascript, principalmente en React. Intenta devolver a Internet lo que Internet le ha dado.

Sigue leyendo 😀


Apoya al blog


Si te ha gustado este artículo, valora apoyarme económicamente a través de Patreon, una plataforma de Micro-mecenazgo con la que puedes hacerme un donativo que ayude a la continuidad del blog. Una pequeña ayuda significa mucho.

Permanezcamos en contacto!


¿Quieres enterarte de todas las novedades del sector? ¿Te gustaría trabajar conmigo? ¡Puedes contactar conmigo de forma muy sencilla!

@ricveal

ricardo.vega@ricveal.com

Ricardo Vega