Cuando entregamos software a nuestros usuarios, testers u otros desarrolladores es importante proporcionar una lista de cambios incorporados, errores solucionados y tareas secundarias relevantes para el lanzamiento de nuestras aplicaciones, juegos, servicios o bibliotecas.
El Changelog es una herramienta relevante para lograr este propósito. Muchos proyectos de código abierto, como Eureka o GRDB.swift, por ejemplo, tienen un Changelog muy completo y detallado, y en ambos casos aportan mucha información al respecto.
Nota: No estoy diciendo que Eureka y GRDB.swift generen su Changelog usando esta técnica, pero sí es importante notar la relevancia que el registro de cambios tiene para ambos proyectos.
Qué es Conventional Commits
Es en este punto donde cabe hablar de Conventional Commits, una especificación pensada para armonizar los mensajes de commit que escribimos para mejorar su legibilidad y, al mismo tiempo, permitir la automatización de algunas tareas –sí, tareas como un Changelog, pero de eso hablaremos en un momento.
En resumen, Conventional Commits es una convención simple para escribir mensajes de commit en nuestro sistema de control de cambios preferido, Git. Se trata de cambiar nuestros mensajes de commit para evitar incluir mensajes de commit simples y sin estructura como estos
Fix crash on login
Cómo se usa
Dentro de la especificación de Conventional Commits se señala que la estructura básica de un commit debe ser
<tipo>[alcance opcional]: <descripción>
[cuerpo opcional]
[footer(s) opcional(es)]
El ejemplo anterior, siguiendo las convenciones de Conventional Commits y con una explicación adicional quedaría de la siguiente forma:
fix: the application will no longer crash on login with invalid email addresses
The application was trying to force the user input as an email by validating it using a
RegEx but the exceptions were not correctly handled
Y aunque este commit de ejemplo está omitiendo mostrar otras características de Conventional Commits como un footer, los ejemplos adicionales, mucho más completos, se pueden consultar en el sitio https://www.conventionalcommits.org/en/v1.0.0/#examples.
Integrar Conventional Commits a tu proyecto
Para esta parte de la configuración vamos a utilizar Husky, una herramienta de desarrollo que agrega Git hooks a nuestro proyecto que serán útiles para validar, primero, que nuestros commits cumplan adecuadamente con las reglas de Conventional Commits.
Husky se instala utilizando Node.js, por lo que la integración con proyectos basados en NPM tales
como Angular, backend Node.js y algunos otros es inmediata, pero eso no significa que para un
proyecto Swift, Kotlin o Java sea inservible, simplemente se requiere inicializar un archivo
package.json
para mantener las dependencias de desarrollo del proyecto instaladas.
Si tu proyecto está basado en NPM puedes omitir este paso.
Si no tienes un archivo package.json
puedes inicializarlo en tu directorio principal de trabajo
del repositorio con el comando
npm init -y
Puedes consultar la documentación de NPM para este comando aquí.
El siguiente paso es instalar Husky y las herramientas de Conventional Commits
npm i husky --save-dev
Una vez instalado Husky, habilitamos los Git hooks
npx husky install
Para evitar que otras personas que colaboran en nuestro repositorio deban configurar Husky de forma
manual, podemos habilitar una configuración Post-Install en el archivo package.json
, agregando
la sección de scripts como sigue:
"scripts": {
"postinstall": "husky install"
}
luego de Husky incorporamos las dependencias de commitlint,
npm i --save-dev @commitlint/{config-conventional,cli}
Y agregaremos un archivo .commitlintrc.json
especificando las reglas de validación
de los commits
{
"extends": ["@commitlint/config-conventional"]
}
Finalmente, configuraremos el hook de validación de los commits
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
A partir de este momento, si creamos un nuevo commit con un mensaje sin seguir las reglas de Conventional Commits el Hook de Husky va a regresar un error:
¿Y el Changelog?
En la sección anterior instalamos y configuramos hooks de Git y Commitlint con la configuración Conventional Commits para validar nuestros mensajes de commit. La siguiente etapa en este proceso será instalar una nueva dependencia, Standard Version con la que generaremos un archivo CHANGELOG.md de forma automática, basándonos en la información de nuestros commits, y también manteniendo un rastreo de las versiones lanzadas de nuestro software.
Para instalar la dependencia debemos ejecutar
npm i --save-dev standard-version
Luego, podemos complementar nuestro archivo package.json
con más scripts que serán
útiles en un momento:
"scripts": {
"postinstall": "husky install"
"release": "standard-version",
"release:minor": "standard-version --release-as minor",
"release:patch": "standard-version --release-as patch",
"release:major": "standard-version --release-as major"
},
Y finalmente deberemos crear un archivo .versionrc.json
con el siguiente
contenido –que también puedes editar de acuerdo a tus necesidades:
{
"header": "Changelog",
"types": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "chore", "hidden": true},
{"type": "docs", "hidden": true},
{"type": "style", "hidden": true},
{"type": "refactor", "hidden": true},
{"type": "perf", "hidden": true},
{"type": "test", "hidden": true}
],
"commitUrlFormat": "{{host}}/{{owner}}/{{repository}}/commit/{{hash}}",
"compareUrlFormat": "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}",
"issuePrefixes": ["#"],
"issueUrlFormat": "{{host}}/{{owner}}/{{repository}}/issues/{{id}}"
}
Aquí hace falta hacer algunas sustituciones de acuerdo a tu proyecto, específicamente
en los campos commitUrlFormat
, compareUrlFormat
y issueUrlFormat
. Para un ejemplo
en Github sobre el repositorio demo de este artículo, las URLs se sustituyen como
"commitUrlFormat": "https://github.com/OrlSan/husky-conventional-commits-demo/commit/{{hash}}",
"compareUrlFormat": "https://github.com/OrlSan/husky-conventional-commits-demo/compare/{{previousTag}}...{{currentTag}}",
"issuePrefixes": ["#"],
"issueUrlFormat": "https://github.com/OrlSan/husky-conventional-commits-demo/issues/{{id}}"
Todas las opciones y detalles de uso para este archivo los puedes encontrar directamente en https://github.com/conventional-changelog/conventional-changelog-config-spec/blob/master/versions/2.1.0/README.md#types
En este punto, con todo creado, lo que resta es crear el archivo CHANGELOG
npm run release -- --first-release
Empujando los cambios a nuestro repositorio (sin olvidar empujar los tags también)
con el comando git push --tags
.
Repositorio demo
Si te interesa conocer el repositorio que he usado como ejemplo para este post puedes verlo en https://github.com/OrlSan/husky-conventional-commits-demo
El archivo CHANGELOG.md del proyecto incluye links hacia el commit que resuelve el Issue #3, por ejemplo.
Conclusiones
Agregar y configurar Husky, Commitlint con la configuración Conventional Commits y Standard Version es una tarea un poco complicada. Sin embargo los beneficios a nivel documentación, rastreo de cambios y legibilidad del proyecto no solo para quienes desarrollan el Software sino para usuarios o testers son muchos.
En mi opinión es un aporte muy grande y, en el futuro, esto nos ahorrará la necesidad de escribir mucha documentación respecto a cambios. Tendremos a la vista un archivo CHANGELOG.md con el registro detallado de los cambios y los links a los commits que introducen nuevas características o arreglan errores de forma muy rápida.
Referencias
- Este post sería imposible de haber sido realizado sin el asombroso trabajo y explicación de Michael Hoffman en su artículo How To Automatically Generate A Helpful Changelog From Your Git Commit Messages
- https://www.conventionalcommits.org
- https://github.com/conventional-changelog/standard-version
- https://github.com/conventional-changelog/conventional-changelog-config-spec/blob/master/versions/2.1.0/README.md