Por Raul Padilla
Los linter son herramientas esenciales en proyectos de código, ya que ayudan a mantener una base de código consistente y de alta calidad. Analizan el código en busca de posibles errores, violaciones de estilo y otros problemas, asegurando que el código siga las mejores prácticas y estándares de codificación acordados por el equipo. Al utilizar una herramienta de linter, podemos detectar y solucionar problemas temprano, lo que resulta en un código más limpio y un proceso de desarrollo más fluido. Con la capacidad de aplicar convenciones de codificación, identificar posibles errores y mejorar la legibilidad del código, una herramienta de linter es un activo valioso para cualquier proyecto de código. Al mantener un código consistente y de alta calidad, se facilita la colaboración entre los miembros del equipo, se reducen los conflictos y se mejora la eficiencia en el desarrollo.
Cuando hablamos de linter, nos encontramos con opciones muy populares y extendidas. Podríamos destacar ESLint en JavaScript, entre muchas otras que existan. En este post vengo a contar un caso muy particular, en el cual integro un linter muy completo en una aplicación flutter.
En el caso particular de que estés usando Flutter en tu proyecto, tenemos la opción de usar el analizador de código que viene por defecto con flutter. Con este, podemos configurar una serie de reglas en el archivo analysis_options.yaml que serán interpretadas cuando ejecutemos el comando flutter analyze
. Nos las mencionaré todas porque hay una gran cantidad, pero puedes consultarlas en este enlace. Muchas tienen asociado un Quick fix, lo que viene a ser que pulsando un botón se aplica una sugerencia automática para el problema que nos detecta el linter. En sí la capacidad de ejecutar el linter viene por defecto con flutter, pero para importar una serie de reglas recomendadas por el equipo de flutter añadiremos como dependencia el paquete ‘flutter_lints’ a nuestro proyecto y lo usaremos de la siguiente forma:
analysis_options.yaml
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- test/**/*.mocks.dart
- lib/config/firebase_options.dart
linter:
rules:
- prefer_relative_imports
- avoid_slow_async_io
- cancel_subscriptions
- close_sinks
La línea del include es la que nos importa las opciones recomendadas por flutter, y sería a partir de la sección linter donde aparece la que en adición añadimos nosotros.
Para que nos aplique los cambios de aquellas reglas que sí que tienen asociada un quick fix, podemos ejecutar el comando dart fix --apply <nombre_de_archivo>
.
El archivo “.editorconfig” es una herramienta que se utiliza en proyectos de código para establecer y mantener la consistencia en la configuración del editor. Permite definir reglas y configuraciones específicas, como el estilo de sangrado, la indentación, la codificación de caracteres y las preferencias de final de línea. Al emplearlo, se mejora la legibilidad del código, se reduce el conflicto en el control de versiones y se facilita la colaboración entre los miembros del equipo.
Quisimos aplicarlo en adición al linter, ya que la cantidad de indentación o el ancho de línea eran cosas que no podíamos tener con el linter por defecto de Flutter.
En el título de este apartado he indicado “linter para el editorconfig” aunque como hemos visto, el editorconfig en sí no es un linter. Pero encontré una forma de que se comportara como tal. Gracias al uso de la herramienta editorconfig-checker, podía ejecutar el comando ec y este me arrojaría por consola el resultado de comprobar si se están cumpliendo las reglas del editorconfig en el proyecto.
Ahora sabemos aplicar el linter de flutter y además usamos la herramienta editorconfig-checker para garantizar que nuestro código cumple los estándares que esperamos, pero… si ahora pretendemos añadirlo en nuestro proyecto tendrá que recorrer muchos archivos, nos arrojará muchas propuestas de cambios y será un poco dolor de cabeza que una sola persona tenga que arreglar eso, algo que le llevará mucho tiempo. Para solventar esto, lo que vamos a hacer es que solo tenga en cuenta lo que consideramos que son archivos en los que hemos trabajado. En nuestro caso, serían esos archivos que se van a incorporar al repositorio remoto y que están a la espera de ser subidos. Usando git lo haríamos tal que así:
Primero añadimos los archivos modificados al VCS
git add -u
flutter analyze + only changes
echo "🔃 Running flutter analyze..."
flutter analyze $(git diff --cached --name-only)
ec-checker + only changes
echo -e "\n🔃 Running .editorconfig check..."
ec $(git diff --cached --name-only)
echo -e "\n✅ .editorconfig check passed"
auto-fix the output of flutter analyze + only changes
git diff --cached --name-only | xargs -I {} dart fix --apply "{}"
La gracia de definir unas herramientas que buscan conseguir que el equipo siga un estilo de código común, es utilizar la herramienta, por lo que no nos sirve que a veces si la usemos y otras no. A veces estamos pensando en muchas cosas, nos olvidamos del linter y es ahí donde empiezan las inconsistencias. Además de querer que se emplee la herramienta, también debemos creer que cuando añadimos herramientas a nuestro flujo de trabajo es importante que no supongan una carga para el equipo, en su lugar solo nos debería beneficiar.
Por estas razones, lo mejor es tratar de automatizar la ejecución de las herramientas para que los desarrolladores nos podamos concentrar en la tarea que nos ocupa.
Los hooks de Git son scripts personalizables que se pueden ejecutar automáticamente en ciertos puntos clave del ciclo de vida de Git. Estos scripts se encuentran en la carpeta “.git/hooks” de un repositorio de Git y se activan en respuesta a eventos específicos, como confirmar cambios, fusionar ramas, empujar cambios, entre otros.
Para simplificarnos el proceso de ejecutar los linter, nos apoyaremos en dos hooks de Git. Primero usaremos el pre-commit para que antes de hacer commit dé los cambios:
pre-commit
#!/bin/bash
MODIFIED_FILES=($(git diff --cached --name-only --diff-filter=d))
if [[ ${MODIFIED_FILES[@]} =~ .dart$ ]]; then
DART_FILES=($(git diff --cached --name-only --diff-filter=d | grep .dart))
dart format --line-length 150 "${DART_FILES[@]}"
fi
git add "${MODIFIED_FILES[@]}"
exit 0
Esto nos garantiza que se cumple un ancho de línea determinado y que el código sigue unos estándares de formato definidos por Dart. Finalmente, usaremos el pre-push, para que antes de hacer push de los cambios al repositorio remoto, se realicen las comprobaciones de los linter pertinentes.
pre-push
#!/bin/bash
IS_SCRIPT_PASSED=0
CURRENT_BRANCH=$(git branch --show-current)
MODIFIED_FILES=($(git diff --name-only --diff-filter=d "origin/$CURRENT_BRANCH" HEAD))
if [ ${#MODIFIED_FILES[@]} -eq 0 ]; then
echo "✅ No versioned modified files tracked by the linter are pending to be pushed, skipping linter..."
echo -e "\n🚀 Proceeding with the push..."
exit 0
fi
echo -e "\n🔃 Running flutter analyze..."
flutter analyze "${MODIFIED_FILES[@]}"
ANALYZE_EXIT_CODE=$?
if [ $ANALYZE_EXIT_CODE -ne 0 ]; then
echo -e "\n❌ Flutter analyze failed. Please fix the issues before pushing"
echo -e "Execute \e[36mtask linter\e[0m to list the warnings or execute \e[36mtask linter-fix\e[0m to automatically fix all of them"
echo -e "\e[33mRemember that maybe not all the warnings would be automatically fixed as they could haven't a quick fix\e[0m"
IS_SCRIPT_PASSED=1
else
echo -e "\n✅ Flutter analyze passed"
fi
echo -e "\n🔃 Running .editorconfig check..."
ec "${MODIFIED_FILES[@]}"
CHECK_EXIT_CODE=$?
if [ $CHECK_EXIT_CODE -ne 0 ]; then
echo -e "\n❌ You have infringed some rules of the .editorconfig. Please fix the issues before pushing"
echo -e "\e[33mYou can reformat the code using your IDE but remember that some rules need to be fixed manually\e[0m"
IS_SCRIPT_PASSED=1
else
echo -e "\n✅ .editorconfig check passed"
fi
if [ $IS_SCRIPT_PASSED -ne 0 ]; then
echo ""
exit 1
fi
echo -e "\n🚀 Proceeding with the push..."
exit 0
En el equipo decidimos dejar en el pre-commit algo que no fuera muy restrictivo, es decir, algo que no pudiese detener que hagamos commit, ya que si estamos trabajando con micro-commit nos parecía que podía suponer una carga. Es a la hora de subir los cambios utilizando el pre-push donde se ejecutan las revisiones más restrictivas, donde en caso de no seguir los estándares no se permite hacer push de los cambios, con el fin de cumplir los estándares de código acordados por el equipo.
Además de tener los hooks de Git, es buena opción tener los linter como un paso de nuestra pipeline para que se ejecute en nuestra integración continua. Al final, los hooks de Git son archivos configurables que no son versionados, por lo que depende del buen hacer de cada persona tenerlos actualizados y respetar que se ejecuten, en lugar de configurar el IDE para que se salte las comprobaciones. Es en este escenario, donde la integración continua sí nos garantiza que el código que está listo para ser fusionado con la rama principal y cumple los estándares de los linter.
En ocasiones he escuchado opiniones de otros desarrolladores que opinan que incluir un linter en la pipeline es un lastre, porque puede suponer que tardes más en subir cambios, o porque resulta tedioso el hecho de querer subir cambios y tener que solventar esos conflictos primero. Yo creo que en un equipo es normal que entre las diferentes personas, haya distintas formas de cuidar el código, por eso usamos los linter, para conseguir un resultado uniforme que sea del agrado de todos. Por esto, no encuentro razones en los que hayan casos donde no tenga sentido seguir los estilos definidos, es más, si empezamos a hacer pequeñas excepciones solo para subir código más rápido, es cuando comienzan a surgir incongruencias en el código que darán más trabajo a los demás cuando se lo encuentren desarrollando alguna feature.
Personalmente, es de los primeros linter que configuro y probablemente no tengo mucho bagaje en el asunto, por lo que me encantaría escuchar opiniones y sugerencias ¡Gracias por leer!
¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.
Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?
Pero espera 🖐 que tenemos un conflicto interno. A nosotros las newsletter nos parecen 💩👎👹 Por eso hemos creado la LEAN LISTA, la primera lista zen, disfrutona y que suena a rock y reggaeton del sector de la programación. Todos hemos recibido newsletters por encima de nuestras posibilidades 😅 por eso este es el compromiso de la Lean Lista