How hard can it be to get up and running with a Gradle project and getting it analyzed with SonarQube locally?

The ingredients are:

SonarQube (formerly Sonar) is an open source platform for continuous inspection of code quality.

sonarqube-logo

Gradle is an open source build automation system.

gradle-logo

Docker allows you to package an application with all of its dependencies into a standardized unit for software development.

docker-logo

“How easy is this?” was a question I had after I decided to reformat my company HP EliteBook with Windows into a full Linux Mint machine.

So, I’ve already played around with Docker and IMHO this works much more pleasant under Linux, so I thought it’s time to play around with it some more and may be get a project analyzed by SonarQube without too much hassle.

So, how easy is this?

Step 1 – Getting SonarQube with Docker

First thing I went to look for was a Docker image which already has SonarQube in it. This is the future IMHO: running whatever components you need inside a container.

At least for development and testing purposes this is a great win: no matter the underlying OS or personal work environment, the image is what it is, and as long as you can run Docker you’ll know “exactly” what will be running.

No time to create an image for now, just get one someone already shared in one’s own company or on DockerHub.

Docker Hub is the canonical platform for publishing and consuming Docker container images.

Just searched for “sonar”.

dockerhub-search-for-sonar

243 reposiories at the moment. There were 2 which caught my attention:

  • sonarqube – which is official. Has 165 stars and 243 thousand pulls.
  • orangesignal/sonar which has 2 stars and 378 pulls
  • tpires/sonar-server which has 18 stars and 94 thousand pulls.

Although numbers #1 and #3 seem to have a higher stars/pulls combination, I went for orangesignal/sonar because it used Docker Compose.

Docker Compose

On orangesignal/sonar’s DockerHub page a GitHub repo is referenced. Cloned it:

git clone https://github.com/orangesignal/docker-sonarqube.git

The directory layout is:

tvinke@picard ~ $ cd docker-sonarqube/
tvinke@picard ~/docker-sonarqube $ tree
.
├── 3.6
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 3.7
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 4.0
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 4.1
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 4.2
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 4.3
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 4.4
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 4.5
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 5.0
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── 5.1
│   ├── docker-entrypoint.sh
│   └── Dockerfile
├── docker-build.bash
├── docker-compose.yml
├── example
│   ├── docker-compose.yml
│   ├── docker-entrypoint.sh
│   ├── Dockerfile
│   └── nginx-conf.d
│       └── default.conf
├── LICENSE
├── README.md
├── remove_all_stopped_containers.bash
└── remove_all_untagged_images.bash

12 directories, 30 files

Various versions of SonarQube (actually Dockerfiles ofcourse) and an interesting docker-compose.yml which contains the “composition”.

docker-compose.yml

db:
  image: postgres:9
  hostname: pgsql-01
  ports:
    - 5432:5432
  environment:
    - POSTGRES_DB=sonar
    - POSTGRES_USER=sonar
    - POSTGRES_PASSWORD=sonar

sonar:
  image: orangesignal/sonar:latest
  hostname: sonar-01
  links:
    - db
  ports:
    - 9000:9000
  environment:
    - SONAR_JDBC_URL=jdbc:postgresql://pgsql-01:5432/sonar
    - SONAR_JDBC_USERNAME=sonar
    - SONAR_JDBC_PASSWORD=sonar

Instead of H2 or MySQL this says to use a Postgres database, using the Docker image postgres:9 and run it under a certain host name and port. It is followed by the part where the latest published version of the orangesignal/sonar image is referred to and linked with the Postgres database.

I had Docker already installed, but I yet had to install Docker Compose to be able to run

tvinke@picard ~/docker-sonarqube $ docker-compose up

This starts the whole shebang.

...
sonar_1 | 2016.02.26 10:24:35 INFO  web[o.s.s.s.IndexSynchronizer] Index source lines
sonar_1 | 2016.02.26 10:24:35 INFO  web[o.s.s.s.IndexSynchronizer] Index users
sonar_1 | 2016.02.26 10:24:35 INFO  web[o.s.s.s.IndexSynchronizer] Index views
sonar_1 | 2016.02.26 10:24:35 INFO  web[jruby.rack] jruby 1.7.9 (ruby-1.8.7p370) 2013-12-06 87b108a on Java HotSpot(TM) 64-Bit Server VM 1.8.0_66-b17 [linux-amd64]
sonar_1 | 2016.02.26 10:24:35 INFO  web[jruby.rack] using a shared (threadsafe!) runtime
sonar_1 | 2016.02.26 10:24:41 INFO  web[jruby.rack] keeping custom (config.logger) Rails logger instance
sonar_1 | 2016.02.26 10:24:41 INFO  web[o.a.c.h.Http11NioProtocol] Starting ProtocolHandler ["http-nio-0.0.0.0-9000"]
sonar_1 | 2016.02.26 10:24:41 INFO  web[o.s.s.a.TomcatAccessLog] Web server is started
sonar_1 | 2016.02.26 10:24:41 INFO  web[o.s.s.a.EmbeddedTomcat] HTTP connector enabled on port 9000
sonar_1 | 2016.02.26 10:24:42 INFO  app[o.s.p.m.Monitor] Process[web] is up

Accessing the SonarQube Dashboard

On http://localhost:9000 now SonarQube is serving:

sonarqube-dashboard

No projects analyzed yet.

Step 2 – Getting Example Gradle Project

I needed an example Gradle project.

I could have chosen to create a clean, Java-based Gradle project from scratch, but adding some sources and tests to analyze would make me go over the 5 minute time-limit I set for myself.

Best option is to just use a project from SonarQube’s own examples GitHub repository:

tvinke@picard ~/workspace $ git clone https://github.com/SonarSource/sonar-examples.git

The directory /projects contains sample projects for:

  • the different analyzers (SonarQube Runner, Maven, Ant)
  • the different languages (Java, Cobol, .Net, etc.)
  • the different ways to execute unit tests and get code coverage data

Examples use the Sonar Runner, which is a deprecated plugin now. Luckily, the java-gradle-simple project was exactly what I needed.

Output of tree (another beautiful Linux tool):

tvinke@picard ~/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple $ tree
.
├── build.gradle
├── README.md
└── src
    ├── main
    │   └── java
    │       └── example
    │           └── Greeting.java
    └── test
        └── java
            └── example
                ├── FailingTest.java
                └── GreetingTest.java

7 directories, 5 files

Some stuff I hoped SonarQube could report something about. What I was looking for was an example of a proper build.gradle using the Sonar Gradle plugin.

build.gradle

apply plugin: 'java'
apply plugin: 'org.sonarqube'
apply plugin: 'jacoco'

allprojects {  
  ext.baseVersion = "0.1"
  ext.snapshotVersion = true
  
  group = "org.sonarqube"
  version = "$baseVersion" + (snapshotVersion ? "-SNAPSHOT" : "")
}

sonarqube {
    properties {
        property "sonar.projectName", "Java :: Simple Project :: SonarQube Scanner for Gradle"
        property "sonar.projectKey", "org.sonarqube:java-gradle-simple"
        property "sonar.jacoco.reportPath", "${project.buildDir}/jacoco/test.exec"
    }
}

buildscript {
    repositories { 
      maven {
        url "http://repo1.maven.org/maven2/"
      }
      maven {
        url "https://plugins.gradle.org/m2/"
      }
      mavenLocal()
    }
    dependencies { 
      classpath 'org.ajoberstar:gradle-jacoco:0.1.0'
      classpath 'org.sonarqube.gradle:gradle-sonarqube-plugin:1.0'
    }
}

test {
  ignoreFailures = true
}

repositories { 
  repositories { 
    maven {
      url "http://repo1.maven.org/maven2/"
    }
  }
}

dependencies {
    testCompile 'junit:junit:4.12'
}

As you can see there are a few plugins:

apply plugin: 'java'
apply plugin: 'org.sonarqube'
apply plugin: 'jacoco'

Besides the Java plugin, there’s also the SonarQube plugin and JaCoCo plugin. The latter provides Java code coverage metrics using the JaCoCo (“Java Code Coverage”) library, created by the EclEmma team.

Furthermore there some other stuff configured, such as Sonar properties:

sonarqube {
    properties {
        property "sonar.projectName", "Java :: Simple Project :: SonarQube Scanner for Gradle"
        property "sonar.projectKey", "org.sonarqube:java-gradle-simple"
        property "sonar.jacoco.reportPath", "${project.buildDir}/jacoco/test.exec"
    }
}

Part is for identification the project inside SonarQube, for if you’re using it for multiple projects.

I left everything as-is because I can just start…

Step 3 – Analyzing Using Gradle Sonar Plugin

The Gradle Sonar plugin uses all kinds of defaults and uses much of the information from the Gradle build to analyze a project.

So I hoped Things Just Worked (which is kind of a pitfall if everyhing goes smooth as of far – with minimal configuration) so just see what happens if we do:

gradle sonarqube

With actually default SonarQube settings this would have worked and a local H2 database would have been found.

Where is SonarQube actually?

INFO: Work directory: /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple/build/sonar
INFO: SonarQube Server 5.1.2
11:38:15.164 INFO  - Load global repositories
11:38:15.281 INFO  - Load global repositories (done) | time=119ms
11:38:15.283 INFO  - Server id: 20160226102431
11:38:15.285 INFO  - User cache: /home/tvinke/.sonar/cache
11:38:15.292 INFO  - Install plugins
11:38:15.329 INFO  - Install JDBC driver
11:38:15.335 INFO  - Create JDBC datasource for jdbc:h2:tcp://localhost/sonar
:sonarqube FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sonarqube'.
> Unable to execute Sonar

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 2.41 secs

Unfortunately, our docker-compose.yml says otherwise – we’re using a Postgres database instead with a different JDBC settings – not an H2 database on jdbc:h2:tcp://localhost/sonar.

We can either put override the defaults by setting some global properties in ~/.gradle/gradle.properties like

systemProp.sonar.host.url=http://localhost:9000
systemProp.sonar.jdbc.url=jdbc:postgresql://localhost/sonar
systemProp.sonar.jdbc.username=sonar
systemProp.sonar.jdbc.password=sonar

Or pass it on the command-line (keeping the defaults):

gradle sonarqube 
 -Dsonar.jdbc.url=jdbc:postgresql://localhost:5432/sonar
 -Dsonar.verbose=true

SonarQube starts and finishes analyzing this small project quickly:

tvinke@picard ~/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple $ gradle sonarqube -Dsonar.jdbc.url=jdbc:postgresql://localhost:5432/
sonar
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:sonarqube
INFO: Default locale: "en_US", source code encoding: "UTF-8" (analysis is platform dependent)
INFO: Work directory: /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple/build/sonar
INFO: SonarQube Server 5.1.2
12:00:29.104 INFO  - Load global repositories
12:00:29.184 INFO  - Load global repositories (done) | time=81ms
12:00:29.185 INFO  - Server id: 20160226102431
12:00:29.186 INFO  - User cache: /home/tvinke/.sonar/cache
12:00:29.192 INFO  - Install plugins
12:00:29.219 INFO  - Install JDBC driver
12:00:29.223 INFO  - Create JDBC datasource for jdbc:postgresql://localhost:5432/sonar
12:00:29.758 INFO  - Initializing Hibernate
12:00:30.421 INFO  - Load project repositories
12:00:30.574 INFO  - Load project repositories (done) | time=153ms
12:00:30.574 INFO  - Load project settings
12:00:30.786 INFO  - Load technical debt model
12:00:30.801 INFO  - Apply project exclusions
12:00:30.939 WARN  - 'sonar.dynamicAnalysis' is deprecated since version 4.3 and should no longer be used.
12:00:30.954 WARN  - SCM provider autodetection failed. No SCM provider claims to support this project. Please use sonar.scm.provider to define SCM of your project.
12:00:30.955 INFO  - -------------  Scan Java :: Simple Project :: SonarQube Scanner for Gradle
12:00:30.959 INFO  - Load module settings
12:00:31.025 INFO  - Load rules
12:00:31.231 INFO  - Base dir: /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple
12:00:31.231 INFO  - Working dir: /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple/build/sonar
12:00:31.232 INFO  - Source paths: src/main/java
12:00:31.232 INFO  - Test paths: src/test/java
12:00:31.233 INFO  - Binary dirs: build/classes/main
12:00:31.233 INFO  - Source encoding: UTF-8, default locale: en_US
12:00:31.233 INFO  - Index files
12:00:31.241 INFO  - 3 files indexed
12:00:31.314 INFO  - Quality profile for java: Sonar way
12:00:31.326 INFO  - Sensor JavaSquidSensor
12:00:31.513 INFO  - Configured Java source version: 7
12:00:31.746 INFO  - Java Main Files AST scan...
12:00:31.748 INFO  - 1 source files to be analyzed
12:00:31.950 INFO  - Java Main Files AST scan done: 204 ms
12:00:31.950 INFO  - 1/1 source files have been analyzed
12:00:31.951 INFO  - Java bytecode scan...
12:00:31.964 INFO  - Java bytecode scan done: 13 ms
12:00:31.964 INFO  - Java Test Files AST scan...
12:00:31.964 INFO  - 2 source files to be analyzed
12:00:32.011 INFO  - Java Test Files AST scan done: 47 ms
12:00:32.011 INFO  - 2/2 source files have been analyzed
12:00:32.013 INFO  - Package design analysis...
12:00:32.015 INFO  - Package design analysis done: 2 ms
12:00:32.016 INFO  - Sensor JavaSquidSensor (done) | time=690ms
12:00:32.016 INFO  - Sensor Lines Sensor
12:00:32.017 INFO  - Sensor Lines Sensor (done) | time=1ms
12:00:32.018 INFO  - Sensor QProfileSensor
12:00:32.021 INFO  - Sensor QProfileSensor (done) | time=4ms
12:00:32.021 INFO  - Sensor InitialOpenIssuesSensor
12:00:32.032 INFO  - Sensor InitialOpenIssuesSensor (done) | time=11ms
12:00:32.032 INFO  - Sensor ProjectLinksSensor
12:00:32.039 INFO  - Sensor ProjectLinksSensor (done) | time=7ms
12:00:32.039 INFO  - Sensor VersionEventsSensor
12:00:32.057 INFO  - Sensor VersionEventsSensor (done) | time=18ms
12:00:32.057 INFO  - Sensor SurefireSensor
12:00:32.057 INFO  - parsing /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple/build/test-results
12:00:32.100 INFO  - Sensor SurefireSensor (done) | time=43ms
12:00:32.100 INFO  - Sensor JaCoCoOverallSensor
12:00:32.105 WARN  - You are not using the latest JaCoCo binary format version, please consider upgrading to latest JaCoCo version.
12:00:32.106 INFO  - Analysing /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple/build/jacoco/test.exec
12:00:32.117 WARN  - You are not using the latest JaCoCo binary format version, please consider upgrading to latest JaCoCo version.
12:00:32.117 INFO  - Analysing /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple/build/sonar/jacoco-overall.exec
12:00:32.151 INFO  - No information about coverage per test.
12:00:32.152 INFO  - Sensor JaCoCoOverallSensor (done) | time=52ms
12:00:32.152 INFO  - Sensor SCM Sensor
12:00:32.152 INFO  - No SCM system was detected. You can use the 'sonar.scm.provider' property to explicitly specify it.
12:00:32.152 INFO  - Sensor SCM Sensor (done) | time=0ms
12:00:32.152 INFO  - Sensor JaCoCoSensor
12:00:32.152 WARN  - You are not using the latest JaCoCo binary format version, please consider upgrading to latest JaCoCo version.
12:00:32.153 INFO  - Analysing /home/tvinke/workspace/sonar-examples/projects/languages/java/gradle/java-gradle-simple/build/jacoco/test.exec
12:00:32.155 INFO  - No information about coverage per test.
12:00:32.155 INFO  - Sensor JaCoCoSensor (done) | time=3ms
12:00:32.155 INFO  - Sensor CPD Sensor
12:00:32.155 INFO  - JavaCpdEngine is used for java
12:00:32.155 INFO  - Cross-project analysis disabled
12:00:32.169 INFO  - Sensor CPD Sensor (done) | time=14ms
12:00:32.170 INFO  - No quality gate is configured.
12:00:32.195 INFO  - Compare to previous analysis (2016-02-26)
12:00:32.197 INFO  - Compare over 30 days (2016-01-27, analysis of Fri Feb 26 09:54:19 CET 2016)
12:00:32.501 INFO  - Execute decorators...
12:00:33.012 INFO  - Store results in database
12:00:33.177 INFO  - Analysis reports generated in 13ms, dir size=1002 bytes
12:00:33.181 INFO  - Analysis reports compressed in 4ms, zip size=2 KB
12:00:33.211 INFO  - Analysis reports sent to server in 29ms
12:00:33.211 INFO  - ANALYSIS SUCCESSFUL, you can browse http://localhost:9000/dashboard/index/org.sonarqube:java-gradle-simple
12:00:33.211 INFO  - Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.

BUILD SUCCESSFUL

Total time: 5.274 secs

The result:

ANALYSIS SUCCESSFUL

The Dashboard of SonarQube now lists our Simple Java Project. The overview of the project now reveals much information about the state of the current code base.

sonarqube-project-analyzed

SonarQube’s own example project has 2 (deliberate) issues:

sonarqube-simple-java-project-issues

Issues overview says what’s really going on:

sonarqube-simple-java-project-issues-overview.png

sonarqube-simple-java-project-issue-detail

sonarqube-simple-java-project-issue-more-detail

So tho answer my original question: it isn’t that hard to have a Gradle project analyzed by a locally running SonarQube installation.

Since I already had Docker and Docker Compose installed it took “5 minutes” for us to get here, but it will take you 10 more minutes to squash the technical debt.

Now, what are you waiting for – go fix it! 🙂

Further Reading

Advertisements