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:
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:
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’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. |