loading...

Grails and Sonarqube

jagedn profile image Jorge Eψ=Ĥψ Originally published at jorge.aguilera.soy on ・3 min read

Recently I’ve had to add SonarQube to our Grails application to gain visibility in our metrics and though at the end the change was only a few of config lines I think can be interesting to document it in this post due the lack of updated documentation about it

Grails is a groovy-lang framework where you can develop a CRUD web application in a few seconds with a great plugin ecosystem. It has close to 10 years and it has evolved from initial heavy versions to a more light framework. Currently I’m working in a 3.x application althouht version 4.x is out a few months ago.

We’re using some plugins as Codenarc and Coverage how generate reports about the quality of your code (rule violations, check style, coverture, and so on) and we have some asserts to abort the build if we’re in a low coverture situtation for example

SonarQube is an open-source platform developed by SonarSource for continuous inspection of code quality to perform automatic reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities on 20+ programming languages. SonarQube offers reports on duplicated code, coding standards, unit tests, code coverage, code complexity, comments, bugs, and security vulnerabilities. ( https://en.wikipedia.org/wiki/SonarQube )

With SonarQube you can have a good dashboard your QA team will appreciate, so we’ll try to integrate our gradle build with it.

Docker SonarQube

Firstly you need to have a SonarQube instance running due the build needs to comunicate with it to send the information. The idea is to have an instance for multiple projects and in this way compare between them, align force, styles etc.

For development I’ll used a docker instance at port 9000:

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

Grails

This is part of our build.gradle configuration (nothing special to comment) where, plus standard Grails configuration, we’ve codenarc and clover installed

buildscript {
    repositories {
        mavenLocal()
        maven { url "https://repo.grails.org/grails/core" }
        maven { url "https://plugins.gradle.org/m2/" }
        jcenter()
    }
    dependencies {
        classpath "org.grails:grails-gradle-plugin:$grailsVersion"
        classpath "org.grails.plugins:hibernate4:${gormVersion - ".RELEASE"}"
        classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.14.2"
        classpath 'com.bmuschko:gradle-clover-plugin:2.2.3'
        // others stuff
    }
}

// others plugins
apply plugin: "codenarc"
apply plugin: 'com.bmuschko.clover'

dependencies {
    clover 'org.openclover:clover:4.4.1'
}

codenarc {
    config = file('conf/codenarc-rulsets.groovy')
    //... some more configs
}

clover {
    excludes = ['/test/**',]

    testIncludes = ['**/*Spec.groovy']

    compiler {

    }
    compiler {
        encoding = 'UTF-8'

        // used to add debug information for Spring applications
        debug = true
        additionalArgs = '-Dclover.pertest.coverage=off'
        additionalGroovycOpts = [configscript: project.file('cloverXtraConfig.groovy').absolutePath]
    }

    report {
        html = true
        xml = true
        pdf = true
        columns{
            complexity format: 'raw'
            coveredBranches format: '%'
            totalBranches format: 'raw'
            coveredStatements format: '%'
            lineCount format: 'raw'
        }
    }
}
cloverGenerateReport.doLast {
    run(new File("scripts/AbortIfNotCoverage.groovy"))
}

Grails & SonarQube

First thing I did was a quick search with grails sonarqubeterms and I realized more usefull post were a little out of date (2014-2015)

After reading some of these posts I was confused because in some of them I understood the required plugins need to be installed in local project downloading some jars but in others talking to download to a sonarqube directory.

So after some investigation the photo finish is:

  • you need to install in your Grails (Gradle) project only the sonarqube plugin

  • you install the groovy and coverage plugins via sonarqube web application

  • at the build time the plugin downloads both of them (and others), perfom the analisys and send the result to the backend

To sumarize, we only need to include the sonarqube plugin and set some properties:

buildscript {
    ...
    dependencies {
        ...
        classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8"
    }
}

...
apply plugin: "org.sonarqube"

...
sonarqube {
    properties {
        property 'sonar.verbose', 'true'
        property "sonar.host.url", "http://localhost:9000/" (1)
        property "sonar.sourceEncoding","UTF-8"
        property "sonar.projectName", "hello-grails"
        property "sonar.projectKey", "hello-grails"

        property "sonar.dynamicAnalysis","reuseReports" (2)
        property "sonar.clover.reportPath", file("build/reports/clover/clover.xml").absolutePath

        property "sonar.projectBaseDir",""
        property "sonar.sources","grails-app,src/main/groovy" (3)
        property "sonar.exclusions"," **/*.properties,** /*.js,**/*.html"
        property "sonar.test.exclusions","**/*.properties"
    }
}

| 1 | Only to test, needed to change to your QA instance. You can overwrite via -D or -P |
| 2 | We’ll use the report generated by clover |
| 3 | Sonar will scan all the grails applicaion plus src except js and html |

Sonarqube dashboard

These are some images from official page to show you what kind of information you can obtain

Dashboards Global

Dashboards Project

More information

If you want to read more about Sonarqube these links are helpfull

Posted on by:

jagedn profile

Jorge Eψ=Ĥψ

@jagedn

groovy/grails developper micronaut beginner

Discussion

markdown guide
 

hi thx for the article, it was very helpful.

what's in your cloverXtraConfig.groovy & AbortIfNotCoverage.groovy files .

Cheers !

 

true, I forgotten them

I use cloverXtraConfig.groovy to remove CompileStatic and TypeChecked annotations:

withConfig(configuration) {
    inline(phase: 'CONVERSION') { source, context, classNode ->
        source.ast.unit.classes.each { clazz ->
            clazz.annotations.removeAll { annotation -> annotation.classNode.name in ['CompileStatic', 'TypeChecked'] }
        }
    }
}

and in AbortIfNoCoverage.groovy I parse the final xml to calculate the average cobertura and if it's under a limit throw a exception to abort the pipeline