Skip to content

Commitlint

Commitlint es la única de estas herramientas que no mira tu código: mira el mensaje de tus commits. Su trabajo es rechazar el commit si el mensaje no sigue la convención acordada.

a1b2c3d cambios
d4e5f6a fix
7g8h9i0 ya funciona
1j2k3l4 subiendo lo de ayer

El segundo historial se puede leer, filtrar (git log --grep "feat(checkout)") y usar para generar un changelog automáticamente. El primero no sirve para nada.

La convención que usamos se llama Conventional Commits. Un mensaje válido tiene esta forma:

tipo(ámbito): descripción
[cuerpo opcional]
[pie opcional]
  1. tipo — qué clase de cambio es. Es obligatorio y solo puede ser uno de una lista cerrada.

  2. (ámbito) — a qué parte del proyecto afecta. Va entre paréntesis. En Alebat es obligatorio.

  3. : + espacio — separador. Literalmente dos puntos y un espacio.

  4. descripción — qué has hecho, en una línea.

TipoCuándo se usa
featUna funcionalidad nueva para el usuario.
fixLa corrección de un bug.
docsCambios solo en documentación.
styleFormato, espacios, comas. Nada que cambie el comportamiento.
refactorReescritura de código que no añade funcionalidad ni corrige bugs.
perfUn cambio cuyo objetivo es mejorar el rendimiento.
testAñadir o corregir tests.
buildCambios en el sistema de build o en dependencias.
ciCambios en la configuración de integración continua (workflows, pipelines).
choreTareas de mantenimiento que no encajan en nada de lo anterior (releases, etc.).
revertRevertir un commit anterior.
✅ feat(checkout): añadido el selector de método de pago
✅ fix(i18n): corregida la traducción del botón de compra en catalán
✅ docs(coemCourses): añadida documentación de los cursos de COEM
✅ chore(release): release 1.2.0
❌ arreglado el bug del carrito → falta tipo y ámbito
❌ fix: corregido el carrito → falta el ámbito (scope-empty)
❌ Fix(cart): corregido el carrito → el tipo va en minúscula
❌ feat(cart):añadido el cupón → falta el espacio tras los dos puntos
❌ update(cart): añadido el cupón → 'update' no es un tipo permitido

Commitlint no se ejecuta solo: lo lanza el hook commit-msg de Git, que instala Husky. Ese hook contiene una única línea:

.husky/commit-msg
npx --no -- commitlint --edit "$1"
  • $1 es la ruta al archivo temporal donde Git ha guardado el mensaje que acabas de escribir (normalmente .git/COMMIT_EDITMSG).
  • --edit le dice a Commitlint que lea el mensaje de ese archivo.
  • --no evita que npx intente descargar el paquete de internet: si commitlint no está instalado en el proyecto, falla en lugar de instalarlo por su cuenta.

Si Commitlint devuelve un error, el hook devuelve un código de salida distinto de cero y Git aborta el commit. Tu mensaje no se pierde: se queda en .git/COMMIT_EDITMSG y puedes recuperarlo con git commit -e -F .git/COMMIT_EDITMSG.

⧗ input: fix: corregido el carrito
✖ scope may not be empty [scope-empty]
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

Entre corchetes aparece el nombre de la regla que has incumplido, que es la forma rápida de saber qué corregir.

En Alebat las reglas no se escriben en cada proyecto. Viven en el paquete @alebat/default-alebat-config y cada repositorio las importa en tres líneas:

commitlint.config.ts
import { commitLintFront } from '@alebat/default-alebat-config'
export default commitLintFront

El análisis completo de esa configuración está en default-alebat-config.

Terminal window
# Validar un mensaje concreto
echo "feat(cart): añadido el cupón" | npx commitlint
# Validar los mensajes de los últimos commits de tu rama
npx commitlint --from HEAD~5 --to HEAD --verbose

Esto último es útil antes de abrir un Pull Request, para asegurarte de que todo el historial de la rama es válido.