Lerna + husky + commitizen + commitlint

Joel H. Gomez Paredes
6 min readMar 14, 2021
Larry langosta viendo los músculos de bob esponja
Cielos que macizo

Hace unos días, casualmente un viernes de deployment (me gusta vivir al límite) un amigo del trabajo al que llamaremos señor C(me cae bien a pesar de que hace Java) quería agregar a uno de sus proyectos commitizen, normalmente esto es algo “simple” de hacer pero hubo ciertos inconvenientes que complicaron esta integración.

Adicionalmente se decidió hacer el uso de husky y commitlint (lerna también).

Estas herramientas ya habían sido utilizadas en otro proyecto (con excepción de lerna) lo cual podría ser utilizado como base pero en esta aventura aprendimos a tomar en cuenta ciertos aspectos al momento de utilizarlas:

  1. Estructura de su repositorio: Monorepo o multirepo.
  2. Tipo de dependencia: El alcance de la tarea de la dependencia.
  3. Versión: El software cambia.

Estructura

El ejemplo que teníamos como base es un repositorio que corresponde a un solo proyecto pero el repositorio al que se quería adaptar es un monorepo, entonces tenemos varios proyectos. Esto complica un poco las cosas por las siguientes preguntas: ¿Dónde debo instalar husky, commitizen y commit-lint? ¿Quizás debería tener un proyecto que sea únicamente para instalar esas dependencias? ¿Quién inventó la mayonesa?

Esto nos lleva al siguiente punto

Tipo de dependencia

Algunas dependencias corresponden a lo que tiene que ver con nuestro proyecto y cómo trabaja, otras nos ayudan en la parte de calidad como linters o test runners, las cuales son normalmente dependencias de desarrollo y cuyo alcance sólo afecta a tu proyecto, pero hay otras dependencias como husky cuyo alcance no es a nivel proyecto sino repositorio.

En el enfoque de repositorio por proyecto no tenemos problemas, lo ponemos como dependencia de desarrollo y se acabó el problema, pero en un monorepo las cosas cambian.

Porque estas solo deberían y deben ser instaladas desde un solo lugar

Así que consideramos los siguientes puntos

  • Dejando a un lado el conflicto que le causa al Señor C tener package.json en diferentes niveles, la razón de esto no es que el Señor C sea un cobarde (espero que este leyendo esto XD) sino que hay escenarios bastante comunes donde triunfa el mal y las dependencias de los proyectos internos chocan con las dependencias en un proyecto que está en un nivel jerárquico más alto(carpeta)
  • Crear un proyecto dummy para estas herramientas: husky puede ser configurable para instalarse y commitizen puede configurarse para hacer la búsqueda de ciertos paquetes que necesita para funcionar

Así que nos decidimos por el primer punto, porque ya hay muchos proyectos que se manejan con esa estructura y nos hizo sentido que estén en la raíz del repositorio porque su enfoque es a nivel repositorio, en este caso nos arriesgamos, ¿esto puede traer problemas? Si pero toda decisión en desarrollo de software nos trae inconvenientes así que elegimos lidiar con esto.

Homero Simpson causando problemas al viajar en el tiempo
Esto se va a poner feo XD

Que Joel del futuro se preocupe

Ya con esto en mente decidimos crear el proyecto a nivel repositorio y comenzar, tomamos como base el ejemplo ya hecho pero por alguna razón los hooks de husky no estaban funcionando lo que nos lleva al siguiente punto.

Versión

El software tiende a cambiar y husky no es la excepción nuestro proyecto base estaba usando husky 4.x.x y la versión que instalamos en el proyecto del señor C era la versión 5.x.x.

Sin más premura nos bajamos a la 4 … no espera no hicimos eso XD, usamos la 5.x.x la cual como el número lo indica(chequen semver si no entienden) agrega cambios que rompen o cambian completamente la forma de uso anterior.

Manos a la obra

Bob esponja emocionado

Para este tutorial voy a usar mi repositorio de ejemplos de google maps, lo único adicional que voy a hacer es agregar lerna porque es una buena herramienta para la gestión de paquetes/proyectos node/js y porque es mi repositorio y quiero hacerlo.

El proyecto antes de las modificaciones luce así(puedes recrear esta estructura para seguir este post)

Para iniciar con esto voy a crear una rama que se llame repo-management

git checkout -b repo-management

Después iniciaremos lerna, para esto nos ubicamos en la raíz del proyecto y ejecutamos el comando

lerna init --independent

Este comando agrega lerna.json con la configuración que necesitamos (tengo otro post donde explico más de lerna) y el package.json (ojo no instala node_modules ni nada esto será usando otro comando). Husky ya tiene un comando para hacer la configuración automática pero vamos a hacerlo paso a paso para no perder ningún detalle.

Archivos package.json y lerna.json

Husky

Instalamos husky con el siguiente comando

npm i -D husky

Después debemos hacer el setup

npx husky install

Al ejecutar el comando anterior se crea una carpeta llamada .husky

Folder .husky creado y dependencia instalada

Ahora el siguiente paso es agregar un script en el package.json para que haga el setup automático cuando hacemos npm install en el proyecto raíz

script “prepare”: “husky install”

Hasta ahora no tenemos ningún hook configurado, lo haremos después de agregar nuestra siguiente herramienta.

Commitizen

Commitizen es una herramienta que nos ayuda a escribir los mensajes de un commit siguiendo una convención llamada conventional commits, aquí pueden ver un post que tengo sobre ese tema (que bonito es cuando todo lo que haces tiene sentido XD)

El primer paso es instalar commitizen

npm i -D commitizen

El siguiente comando instalará el adaptador a define las reglas a utilizar por commitizen al mostrar el prompt para capturar nuestro commit y agrega configuración adicional

npx commitizen init cz-conventional-changelog --save-dev --save-exact
Configuración del path del changelog en para commitizen

Como siguiente paso vamos a agregar commitizen al workflow del comando git commit, para que al ejecutar git commit se despliegue commitizen.

Para esto debemos decirle a husky que agregue el hook prepare-commit-msg con el siguiente comando

npx husky add .husky/prepare-commit-msg "exec < /dev/tty && node_modules/.bin/cz --hook || true"

Nota: La documentación aún no se ha actualizado y muestra un uso distinto con husky

Para este momento si ejecutas git add y un git commit podrás ver algo similar a esto

commitizen prompt

Commitlint

Commitizen nos brinda una mejor experiencia al momento de escribir nuestros commits pero si usamos el comando git commit -m “mensaje” sin seguir el formato y damos ctrl + c ocultará el prompt y el commit se guardará (creo que no queremos eso), para evitar esto vamos a utilizar commitlint

Instalando commitlint y la convención a utilizar

npm install -D @commitlint/{config-conventional,cli}

Creando configuración de commitlint

echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js
Archivo commitlint.config.js

Para finalizar con la configuración vamos a agregar el hook commit-message para que el mensaje sea validado antes de guardar el commit

npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
commit-msg hook
Error al tratar de hacer un commit que no siga la convención

Setup al clonar

Y con esto ya casi hemos terminado, ahora solo falta agregar un comando central el cual solo utilizaremos para inicializar los proyectos agregar las dependencias de nuestros proyectos y las dependencias del proyecto raíz, en el package.json agregamos el siguiente script

"setup": "npm install && lerna bootstrap"

Al clonar este comando instalamos dependencias del proyecto raíz, se ejecutará la instalación de los hooks de husky e instalaremos las dependencias de todos los paquetes dentro de package.

Clona mi repositorio y ejecuta npm run setup, haz un cambio y comprueba que funciona :)

Espero les haya gustado y nos veremos en futuros cambios XD

--

--

Joel H. Gomez Paredes

Frontend Developer at Platzi, Google Developer Expert in Web Technologies And Google Maps Platform, A Reverse Developer & Fullsnack JS Developer