Mantener CSS compilado fuera de tu repositorio git al usar LESS/SASS con Grunt y hooks

Ten un repositorio git ordenado y liviano al excluir los CSS compilados y regenerarlos automáticamente con Grunt y hooks de git.

Una de las desventajas de trabajar con pre-procesadores (ya sea LESS, SASS u otros) es que los archivos producidos pueden generar mucho ruido en el historial de versiones de tu proyecto, lo que es especialmente crítico si estás compilando a archivos minificados o a varias cascadas a partir de un set de variables.

Por ejemplo, si cambias una variable que define un color utilizado en muchos lugares, tu historial mostrará no solamente ese cambio sino también todas las líneas del CSS donde cambió ese valor. Luego, al revisar cambios anteriores se hace mucho más difícil poder determinar fácilmente cuáles son cambios son relevantes (el cambio en el archivo fuente LESS/SASS) de los que no (todas las líneas modificadas en el CSS resultante).

Por ello, una práctica recomendada es versionar los archivos LESS/SASS pero no el CSS resultante, ya que estos últimos son solamente el producto del proceso de compilación de las fuentes.

Para que tu CSS refleje fielmente los cambios en LESS/SASS, puedes automatizar la compilación con una mezcla de tareas en Grunt y hooks de git.

Git hooks

Los hooks de git (o “puntos de enganche” según la traducción al español) permiten ejecutar acciones ante determinados eventos ligados al control de versiones, por  ejemplo, al hacer commit, compartir cambios (push), recibir cambios, etc.

Los scripts que se ejecutan son específicos de cada repositorio, lo que si bien dificulta un poco poder compartirlos (aunque para ello basta versionarlos en otra carpeta de tu proyecto), permite definir flujos separados para tareas de desarrollo y producción. Por ejemplo, en desarrollo puedes generar CSS sin minificar y con sourceMap, mientras que en producción usar versiones minificadas y sin sourceMap.

Como se trata de shell scripts, puedes ejecutar prácticamente cualquier cosa que tenga una interfaz por línea de comandos, pero en lo particular me resulta especialmente útil ejecutar tareas de Grunt.

Por ejemplo, este es el script que uso en el hook post-merge en un proyecto en desarrollo

#!/bin/sh
if ! git diff --quiet @{-1} HEAD front/less/**/*.less
then
    grunt less:development
fi

Lo que hace es bastante sencillo:

  • Detecta si hay algún cambio en cualquier archivo .less  de la carpeta front/less o cualquiera de sus subdirectorios entre la última versión de trabajo (o sea, “mi” versión) y la última versión que estoy incorporando
  • Si hay cambios, ejecuta el comando grunt less:development, que corresponde a una tarea definida con Grunt.

Este mismo principio se puede aplicar para mantener fuera del control de versiones todos los recursos que son administrados con algún gestor de paquetes (Bower, npm, Composer), de modo de mantener un repositorio bien ordenado y muy rápido de clonar.