Zoeken

Inzicht verkrijgen in de code kwaliteit met behulp van Sonar

OCS-abstract_patroon
SonarQube is een server-applicatie die de kwaliteit van programmacode kan bijhouden van meerdere programmeer- en scripttalen. SonarQube was in 2006 een van de eerste, zo niet de eerste, applicaties om Java codekwaliteit bij te houden. Daarom heeft het nu ook een zeer grote groep gebruikers. Inmiddels zijn er diverse alternatieven (Verocode, Black Duck, Embold, Klocwork, Deepsource, Synopsys, Checkmarx, Semgrep, Kiuwan) beschikbaar maar, voor zover bekend, zijn de meesten hiervan niet zo compleet als SonarQube.

In: Tutorial

SonarQube heeft ook een community-editie en daardoor ideaal om mee te beginnen.

In dit voorbeeld ligt de focus op SonarQube en Java. SonarCloud is in feite de cloudversie van SonarQube. Voor het gemak gebruiken we de term ‘Sonar’ om naar beide varianten te verwijzen.

Sonar kan ook worden gebruikt om de testdekking te controleren en blinde vlekken in de applicatie bloot te leggen. In de praktijk kan het gebruik van Sonar complex zijn vanwege de vele onderdelen die afhankelijk zijn van elkaar en moeten samenwerken. Daarom volgt een opsomming van deze onderdelen, aangevuld met aanbevelingen voor optimaal gebruik. Voorkennis van Java, Maven en Docker is een pré.

Bij de ontwikkeling van Java-applicaties is het raadzaam om vanaf het begin de codekwaliteit met Sonar te controleren. Het is ook mogelijk om Sonar toe te passen op bestaande Java-applicaties en alleen de nieuwe en aangepaste code te controleren. Daarnaast kan de oude code worden gecontroleerd op veiligheidsaspecten, technical debt en prestatie-optimalisaties. Let wel, op het gebied van beveiliging loopt Sonar wat achter op alternatieven zoals Veracode.

Maven

Het uitgangspunt is dat het project een multi-module Maven-project is. Tevens dat de testen zijn onderverdeeld in unit, integratie en archunit testen. Bij een multi-module project wordt door Sonar aangeraden om een extra rapportage module aan te maken die gebruikt kan worden voor rapportage aggregatie van alle andere modules.

SSonar adviseert om de versie van de ‘sonar-maven-plugin’ af te stemmen op de gebruikte SonarQube-server om problemen te voorkomen. Voor de ‘jacoco-maven-plugin’ wil je alle externe bibliotheken uitsluiten, omdat je doorgaans alleen geïnteresseerd bent in de dekking van je eigen code. Door de ‘jacoco-maven-plugin’ in de main pom te gebruiken, kan per (sub)module een dekkingsrapportage worden gemaakt. Deze rapportages kunnen dan in een aparte module worden samengevoegd.

Het is aan te raden om bij de Sonar Maven-configuratie zo dicht mogelijk bij de standaarden te blijven zodat het makkelijker is om de documentatie te volgen en bij te blijven met de laatste ontwikkelingen. Met name de laatste 5 tot 10 jaar zijn er diverse veranderingen geweest binnen het Sonar ecosysteem. Vandaar dat oudere artikelen en beschrijving hierover vaak te oud zijn om te gebruiken voor een courante versie. Het is niet ondenkbaar dat ook de hele sonar suite op middellange termijn meer richting de cloud beweegt en de ondersteuning voor de self-hosted variant langzaam uitgefaseerd wordt.

In dit voorbeeld is dit geïmplementeerd als een Maven profiel (zie: https://maven.apache.org/guides/introduction/introduction-to-profiles.html) dat indien gewenst gebruikt kan worden. Tijdens het ontwikkelen is het gebruik van dit profiel onwenselijk omdat de analyse extra tijd in beslag neemt. Het kan wel handig zijn tijdens het afronden van een taak en je zeker wilt zijn dat het onderdeel klopt. Je kunt het ook achteraf laten controleren door de bouwstraat zelf. Gebruikelijk is dat de bouwstraat zelf een sein geeft of het gelukt of mislukt is. Sonar kan ook zo geconfigureerd worden dat builds worden goed- of afgekeurd op basis van opgegeven criteria.

Main pom

<profile>
    <id>coverage</id>
    <activation>
        <property>
            <name>coverage</name>
        </property>
    </activation>
    <properties>
        <jacoco.version>0.8.12</jacoco.version>
        <sonar-maven-plugin.version>4.0.0.4121</sonar-maven-plugin.version>
        <sonar.junit.reportPaths>${project.build.directory}/surefire-integtest-reports,${project.build.directory}/surefire-unittest-reports,${project.build.directory}/surefire-archtest-reports</sonar.junit.reportPaths>
        <maven.test.skip>false</maven.test.skip>
        <maven-surefire-plugin.argLine.ut>${unitTestArgLine} ${maven-surefire-plugin.argLine}</maven-surefire-plugin.argLine.ut>
        <maven-surefire-plugin.argLine.it>${integTestArgLine} ${maven-surefire-plugin.argLine}</maven-surefire-plugin.argLine.it>
        <maven-surefire-plugin.argLine.au>${archUnitArgLine} ${maven-surefire-plugin.argLine}</maven-surefire-plugin.argLine.au>
    </properties>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.sonarsource.scanner.maven</groupId>
                    <artifactId>sonar-maven-plugin</artifactId>
                    <version>${sonar-maven-plugin.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.jacoco</groupId>
                    <artifactId>jacoco-maven-plugin</artifactId>
                    <version>${jacoco.version}</version>
                    <configuration>
                        <!-- Skip 3rd party library files-->
                        <excludes>**/*.jar</excludes>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <executions>
                    <execution>
                        <id>pre-unit-test</id>
                        <phase>process-test-classes</phase>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <destFile>${project.build.directory}/jacoco-ut.exec</destFile>
                            <propertyName>unitTestArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>pre-integration-test</id>
                        <phase>process-test-classes</phase>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <destFile>${project.build.directory}/jacoco-it.exec</destFile>
                            <propertyName>integTestArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>pre-archunit-test</id>
                        <phase>process-test-classes</phase>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <destFile>${project.build.directory}/jacoco-au.exec</destFile>
                            <propertyName>archUnitArgLine</propertyName>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

In de rapportagemodule kan een profiel worden gebruikt voor de aggregatie van rapporten en de configuratie van de SonarQube-server. De ‘sonar-maven-plugin’ of ‘Sonar Scanner’ kan worden gebruikt voor analyse. De rapportage-analyse moet worden uitgevoerd door een JVM-versie die door de SonarQube-server wordt ondersteund. Lokaal kan je de SonarQube-server starten met ‘mvn sonar’, waarbij de resultaten naar een lokale SonarQube-server in een Docker-omgeving worden gestuurd.

Raportage pom

<profile>
    <id>coverage</id>
    <activation>
        <property>
            <name>coverage</name>
        </property>
    </activation>
    <properties>
        <sonar.language>java</sonar.language>
        <sonar.projectKey>Voorbeeld</sonar.projectKey>
        <sonar.projectName>Voorbeeld</sonar.projectName>
        <sonar.host.url>http://localhost:9000</sonar.host.url>
        <sonar.token>...</sonar.token>
        <sonar.coverage.jacoco.xmlReportPaths>${project.reporting.outputDirectory}/jacoco-aggregate/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <executions>
                    <execution>
                        <id>site-aggregate</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report-aggregate</goal>
                        </goals>
                        <configuration>
                            <includeCurrentProject>true</includeCurrentProject>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.sonarsource.scanner.maven</groupId>
                <artifactId>sonar-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</profile>

IntelliJ

IntelliJ ondersteunt het lezen van Jacoco-rapportagebestanden, wat handig kan zijn om inzicht te krijgen in de codecoverage. De configuratie is beschikbaar via het menu: Run -> Manage Coverage Reports. Hier kun je een ‘jacoco.xml’-bestand selecteren. Houd er rekening mee dat nieuwe Jacoco-rapporten handmatig moeten worden ingeladen.

SonarLint

IntelliJ kan worden uitgebreid met de SonarLint-plugin, die je kunt koppelen aan de SonarQube-server. Deze plugin geeft directe feedback in de IDE en kan helpen om technical debt te identificeren. Het is aan te raden om een strategie af te spreken voor het aanpakken van technical debt, bijvoorbeeld door kleine zaken direct op te lossen en grotere aanpassingen te markeren met een TODO.

SonarQube server

De SonarQube-server heeft verschillende edities, van Enterprise tot Community. De server kan uitgebreid worden geconfigureerd en geïntegreerd met ontwikkelplatforms. Verken de mogelijkheden door de server in een Docker-omgeving te draaien en de configuratie te testen.

Voorbeeld van een docker compose file met SonarQube en postgres. Afhankelijk van welk OS er gebruikt wordt kunnen de volumes verplaatst worden. De ‘/opt/…’ kunnen aangepast worden naar waar de volumes passen.

docker-compose.yml

services:
  SonarQube:
    image: SonarQube:community
    command: -Dsonar.ce.javaOpts=-Xmx1192m -Dsonar.web.javaOpts=-Xmx1192m
    restart: unless-stopped
    container_name: SonarQube
    depends_on:
      - SonarQube_db
    ports:
      - "9000:9000"
    networks:
      - sonar-net
    environment:
      - SonarQube_JDBC_URL=jdbc:postgresql://SonarQube_db:5432/sonar
      - SonarQube_JDBC_USERNAME=sonar
      - SonarQube_JDBC_PASSWORD=sonar
    volumes:
      - /opt/sonar/data:/opt/SonarQube/data
      - /opt/sonar/extensions:/opt/SonarQube/extensions
      - /opt/sonar/logs:/opt/SonarQube/logs

  SonarQube_db:
    image: postgres
    container_name: SonarQube_db
    restart: unless-stopped
    networks:
      - sonar-net
    environment:
      - POSTGRES_USER=sonar
      - POSTGRES_PASSWORD=sonar
    volumes:
      - /opt/postgresql:/var/lib/postgresql
      - /opt/postgresql/data:/var/lib/postgresql/data

networks:
  sonar-net:
    external: false

De Sonarcloud en SonarQube serverconfiguratie is zo uitgebreid dat het even kan duren alvorens dit onder de knie te hebben. In de praktijk zul je afhankelijk van het project eigen kwaliteitsprofielen en condities willen inzetten die aansluiten op je wensen.

Naast de standaardprofielen is het ook mogelijk om andere profielen uit de Marketplace toe te voegen. Houd er rekening mee dat aanvullende regels per geval nuttig of minder nuttig kunnen zijn. Hoewel het mogelijk is om eigen regels te definiëren, voldoen de beschikbare regels in de meeste gevallen.

Nieuwsgierig of maatwerk voor jou het verschil kan maken?

Neem contact met ons op voor maatwerkoplossingen in Java, geleverd door een ervaren team dat stabiliteit, continuïteit en optimalisatie garandeert voor jouw bedrijfsprojecten.

Toestemming