Creando un pipeline de integración con Bitbucket+SonarCloud para nodeJS
Dentro de mi equipo tomamos la determinación de comenzar a aplicar TDD como metodología de trabajo para nuestros nuevos proyectos, sabiendo que es un camino largo y que requiere sortear ciertas barreras culturales, principalmente, los usos y costumbres instaurados. A fin de incentivar la ruptura de estas barreras, decidimos integrar un proceso de control de calidad en uno de nuestros repositorios en nodeJS, alojado en bitbucket.
Contando ya con algunos tests, decidimos integrarnos con SonarCloud, servicio del cual ya dispone nuestra organización, a fin de validar no sólo la cobertura, sino además la calidad de nuestro código en general. Utilizamos como referencia uno de los ejemplos oficiales de SonarSource, y otros proyectos de la organización.
El pipeline resultante es el siguiente:
clone:
depth: full # SonarCloud scanner needs the full history to assign issues properly
definitions:
services:
docker:
memory: 2048
caches:
sonar: ~/.sonar/cache
vendor: vendor/*
steps:
- step: &test-and-sonar-analysis
size: 2x
name: Run tests, analyze on SonarCloud
caches:
- node
- sonar
image: node:14.17-slim
script:
- npm install
- npm run test:cov
- pipe: sonarsource/sonarcloud-scan:1.3.0
variables:
SONAR_TOKEN: ${SONAR_TOKEN}
EXTRA_ARGS: '-Dsonar.sources="src" -Dsonar.exclusions="src/Adapters/**/*, src/Database/**, src/Config/*, src/UI/**, src/Tests/**" -Dsonar.typescript.lcov.reportPaths="coverage/lcov.info"'
- step: &sonar-quality-gate
name: Check Quality Gate on SonarCloud
script:
- pipe: sonarsource/sonarcloud-quality-gate:0.1.6
pipelines:
pull-requests:
'**': # All pr reached
- step: *test-and-sonar-analysis
- step: *sonar-quality-gate
Inconvenientes con los que nos hemos encontrado
No LCOV files were found using coverage/lcov.info
Como primera medida, agregamos el argumento Dsonar.typescript.lcov.reportPaths. En nuestro caso, resultó importante utilizar el correcto en cuanto a lenguaje (typescript / javascript), ya que al usar el incorrecto, el reporte de error en el pipeline era silenciado. Notamos además que en la documentación oficial no están diferenciados ambos argumentos.
En la primera versión del pipeline teníamos el proceso de build+test separado del de análisis de Sonar. Pasaron unos largos minutos de ardua búsqueda en internet, principalmente en el foro de ayuda de SonarSource, hasta que caímos en la cuenta de que todo aquello generado en un paso, se pierde en el otro, a menos que definamos un artifact. En nuestro caso la solución rápida fue integrar ambas tareas en un mismo paso; nos queda pendiente probar otras alternativa (artifacts).
Baja calificación
En nuestro caso sólo nos interesa cubrir la funcionalidad de negocio por el momento, que se encuentra dentro de la carpeta /Domain. Por ello los tests estuvieron enfocados en esos componentes: casos de uso, entidades de dominio y servicios internos. En los primeros intentos, las métricas que arrojaba Sonar dejaban bastante que desear. La solución fue excluir los directorios que no agregan valor al análisis, mediante el argumento -Dsonar.exclusions, en el que es posible indicar varios directorios utilizando los respectivos patrones para archivos y directorios.
Segundo enfoque: modularizar
Utilizando los artifacts descritos más arriba, re-escribimos el pipeline para individualizar cada paso del proceso:
image: node:14.17-slim
clone:
depth: full # SonarCloud scanner needs the full history to assign issues properly
definitions:
services:
docker:
memory: 2048
caches:
sonar: ~/.sonar/cache
vendor: vendor/*
steps:
- step: &install-deps
size: 2x
name: Install deps
caches:
- node
script:
- npm install --quiet
artifacts:
- node_modules/**
- step: &test
size: 2x
name: Run tests
caches:
- node
script:
- npm run test:cov
artifacts:
- coverage/**
- step: &sonar-analysis
size: 2x
name: Analyze on SonarCloud
caches:
- node
- sonar
script:
- pipe: sonarsource/sonarcloud-scan:1.3.0
variables:
SONAR_TOKEN: ${SONAR_TOKEN}
EXTRA_ARGS: '-Dsonar.sources="src" -Dsonar.exclusions="src/Adapters/**/*, src/Database/**, src/Config/*, src/UI/**, src/Tests/**" -Dsonar.typescript.lcov.reportPaths="coverage/lcov.info"'
- step: &sonar-quality-gate
name: Check Quality Gate on SonarCloud
script:
- pipe: sonarsource/sonarcloud-quality-gate:0.1.6
pipelines:
pull-requests:
'**': # Every PR
- step: *install-deps
- step: *test
- step: *sonar-analysis
- step: *sonar-quality-gate
Si bien esta implementación es más flexible y promueve la reusabilidad, nos encontramos con que el tiempo de ejecución del pipeline se dispara a 6 minutos, contra los 3 minutos de la primera versión. Esto es consecuencia del tiempo requerido para construir cada artifact (comprimir los archivos) y guardarlo, para luego descargarlo y descomprimirlo en el siguiente paso.