Análisis de Código con SonarQube

La calidad de código suele decirse que es un atributo interno de calidad, dado que no se hace visible al usuario. Pero llega un momento en el cual este atributo de calidad pasa de ser interno a externo, y esto se da cuando el hecho de tener modificar el código para hacer un cambio lleva mucho más tiempo del que debería. Con el fin de verificar la calidad interna de un sistema se suelen hacer análisis de código con SonarQube o herramientas similares. En este post te comparto parte de una investigación hecha por Matías Fornara en Abstracta al respecto, donde básicamente te cuenta cómo hacer una prueba de concepto rápidamente usando una imagen Docker de SonarQube, y ejecutando el análisis desde SonarQube Scanner.



SonarQube, como tantas otras herramientas similares, permite realizar análisis estático de código fuente de manera automática, buscando patrones con errores, malas prácticas o incidentes. Además, realiza un cálculo de la deuda técnica. Dentro de las verificaciones que hacen herramientas como SonarQube, se encuentran las siguientes:

  • Detección de código duplicado.
  • Falta de pruebas unitarias, falta de comentarios.
  • Código spaghetti, complejidad ciclomática, alto acoplamiento.
  • Tamaño de archivos de código.
  • Tamaño de métodos.
  • No adecuación a estándares y convenciones de código.
  • Vulnerabilidades conocidas de seguridad.

A través de definir “Quality Gates” es posible marcar los umbrales aceptables en el proceso de desarrollo, y estos pueden ser definidos en base a distintas métricas. Esto es muy útil si se integra SonarQube en el pipeline de CI/CD.

Un Quality Gate puede ser definido para responder preguntas como:

  • No hay issues nuevos que sean críticos o bloqueantes.
  • El code coverage de las pruebas unitarias del nuevo código es mayor a 80%.

Para poder utilizar SonarQube se necesita instalar un componente servidor, donde se encuentra el engine que realiza el análisis y almacena los resultados, y se debe invocar de alguna manera el análisis, lo cual puede realizarse con un cliente llamado SonarQube Scanner o con un plugin de Maven. También se puede integrar el análisis al IDE que se esté utilizando, con un plugin llamado SonarLint. En este post veremos cómo instalar el servidor (usando una imagen Docker) y cómo invocar el análisis usando SonarQube Scanner. En un próximo post veremos otras alternativas que se pueden usar.

Servidor SonarQube con Docker

Precisamos tener un servidor SonarQube corriendo. Para esta tarea usaremos una imagen Docker para así agilizar este proceso. Partimos de que ya tenemos Docker instalado, de no ser así el proceso es bastante simple y se puede ver aquí.

Entonces, podemos proceder a descargar el contenedor SonarQube haciendo un pull contra el repositorio de Docker:

Una vez descargado debemos iniciarlo para dejarlo disponible en un determinado puerto. Para lograr esto ejecutamos:

docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 sonarqube

En esta línea le estamos especificando el nombre del contenedor que queremos correr y el mapeo de puertos entre el contenedor y nuestra red.

Para verificar la instalación podemos intentar acceder a http://ip-de-docker:puerto. En nuestro caso el puerto es 9000 y para saber la IP de nuestro contenedor podemos usar el comando “docker-machine ip default“.

Si todo salió bien, deberíamos ver algo como esta imagen:

Crear Proyecto en el servidor SonarQube

Primero se debe estar logueado en SonarQube y desde ahí nos dirigimos al menú de “Administration”. Luego, “Projects->Management” y ahí seleccionamos “Create Project”.

En este formulario definimos el nombre que el proyecto va a tener en el servidor, la versión y la project key (que puede contener letras, números, ‘-‘, ‘_’, ‘.’ y ‘:’,y  al menos un dígito).

Ejecutar SonarQube Scanner

Para poder ejecutar un análisis con SonarQube sobre un proyecto debemos descargar SonarQube Scanner aquí. Una vez descargado, se debe descomprimir y luego agregar al path la carpeta /bin que se encuentra dentro del directorio donde descomprimimos, para poder ejecutarlo desde línea de comandos fácilmente.

Para saber si la instalación fue exitosa, ejecutar:

sonar-scanner -h

Se debería ver una salida como esta:

Una vez culminado el paso anterior, se debe configurar la ubicación del servidor SonarQube. Para esto hay que editar el archivo de propiedades <directorioSonarScanner>/conf/sonar-scanner.properties como se muestra debajo.

(Quitar comentario para que tome la configuración)

Para proceder con el análisis en cuestión, se necesita de la creación de un archivo de properties que es particular para cada proyecto, llamado sonar-project.properties (situado en la raíz del mismo):

En este indicamos los parámetros correspondientes al proyecto creado en el servidor SonarQube.

Habiendo configurado los 2 archivos, es momento de ejecutar el análisis. Para esto hay que situarse  en el directorio del proyecto y ejecutar el comando:

sonar-scanner

Una vez finalizado el análisis, se desplegará en pantalla la URL para acceder al reporte del mismo.

Me encantaría saber cómo te va con tus pruebas, o si tienes experiencias para compartir relacionadas al uso de SonarQube en un entorno de Continuous Delivery.

22 thoughts on “Análisis de Código con SonarQube

  1. samuel pardo mesias says:

    Buenos dias, tengo error al hacer sonar-scanner, a parte de lo que publico se necesita hacer otro tipo de configuracion

    1. Matias Fornara says:

      Hola Samuel, podrías comentarme el error y lo vemos, a priori no habría que configurar nada extra.

      Saludos!
      Matias.

      1. samuel pardo mesias says:

        Saludos Matias Fornara, sobre los errores que tube con sonarqube ya lo solucione , tengo algunas preguntas sobre los tag que sonarqube muestra. Te dejo mi correo samuel.rpardo@gmail.com si tubieras alguna informacion sobre ello ejemplo: Bad – Practice, Redundant, Brain Overload etc

        1. Matias Fornara says:

          Samuel, cómo estás? Me alegro que hayas podido solucionar los inconvenientes. Te dejo el link a la documentación que tal vez puedas encontrar útil. https://docs.sonarqube.org/display/SONAR/Built-in+Rule+Tags

          Saludos!
          Matias

  2. asaflugo says:

    Hola! Al intentar escanear el código, me arroja los siguientes errores. Espero puedan apoyarme. Saludos

    ERROR: Error during SonarQube Scanner execution
    java.lang.IllegalStateException: Cannot analyse the file ‘/home/oracle/sonar/codigoAppPro/codigoAppPro/PYTHON/__init__.py’, details: ‘org.sonar.api.utils.command.CommandException: java.io.IOException: Cannot run program “pylint”: error=2, No such file or directory’
    at org.sonar.plugins.python.pylint.PylintSensor.analyse(PylintSensor.java:91)
    at org.sonar.scanner.phases.SensorsExecutor.executeSensor(SensorsExecutor.java:87)
    at org.sonar.scanner.phases.SensorsExecutor.execute(SensorsExecutor.java:81)
    at org.sonar.scanner.phases.SensorsExecutor.execute(SensorsExecutor.java:67)
    at org.sonar.scanner.phases.AbstractPhaseExecutor.execute(AbstractPhaseExecutor.java:75)
    at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:178)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:144)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:129)
    at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:259)
    at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:254)
    at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:243)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:144)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:129)
    at org.sonar.scanner.task.ScanTask.execute(ScanTask.java:47)
    at org.sonar.scanner.task.TaskContainer.doAfterStart(TaskContainer.java:86)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:144)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:129)
    at org.sonar.scanner.bootstrap.GlobalContainer.executeTask(GlobalContainer.java:118)
    at org.sonar.batch.bootstrapper.Batch.executeTask(Batch.java:117)
    at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:77)
    at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:46)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
    at com.sun.proxy.$Proxy0.execute(Unknown Source)
    at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:171)
    at org.sonarsource.scanner.api.EmbeddedScanner.execute(EmbeddedScanner.java:128)
    at org.sonarsource.scanner.cli.Main.execute(Main.java:111)
    at org.sonarsource.scanner.cli.Main.execute(Main.java:75)
    at org.sonarsource.scanner.cli.Main.main(Main.java:61)
    Caused by: org.sonar.api.utils.command.CommandException: java.io.IOException: Cannot run program “pylint”: error=2, No such file or directory
    at org.sonar.api.utils.command.CommandExecutor.execute(CommandExecutor.java:102)
    at org.sonar.plugins.python.pylint.PylintArguments.pylintVersion(PylintArguments.java:47)
    at org.sonar.plugins.python.pylint.PylintArguments.(PylintArguments.java:39)
    at org.sonar.plugins.python.pylint.PylintIssuesAnalyzer.(PylintIssuesAnalyzer.java:47)
    at org.sonar.plugins.python.pylint.PylintSensor.analyzeFile(PylintSensor.java:102)
    at org.sonar.plugins.python.pylint.PylintSensor.analyse(PylintSensor.java:81)
    … 31 more
    Caused by: java.io.IOException: Cannot run program “pylint”: error=2, No such file or directory
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
    at org.sonar.api.utils.command.CommandExecutor.execute(CommandExecutor.java:74)
    … 36 more
    Caused by: java.io.IOException: error=2, No such file or directory
    at java.lang.UNIXProcess.forkAndExec(Native Method)
    at java.lang.UNIXProcess.(UNIXProcess.java:247)
    at java.lang.ProcessImpl.start(ProcessImpl.java:134)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
    … 37 more

  3. Matias Fornara says:

    Hola Asaflugo,

    Parece que Sonar no puede encontrar el Pylint, podés probar especificandole el path del Pylint, para eso averigua donde está situado y luego modifica la propiedad :

    sonar.python.pylint=/examplepath/pylint

    Si es no funciona podes probar correr el Pylint antes y especificarle a Sonar donde está el resutlado del análisis de la siguiente manera:

    sonar.python.pylint.reportPath=pylint-reports/pylint-result-*.txt

    Te dejo información sobre que hace el Pylint, que tal vez te ayude, https://docs.sonarqube.org/display/PLUG/Pylint+Report

    Saludos!
    Matias.

  4. william says:

    Buenos dias tengo una consulta estoy compilando un branch principal y me publica en sonar pero al querer realizar la compilacion con un branch secundario o child de donde ya compile el principal (mismo repositorio) no lo publica aparece un error de llave y no se a que se refiere.:

    [INFO] ANALYSIS SUCCESSFUL, you can browse http://10.25.10.212:9000/sonar/dashboard/index/com.carvajal.platform:certification-feco-documents-port-parent-parent
    [INFO] Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
    [INFO] More about the report processing at http://10.25.10.212:9000/sonar/api/ce/task?id=AW2nBuI1iS3uhX0xOCmz
    [INFO] Task total time: 17.546 s
    [INFO] ————————————————————————
    [INFO] Reactor Summary:
    [INFO]
    [INFO] certification-feco-documents-port-parent-parent 1.0.0-SNAPSHOT SUCCESS [01:30 min]
    [INFO] certification-feco-documents-port-parent-domain …. SUCCESS [ 10.998 s]
    [INFO] certification-feco-documents-port-parent-managers .. SUCCESS [ 1.868 s]
    [INFO] certification-feco-documents-port-parent-services .. SUCCESS [ 2.480 s]
    [INFO] certification-feco-documents-port-parent-api ……. SUCCESS [ 4.911 s]
    [INFO] certification-feco-documents-port-parent-client …. SUCCESS [ 2.431 s]
    [INFO] certification-feco-documents-port-parent-integration SUCCESS [ 16.106 s]
    [INFO] certification-feco-documents-port-parent-distribution SUCCESS [ 39.570 s]
    [INFO] certification-feco-documents-port-parent-persistence 1.0.0-SNAPSHOT SUCCESS [ 3.702 s]
    [INFO] ————————————————————————
    [INFO] BUILD SUCCESS
    [INFO] ————————————————————————
    [INFO] Total time: 03:09 min
    [INFO] Finished at: 2019-10-07T16:21:51Z
    [INFO] ————————————————————————
    e7cabce0-2e32-422b-9290-c262968a852a exists true
    e7cabce0-2e32-422b-9290-c262968a852a exists true
    Server responded with an invalid or unexpected response format.
    Code analysis failed.
    Only single test suite found, parsing its information
    Timestamp is not available for one or more testsuites. Total run duration is being calculated as the sum of time durations of detected testsuites
    Only single test suite found, parsing its information
    Timestamp is not available for one or more testsuites. Total run duration is being calculated as the sum of time durations of detected testsuites
    ##[error]Build failed.
    ##[section]Async Command Start: Publish test results
    Publishing test results to test run ‘1019608’
    Test results remaining: 15. Test run id: 1019608
    Published Test Run : https://ctsebs.visualstudio.com/Factura/_TestManagement/Runs?runId=1019608&_a=runCharts
    ##[section]Async Command End: Publish test results
    ##[section]Finishing: Maven pom.xml
    muchas gracias.

  5. Hola William,

    En el stacktrace que publicas no veo mucha información del error, ni tampoco me queda muy claro a que te referis con “un error de llave”.

    Si tienes más información sobre el error compartela o puedes intentar buscar en la comunidad de sonarqube https://www.sonarqube.org/community/ .

    Por otra parte quería mencionarte algo que el análisis en multiples branches es una funcionalidad que sonarqube solo brinda a partir de su edición Developer en adelante, por lo tanto si estás usando la gratuita es probable que tenga algún problema.

    Saludos!
    Matias.

  6. DAVID says:

    Hola, desde sonaquber, me sale este error de código que no sé a qué exactamente se refiere. “Refactor or remove this statement”
    ({
    helperMethod : function() {

    },
    fireAppEvent : function(cmp, event, eventName, params) {
    var appEvent = $A.get(“e.c:” + eventName);
    appEvent.setParams(params);
    appEvent.fire();
    },
    // funcion de llamada a la clase apex SELFTBS_Map
    getProperties : function (cmp, service) {
    var action = cmp.get(‘c.getProperties’);
    action.setParams({“service”: service});
    action.setCallback(this, $A.getCallback(function (response) {
    var state = response.getState();
    if (state === “SUCCESS”) {

    cmp.set(‘v.Properties’, response.getReturnValue());
    //Extraer las opciones para el input select
    } else if (state === “ERROR”) {
    var errors = response.getError();
    console.error(errors);
    }
    }));
    $A.enqueueAction(action);
    }
    /*
    getPropertyDomains : function (cmp) {
    var action = cmp.get(‘c.getPropertyDomains’);
    action.setCallback(this, $A.getCallback(function (response) {
    var state = response.getState();
    if (state === “SUCCESS”) {

    cmp.set(‘v.PropertyDomains’, response.getReturnValue());
    //Extraer las opciones para el input select
    } else if (state === “ERROR”) {
    var errors = response.getError();
    console.error(errors);
    }
    }));
    $A.enqueueAction(action);
    }, */
    /* getQuotesInOpty : function(cmp, optyId, myData) {
    var action = cmp.get(“c.getQuotesInOpty”);
    action.setParams({“optyId”:optyId});
    action.setCallback(this, function(response){
    var state = response.getState();
    if (state === “SUCCESS” ) {
    var resultData = response.getReturnValue();
    myData(resultData);
    }
    return {resultData};
    });
    $A.enqueueAction(action);
    },
    getQuotesInCase : function(cmp, caseId, myData) {
    var action = cmp.get(“c.getQuotesInCase”);
    var resultData=[];
    action.setParams({“caseId”:caseId});
    action.setCallback(this, function(response){
    var state = response.getState();
    if (state === “SUCCESS” ) {
    resultData = response.getReturnValue();
    myData(resultData);
    }else{
    console.log(‘error ‘ + response.getError());
    }
    return {resultData};
    });
    $A.enqueueAction(action);
    },
    fireAppEvent : function(component, event, eventName, params) {
    console.log(‘Mis parametros: ‘ + params);
    var appEvent = $A.get(“e.c:” + eventName);
    if (params != {}){
    appEvent.setParams(params);
    appEvent.fire();
    }
    },
    createEmptyQuote : function(cmp, optyId, caseId, myData) {
    var action = cmp.get(“c.createEmptyQuote”);
    var params = optyId + “;” + caseId;

    //action.setParams({“optyId”:optyId});
    //action.setParams({“caseId”:caseId});
    //console.log(‘caseIdHelper ‘ + params);
    action.setParams({“params”:params});
    action.setCallback(this, function(response){
    var state = response.getState();
    console.log(‘state ‘ + state);
    if (state === “SUCCESS” ) {
    var resultData = response.getReturnValue();
    myData(resultData);
    }else{
    console.log(‘error ‘ + response.getError());
    }
    return {resultData};
    });
    $A.enqueueAction(action);
    }*/

    })

  7. David, cómo estás?

    Te animás a darme un poco más de contexto? A que te referis con un “error de código”?

    Si entiendo bien Sonar te está diciendo que refactorizes un método, de ser así podes fijarte dentro de la UI de Sonar e ir a esa lineas, donde te va a decir una posible solución, porque a simple vista no se si se refiere a la función vacía del principio o que más.

    Sino podes resolver dame un poco más de contexto sobre cuando y como corres sonar para que te tire ese error, pero si es algo particular de como está desarrollado el código que está analizando no te voy a poder ser de mucha ayuda.

    Gracias por tomarte el tiempo de leer el post 🙂

    Saludos!

  8. Raul says:

    Buenas noches,

    Una pregunata a al analizar mi codigo me regresa el siguiente error.

    Could not find goal ” in plugin org.sonarsource.scanner.maven:sonar-maven-plugin:3.0.2 among available goals help, sonar -> [Help 1]

    me podrian ayudar o dar una pista de como solucionarlo por favor.
    gracias

  9. Raúl, cómo estas?

    Te animas a compartir el sacktrace debajo de ese error? porque eso no me da demasiada info. También comentame que goal estaba intentando ejecutar cuando te pasó eso.

    Fijate que si lo estas haciendo con maven tengas bien definido el pom con el plugin de sonar.

    Saludos!

  10. Jose M. says:

    Buenas.

    Querría haceros una consulta, que aunque esta relacionada directamente con SonarQube, entiendo que es offtopic. Pero parece que este hilo de comentarios es bastante activo, así que voy a probar suerte.

    Llevo un tiempo investigando acerca de como realizar Custom Rules para SonarQube sin demasiado éxito.
    La documentación oficial acerca de este asunto la considero algo escueta e incompleta. Y no he encontrado por otro lugar nada demasiado detallado de como escribir reglas personalizadas. Todos los tutoriales y cursos que he encontrado repasan los mismos temas (que es Sonar, como instalarlo, como pasar el sonar scanner, los Qualitiy Gates, etc). Pero en ningún lado he visto profundización acerca de como realizar tus propias reglas de SonarQube para un lenguaje. (ej: java).

    ¿Tenéis experiencia en este tema? ¿Me podríais recomendar documentación o formación al respecto? Me gustaría profundizar mas, aunque se tratase de una propuesta de pago. Pero no he visto ninguna opción que me aporte mas información,

    Muchas gracias,
    Un saludo.

  11. Doc says:

    Hola. Estoy realizando una investigación educativa sobre SonarQube y me gustaría saber cómo se calcula la deuda técnica exactamente… He visto que se usa SQUALE, pero no encuentro nada que justifique esos tiempos que marca, ¿se tiene alguna guía sobre ello?

  12. Buenas, unos de los inputs que usa SonarQube para hacer los cálculos es el tiempo que vos definís que toma desarrollar una línea de código, esto es una configuración que se puede encontrar dentro de SonarQube si tienes un usuario Admin.

    Por otra parte en la documentación de SonarQube hay bastante info, te dejo por acá:
    https://docs.sonarqube.org/latest/user-guide/metric-definitions/
    https://docs.sonarqube.org/latest/user-guide/concepts/

  13. logan says:

    Hola una consulta tengo el sonarq instalado modo free, en un un servidor windows , actualmente escaneo mi codigo nuevo sin problemas mi consulta es por ser free tengo que hacer algun parche para actualizar las nuevas vulnerabilidades que salgan ?o como sonarq realiza el update de sus resultado?
    Muchas Gracias

  14. Matias Fornara says:

    Logan,

    A priori no es necesario, pero si te recomiendo que uses una versión LTS y que mires https://docs.sonarqube.org/latest/setup-and-upgrade/release-upgrade-notes/ para ver si hay algo que valga la pena el esfuerzo de actualizar.

    Una nota importante es que si lo vas a hacer, hagas un backup de la DB por cualquier problema que pueda surgir.

  15. Enrique says:

    Hola, por favor haber si me puedes apoyar, busco información o documento técnico que indique de acuerdo a la versión utilizada, free u otra, donde se realiza el análisis del código. me indicaron que todo actualmente es en cloud.
    gracias

  16. Enrique, cómo estás?

    En realidad el análisis como tal siempre es “local” ya sea en tu máquina o en un servidor de CI/CD. Luego que el análisis termina se le envía la información al servidor de sonarqube que puede ser sonar cloud, una imagen de docker local, o un servidor de sonarqube hosteado en alguna nube.

    Te dejo este link que tal vez ayude a visualizar lo que comento https://docs.sonarqube.org/latest/setup-and-upgrade/install-the-server/ .

    Espero que ayude,
    Saludos!

  17. Enrique says:

    Agradezco tu respuesta..

    saludos

  18. Monik says:

    Hola,
    ¿Es siempre necesario compilar el código fuente para que sonarqube escaneé el código?

    Muchas gracias

  19. Matias Fornara says:

    Monik, cómo estás?

    Depende del lenguage que estés analizando pero mayormente si. En lenguajes como C o C++ podes utilizar un base de datos para los builds (te dejo acá un poco más sobre eso https://community.sonarsource.com/t/why-does-sonarqube-need-to-compile-the-code/75257/2)

    Pero como decia mayormente si se precisas compilar por como SonarQube hace el análisis, por ejemplo en Java si vos tenés una clase A que tiene una referencia a una clase B, Sonar analiza el fuente de la clase A y luego para entender el tipo de la referencia a B mira el bytecode de la clase B, esto según he leido es una desición de implementación de SonarQube para poder construir el modelo semántico.

    Espero haber aclarado.

    Saludos!

Leave a Reply

Your email address will not be published. Required fields are marked *