Appearance
SonarQube
Calidad en el código
Esta herramienta gratuita nos permite hacer una inspección del código, prevenir bugs reconocidos y reportarnos propuestas de mejoras para nuestro código o prácticas inapropiadas, aunque es finalmente el desarrollador o alguien del equipo quién puede hacer una lectura y determinar que se considera relevante en un análisis obtenido (sobretodo ante comentarios minuciosos de la herramienta).
En otras palabras, SonarQube se enfoca en la calidad del código y suele usarse para verificar cierto estado del código en un proceso de desarrollo automatizado.
Instalacion en Docker
Aunque actualmente Sonar tiene version en la nube (SonarCloud), es posible usarlo en nuestra máquina con contenedores. A continuación se ilustran comandos para lograr un entorno local de Sonar usando Doccker (o Podman en su reemplazo).
bash
docker volume create sonarqube_data
docker volume create sonarqube_plug
docker volume create sonarqube_logs
docker run -d --name sonarqube \
-p 9000:9000 \
-v sonarqube_data:/opt/sonarqube/data \
-v sonarqube_plug:/opt/sonarqube/extensions \
-v sonarqube_logs:/opt/sonarqube/logs \
sonarqube:10.7.0-communityEsto habilita un servicio en
localhost:9000cuyo usuario y contraseña inicial esadmin.
Puedes sustituir el comandodockerporpodmansi prefieres usar Podman.
Preparando un proyecto con Java/Jacoco & SonarQube
Para probar SonarQube implementaremos un programa sencillo en Java con un código para Main como el siguiente:
java
package org.example;
public class Main {
public static void main(String[] args) {
String s = (args.length > 0) ? args[0] : "Welcome here!";
System.out.println("=> ".concat(String.valueOf(numberOfWords(s))));
}
public static int numberOfWords(String s) {
if (s.isEmpty()) return 0;
String[] words = s.split("\\s+");
return words.length;
}
}Y para pruebas unitarias tendremos MainTest con el siguiete código:
java
package org.example;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MainTest {
@Test
void main() {
Main.main(new String[]{"This just works!"});
}
@Test
void numberOfWordsEmptyString() {
assertEquals(0, Main.numberOfWords(""));
}
@Test
void numberOfWords() {
assertEquals(2, Main.numberOfWords("Welcome here!"));
}
}El archivo build.gradle tendría un contenido como el siguiente:
groovy
plugins {
id 'java'
id 'org.sonarqube' version '5.0.0.4638'
id 'jacoco'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
sonar {
properties {
property "sonar.sourceEncoding", "UTF-8"
property "sonar.sources", "src/main/java"
property "sonar.tests", "src/test/java"
property "sonar.test.inclusions", "**/src/test/**"
property "sonar.java.binaries", "build/classes"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/tests/test/jacocoTestReport.xml"
}
}
test {
useJUnitPlatform()
}Como aspectos claves para el bloque
sonartendríamos la propiedadsonar.sourcescon carpeta de fuentes,sonar.testcon carpeta de pruebas unitarias,sonar.java.coveragePluginindicando el uso dejacoco, ysonar.coverage.jacoco.xmlReportPathspara reporte de cobertura.
Podría incluirse la propiedadsonar.exclusionspara indicar rutas a exluir de pruebas. Por ejemplo:sonar.exclusions","**src/application/main/java/config/**
Si tenemos SonarQube configurado, habiendo generado un Token (en la opción de administración, cuyo menú se ubica en la esquina superior derecha, luego en seguridad), podríamos ejecutar desde una terminal un comando como el siguiente:
bash
./gradlew sonar -Dsonar.projectKey=java-tests -Dsonar.host.url=http://localhost:9000 -Dsonar.token=squ_d0f0e905aae9c867d5ae4686f1e63c5778444a1aSobre Quality Gateway
El Quality Gateway define criterios que el código debe cumplir para considerarse apto para producción:
- Coverage: Mínimo 80% de cobertura de código
- Duplicated Lines: Máximo 3% de líneas duplicadas
- Maintainability Rating: Calificación A (deuda técnica < 5%)
- Reliability Rating: Calificación A (sin bugs)
- Security Rating: Calificación A (sin vulnerabilidades)
bash
# Verificar estado del Quality Gateway
curl -u $SONAR_TOKEN: \
"$SONAR_HOST/api/qualitygates/project_status?projectKey=$PROJECT_KEY"El Quality Gateway actúa como un checkpoint automático que puede bloquear deployments si el código no cumple los estándares establecidos, garantizando que solo código de calidad llegue a producción. Esta integración con pipelines CI/CD convierte a SonarQube en una herramienta esencial para mantener la calidad del software de forma continua.
Sobre SonarQube Cloud
SonarQube Cloud es la versión en la nube de SonarQube que elimina la necesidad de mantener infraestructura propia. Se integra directamente con repositorios de GitHub, GitLab, Bitbucket y Azure DevOps.
Veamos un ejemplo para GitHub Actions (archivo: .github/workflows/sonar.yml):
yaml
name: SonarCloud
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
sonarcloud:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup JDK 17
uses: actions/setup-java@v3
with:
java-version: 17
distribution: 'temurin'
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}Y para Azure DevOps tendríamos un archivo azure-pipelines.yml con un contendo como el siguiente:
yaml
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
SONAR_TOKEN: $(sonarToken)
steps:
- task: JavaToolInstaller@0
inputs:
versionSpec: '17'
jdkArchitectureOption: 'x64'
jdkSourceOption: 'PreInstalled'
- task: SonarCloudPrepare@1
inputs:
SonarCloud: 'SonarCloud'
organization: 'your-org'
scannerMode: 'Other'
- task: Gradle@2
inputs:
workingDirectory: ''
gradleWrapperFile: 'gradlew'
gradleOptions: '-Xmx3072m'
tasks: 'test jacocoTestReport sonar'
- task: SonarCloudPublish@1
inputs:
pollingTimeoutSec: '300'