¿Qué es Husky y cómo configurarlo en tu proyecto Git?
Guía práctica para configurar Husky en tu proyecto y aprovechar los hooks de Git para automatizar tareas como validación de commits y ejecución de linters.
¿Alguna vez te ha pasado que alguien hace un commit sin pasar los linters o rompe el código porque no corrió las pruebas?
Aquí es donde entra Husky, una herramienta que ayuda a mantener tu proyecto limpio y saludable desde el momento en que alguien hace un commit.
Antes de comenzar, activaremos algunos conceptos sobre los hooks de Git.
¿Qué es un hook en Git?
Un “Git Hook” es un script peronalizado que puedes ejecutar en respuesta a eventos específicos en el ciclo de vida de Git. Estos eventos pueden ser acciones como:
- Realizar un
commit
- Empujar cambios a un repositorio remoto con
push
- Fusionar ramas con
merge
Existen más pero normalmente esos eventos son los más frecuentes.
Git proporciona una serie de hooks predefinidos, y puedes personalizarlos para satisfacer las necesidades de tu flujo de trabajo. Algunos de los hooks más comunes son:
pre-commit
: Se ejecuta antes de confirmar los cambios (commit
). Puedes usarlo para realizar tareas como ejecutar pruebas automáticas o comprobar la calidad del código.pre-push
: Se ejecuta antes de empujar los cambios al repositorio remoto. Puedes realizar verificaciones adicionales antes de enviar los cambios al servidor.post-commit
: Se ejecuta después de que se ha confirmado un cambio. Puedes usarlo para realizar tareas adicionales después de que se ha realizado un commit.post-receive
: Se ejecuta en el repositorio remoto después de recibir nuevos cambios. Puede ser útil para realizar acciones en el servidor después de que se hayan empujado cambios en el repositorio remoto.
Para aprovechar los hooks en un repositorio Git, debes escribir scripts personalizados y colocarlos en la carpeta .git/hooks/
del repositorio. Git utiliza estos scripts automáticamente en función de los eventos correspondientes. Observa la siguiente demostración:
Demostración básica de hooks nativos de Git
Ventajas y Desventajas de usar los hooks nativos de Git
Git incluye de forma nativa el sistema de hooks. Estos hooks son altamente personalizables y potentes, lo que los hace ideales para automatizar tareas como validación de código, formateo o verificación de mensajes de commit.
Sin embargo, aunque los hooks nativos son una herramienta poderosa, también presentan ciertas limitaciones, especialmente cuando se trabaja en equipos o proyectos que requieren consistencia y facilidad de configuración. A continuación, se detallan las principales ventajas y desventajas:
Ventajas
Ventaja | Explicación |
---|---|
Sin dependencias externas | No necesitas instalar nada adicional. Git ya incluye soporte para hooks en .git/hooks/ . |
Rendimiento ligeramente mejor | Al no depender de Node.js o npx , puede ser un poco más rápido. |
Más control | Puedes escribir los scripts en cualquier lenguaje soportado por tu sistema (bash, Python, etc.). |
Menos archivos en el proyecto | No necesitas carpetas como .husky/ o paquetes npm relacionados. |
Desventajas
Desventaja | Explicación |
---|---|
No se comparten por defecto | Los hooks de Git están en .git/hooks/ , que está fuera del control de Git. Por lo tanto, no se versionan ni se comparten entre colaboradores. |
Mantenimiento manual | Si quieres cambiar los hooks, cada miembro del equipo tiene que copiar los nuevos manualmente. |
Difícil de automatizar | Requiere scripts adicionales para propagar hooks (por ejemplo, usando plantillas de repositorio o scripts post-clone). |
Menor integración con npm | No es tan fácil ejecutar scripts definidos en package.json . |
Aunque los hooks nativos de Git son poderosos, al trabajar en equipo presentan más desventajas que ventajas, especialmente cuando se comparan con herramientas como Husky, que facilitan la gestión compartida y reproducible de hooks.
Husky es una herramienta de JavaScript que te permite agregar fácilmente Git hooks a tu proyecto.
¿Qué es Husky?
Husky es una librería que hace más fácil ejecutar automáticamente comandos o scripts en momentos específicos durante el desarrollo de proyectos.
Algunas características clave de Husky:
- Fácil configuración: Husky simplifica la configuración de hooks de Git mediante la definición en la sección de scripts en un archivo
package.json
. Esto facilita la comprensión y mantenimiento de los hooks en un proyecto. - Integración con comandos npm: Husky se integra normalmente con los comandos de npm, lo que significa que puedes usar todos los scripts definidos en el archivo
package.json
directamente como acciones para los hooks de Git. - Soporte de varios hooks: Husky es compatible con una variedad de hooks de Git, como
pre-commit
,pre-push
,post-merge
, entre otros. Esto permite ejecutar acciones personalizadas en diferentes etapas del ciclo de vida de Git. - Instalación automática de hooks: Husky puede configurar automáticamente los hooks de Git durante la instalación, eliminando la necesidad de configuración manual y mejorando la consistencia y coherencia en los equipos de desarrollos.
Preparar el terreno
Antes de comenzar con la instalación y configuración de Husky, asegúrate de que tienes git
y Node.js
instalados:
mcherrera@dev:~$ git --version
2.43.0
mcherrera@dev:~$ node --version
v22.17.1
1. Inicializar un proyecto
En caso de que no tengas un archivo package.json
, puedes crearlo con el siguiente comando:
1
npm init -y
2. Instalar e inicializar Husky
A continuación, instalamos Husky como dependencia de desarrollo ejecutando:
1
npm install --save-dev husky
Para habilitar e iniciar la configuración de Husky en el proyecto, debemos ejecutar el siguiente comando en la terminal:
1
npx husky init
El comando
init
simplifica la configuración de husky en un proyecto. Crea un scriptpre-commit
en el directorio.husky/
y actualiza el script prepare enpackage.json
.
Observa que ahora, en el archivo package.json
, se ha agregado el script prepare:
1
2
3
"scripts": {
"prepare": "husky install"
}
Este script se ejecutará automáticamente después de que alguien corre
npm install
.
3. Validar mensajes de commit
Hasta ahora, ya tenemos una configuración básica de Husky para ejecutar hooks de Git en nuestro proyecto. Pero tener hooks por sí solos no basta, necesitamos herramientas que realicen tareas concretas durante esos hooks.
Una de esas tareas clave es validar los mensajes de commit. Aquí es donde entra en juego Commitlint
.
Commitlint es una herramienta que verifica que tus mensajes de commit
sigan un formato establecido.
Ahora, necesitamos instalar las siguientes dependencias:
1
npm install --save-dev @commitlint/{cli,config-conventional}
1
npm install --save-dev @commitlint/cli @commitlint/config-conventional
@commitlint/config-conventional
: Esta dependencia proporciona una configuración predefenida para Commitlint basada en las convenciones de Conventional commit para loscommit
.@commitlint/cli
: Esta dependencia es la interfaz de línea de comandos para CommitLint. Proporciona herramientas para ejecutar la validación de mensajes decommit
de acuerdo con las reglas establecidas en la configuración de Commitlint. Puedes usar este CLI (Command Line Interface) para verificar si tus mensajes decommit
cumplen con las convenciones configuradas.
Hasta aquí, dentro del archivo package.json, la sección de dependencias de desarrollo (devDependencies) debería incluir las herramientas que hemos instalado, como Husky y Commitlint, y lucir más o menos así:
1
2
3
4
5
"devDependencies": {
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"husky": "^9.1.7"
}
Algunas de las principales reglas del estándar son las siguientes:
chore
: Cambios en tareas, configuración, y otros aspectos relacionados con el mantenimiento del proyecto.docs
: Cambios en la documentación.feat
: Nuevas características.fix
: Correciones de errores.style
: Cambios que no afectan el significado del código (espacios en blanco, formato, punto y coma que faltan, etc.).test
: Añadir o modificar pruebas.
En la raíz de tu proyecto, crea un nuevo archivo llamado .commitlintrc.json
:
1
touch .commitlintrc.json
1
type nul > .commitlintrc.json
Abre ese archivo, y agrega la configuración básica para decirle a Commitlint que use las reglas estándar del Conventional Commits para validar los mensajes:
1
2
3
{
"extends": ["@commitlint/config-conventional"]
}
"extends": ["@c.../conf..."]
: Esto indica que estás extendiendo de la configuración basada en el estándar decommits
Conventional Commits
Una vea creado y configurado el archivo .commitlintrc.json
, el siguiente paso definir el hook de Git en husky, específicamente en el hook commit-msg
:
1
2
3
touch .husky/commit-msg
chmod +x .husky/commit-msg
Este comando, crea el archivo para el hook y le asigna permisos de ejecución (importante ese segundo paso).
Y pega el siguiente comando, y así Commitlint pueda validar automáticamente el mensaje del commit:
1
npx --no-install commitlint --edit "$1"
Cuando Git ejecuta el hook commit-msg
, le pasa como argumento ($1
) el archivo que contiene el mensaje del commit. Ese archivo es temporal y lo usa Git antes de finalizar el commit.
Entonces, este comando le dice a Commitlint:
“Lee el mensaje de este commit desde el archivo
$1
, y valida si cumple con las reglas definidas en.commitlintrc.json
.”
Observa la siguiente simulación, donde trataremos de realizar un commit
que no cumple con las convenciones:
mcherrera@dev:~$ git status -s
A .gitignore
?? .commitlintrc.json
?? .husky/
mcherrera@dev:~$ git commit -m "add gitignore"
⧗ input: add gitignore
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
Como puedes ver, Husky junto con Commitlint nos han detenido y nos invitan amablemente a revisar la página oficial de Commitlint en GitHub para conocer los formatos de mensajes de commit válidos.
Readme de Commitlint en GitHub
Entonces, esto significa que la primera palabra del mensaje de commit debe coincidir con uno de los tipos definidos en el estándar Conventional Commits. Además, puedes indicar opcionalmente el (scope), que va entre paréntesis, indicando el módulo o componente en el que estás trabajando.
Para el caso anterior, para pasar la validación, debemos respetar la convención:
mcherrera@dev:~$ git commit -m "chore(gitignore): agregado nueva entrada"
[main c537a0e] chore: add gitignore
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
¿Cómo sobrescribir o extender las reglas?
En algunos casos, necesitamos añadir o modificar algunas de las reglas existentes, y para llevar a cabo este proceso, se puede añadir una sección "rules"
justo después de "extends"
para personalizar o desactivar reglas según tus necesidades. Cada regla se define mediante un arreglo que contiene:
Nivel:
0
: desactiva la regla.1
: advertencia (warning).2
: error (fail si no se cumple).
Condición:
"always"
: siempre debe cumplirse."never"
: no debe cumplirse.
Valor:
Puede ser un número, un formato de texto, un array con valores válidos, etc., dependiendo de la regla.
Cómo funciona esto
Commitlint combina primero las reglas del paquete extendido (@commitlint/config-conventional
) y luego aplica las reglas personalizadas definidas en "rules"
, sobrescribiendo cualquier valor previo. Esto te permite mantener la configuración base y ajustarla a tus necesidades de forma controlada.
1
2
3
4
5
6
7
8
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"header-max-length": [1, "always", 30],
"scope-case": [2, "always", "lower-case"],
"subject-full-stop": [2, "never", "."]
}
}
Detalle de la regla:
1
"header-max-length": [1, "always", 30]
Esta regla establece que el encabezado del commit y que no debe exceder los 30 caracteres. El nivel 1 significa advertencia (warning): si se supera, Commitlint lo señalará, pero no bloqueará el
commit
.
1
"scope-case": [2, "always", "lower-case"]
Esta regla exige que el scope esté siempre en minúsculas (lower-case).
El nivel 2 indica que se considera un error; el commit será rechazado si no se cumple.
1
"subject-full-stop": [2, "never", "."]
- Esta regla se encarga del
subject
del mensaje (la parte después de type(scope):).- Esta regla prohíbe que el subject termine con un punto (.).
- Si el mensaje finaliza con un punto, Commitlint lo marcará como error y no permitirá el commit.
En esta guía práctica configuramos Husky y Commitlint para aplicar convenciones estrictas en los mensajes de commit, garantizando consistencia y claridad en el historial de Git.
Finalmente, configuramos el hook commit-msg
de Git en Husky para que ejecute commitlint cada vez que se realiza un commit
, asegurando que todos los mensajes cumplan las reglas establecidas.