3 Steps For Analyzing a Gradle Project With SonarQube Using Docker
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.
Gradle is an open source build automation system.
Docker allows you to package an application with all of its dependencies into a standardized unit for software development.
“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”.
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 pullstpires/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:
1 | git clone https: //github.com/orangesignal/docker-sonarqube.git |
The directory layout is:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | 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
1 | tvinke @picard ~/docker-sonarqube $ docker-compose up |
This starts the whole shebang.
01 02 03 04 05 06 07 08 09 10 11 | ... 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:
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:
1 | 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):
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | 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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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 { } maven { } 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 { } } } dependencies { testCompile 'junit:junit:4.12' } |
As you can see there are a few plugins:
1 2 3 | 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:
1 2 3 4 5 6 7 | 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:
1 | gradle sonarqube |
With actually default SonarQube settings this would have worked and a local H2 database would have been found.
Where is SonarQube actually?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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
1 2 3 4 | 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):
1 2 3 | gradle sonarqube -Dsonar.jdbc.url=jdbc:postgresql: //localhost:5432/sonar -Dsonar.verbose= true |
SonarQube starts and finishes analyzing this small project quickly:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 | 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’s own example project has 2 (deliberate) issues:
Issues overview says what’s really going on:
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
- SonarQube website – http://www.sonarqube.org/
- orangesignal/sonar on DockerHub and GitHub
- Sonarqube.org wiki
- Setup and Upgrade > Installing and Configuring Gradle Sonar Plugin
- Analyzing Source Code > Analyzing with Gradle
- Gradle.org docs – https://plugins.gradle.org/plugin/org.sonarqube
- JaCoCo Java Code Coverage homepage – http://eclemma.org/jacoco/
Reference: | 3 Steps For Analyzing a Gradle Project With SonarQube Using Docker from our JCG partner Ted Vinke at the Ted Vinke’s Blog blog. |