Rather than manually analysing the reports, why not automate the process by integrating SonarQube with your Jenkins continuous integration pipeline? This way, you can configure a quality gate based on your own requirements, ensuring bad code always fails the build.

You’ll learn exactly how to do that in this article, through a full worked example where we add SonarQube analysis and SonarQube quality gate stages to a Jenkins pipeline.

SonarQube refresher

SonarQube works by running a local process to scan your project, called the SonarQube scanner. This sends reports to a central server, known as the SonarQube server.

The SonarQube server also has a UI where you can browse these reports. They look like this:

Quality gates

In SonarQube a quality gate is a set of conditions that must be met in order for a project to be marked as passed. In the above example the project met all the conditions.

Here’s an example where things didn’t go so well.

Clicking on the project name gives full details of the failure.

Here you can see here that a condition failed because the maintainability rating was a D rather than A.

sonarqube-failure-reason

SonarQube and Jenkins

Running a SonarQube scan from a build on your local workstation is fine, but a robust solution needs to include SonarQube as part of the continuous integration process. If you add SonarQube analysis into a Jenkins pipeline, you can ensure that if the quality gate fails then the pipeline won’t continue to further stages such as publish or release. After all, nobody wants to release crappy code into production. 👌

To do this, we can use the SonarQube Scanner plugin for Jenkins. It includes two features that we’re going to make use of today:

  1. SonarQube server configuration - the plugin lets you set your SonarQube server location and credentials. This information is then used in a SonarQube analysis pipeline stage to send code analysis reports to that SonarQube server.
  2. SonarQube Quality Gate webhook - when a code analysis report is submitted to SonarQube, unfortunately it doesn’t respond synchronously with the result of whether the report passed the quality gate or not. To do this, a webhook call must be configured in SonarQube to call back into Jenkins to allow our pipeline to continue (or fail). The SonarQube Scanner Jenkins plugin makes this webhook available.

Here’s a full breakdown of the interaction between Jenkins and SonarQube:

SonarQube and Jenkins interactions
  1. a Jenkins pipeline is started
  2. the SonarQube scanner is run against a code project, and the analysis report is sent to SonarQube server
  3. SonarQube finishes analysis and checking the project meets the configured Quality Gate
  4. SonarQube sends a pass or failure result back to the Jenkins webhook exposed by the plugin
  5. the Jenkins pipeline will continue if the analysis result is a pass or optionally otherwise fail

Full worked example

Let’s get our hands dirty with a worked example. We’ll run through all the steps in the UI manually as this is the best way to understand the setup.

In this example we’ll:

  1. get Jenkins and SonarQube up and running
  2. install the SonarQube Scanner Jenkins plugin and configure it to point to our SonarQube instance
  3. configure SonarQube to call the Jenkins webhook when project analysis is finished
  4. create two Jenkins pipelines
    • one that runs against a codebase with zero issues (I wish all my code was like this 😉)
    • one that runs against a codebase with bad code issues
  5. run the pipelines and see it all working

You’ll need to make sure you have Docker installed before carrying on.

Running Jenkins and SonarQube

What better way to start these two services than with Docker Compose? Create the following file docker-compose.yml:

version: "3"
services:
  sonarqube:
    image: sonarqube:lts
    ports:
      - 9000:9000
    networks:
      - mynetwork
    environment:
      - SONAR_FORCEAUTHENTICATION=false
  jenkins:
    image: jenkins/jenkins:2.319.1-jdk11
    ports:
      - 8080:8080
    networks:
      - mynetwork
networks:
  mynetwork:
  • we’re configuring two containers in Docker Compose: Jenkins and SonarQube
  • the Docker images used come from the official repositories in Docker Hub
  • we’re adding both containers to the same network so they can talk to each other
  • for demo purposes SonarQube authentication is disabled so Jenkins won’t need to pass a token

Running docker-compose up in the directory containing the file will start Jenkins on http://localhost:8080 and SonarQube on http://localhost:9000. Awesomeness!

Configuring the SonarQube Scanner Jenkins plugin

Grab the Jenkins administrator password from the Jenkins logs in the console output of the Docker Compose command you just ran.

jenkins_1    | Please use the following password to proceed to installation:
jenkins_1    |
jenkins_1    | 7efed7f025ee430c8938beaa975f5dde

Head over to your Jenkins instance and paste in the password.

On the next page choose Select plugins to install and install only the pipeline and git plugins. The SonarQube Scanner plugin we’ll have to install afterwards since this Getting Started page doesn’t give us the full choice of plugins.

In the final steps you’ll have to create a user and confirm the Jenkins URL of http://localhost:8080.

Once complete head over to Manage Jenkins > Manage Plugins > Available and search for sonar. Select the SonarQube Scanner plugin and click Install without restart.

Once the plugin is installed, let’s configure it!

Go to Manage Jenkins > Configure System and scroll down to the SonarQube servers section. This is where we’ll add details of our SonarQube server so Jenkins can pass its details to our project’s build when we run it.

Click the Add SonarQube button. Now add a Name for the server, such as SonarQube. The Server URL will be http://sonarqube:9000. Remember to click Save.

Configuring SonarQube

Let’s jump over to SonarQube. Click Log in at the top-right of the page, and log in with the default credentials of admin/admin. You’ll then have to set a new password.

Now go to Administration > Configuration > Webhooks. This is where we can add webhooks that get called when project analysis is completed. In our case we need to configure SonarQube to call Jenkins to let it know the results of the analysis.

Click Create, and in the popup that appears give the webhook a name of Jenkins, set the URL to http://jenkins:8080/sonarqube-webhook and click Create.

Configure webhook

In this case, the URL has the path sonarqube-webhook which is exposed by the SonarQube Scanner plugin we installed earlier.

Adding a quality gate

SonarQube comes with its own Sonar way quality gate enabled by default. If you click on Quality Gates you can see the details of this.

It’s all about making sure that new code is of a high quality. In this example we want to check the quality of existing code, so we need to create a new quality gate.

Click Create, then give the quality gate a name. I’ve called mine Tom Way 😉

Create quality gate

Click Save then on the next screen click Add Condition. Select On Overall Code. Search for the metric Maintainability Rating and choose worse than A. This means that if existing code is not maintainable then the quality gate will fail. Click Add Condition to save the condition.

Add SonarQube condition

Finally click Set as Default at the top of the page to make sure that this quality gate will apply to any new code analysis.

Creating Jenkins pipelines

Last thing to do is setup two Jenkins pipelines:

  1. A pipeline which runs against a code project over at the sonarqube-jacoco-code-coverage GitHub repository. The code here is decent enough that the pipeline should pass.
  2. A pipeline which runs against the same project, but uses the bad-code branch. The code here is so bad that the pipeline should fail.
Good code pipeline

Back in Jenkins click New Item and give it a name of sonarqube-good-code, select the Pipeline job type, then click OK.

Scroll down to the Pipeline section of the configuration page and enter the following declarative pipeline script in the Script textbox:

pipeline {
    agent any

    stages {
        stage('Clone sources') {
            steps {
                git url: 'https://github.com/tkgregory/sonarqube-jacoco-code-coverage.git'
            }
        }

        stage('SonarQube analysis') {
            steps {
                withSonarQubeEnv('SonarQube') {
                    sh "./gradlew sonarqube"
                }
            }
        }
        stage("Quality gate") {
            steps {
                waitForQualityGate abortPipeline: true
            }
        }
    }
}

The script has three stages:

  1. in the Clone sources stage code is cloned from the GitHub repository mentioned earlier
  2. in the SonarQube analysis stage we use the withSonarQubeEnv('Sonarqube') method exposed by the plugin to wrap the Gradle build of the code repository. This provides all the configuration required for the build to know where to find SonarQube. Note that the project build itself must have a way of running SonarQube analysis, which in this case is done by running ./gradlew sonarqube.
  3. in the Quality gate stage we use the waitForQualityGate method exposed by the plugin to wait until the SonarQube server has called the Jenkins webhook. The abortPipeline flag means if the SonarQube analysis result is a failure, we abort the pipeline.

Click Save to save the pipeline.

Bad code pipeline

Create another pipeline in the same way, but name it sonarqube-bad-code. The pipeline script is almost exactly the same, except this time we need to check out the bad-code branch of the same repository.

pipeline {
    agent any

    stages {
        stage('Clone sources') {
            steps {
                git branch: 'bad-code', url: 'https://github.com/tkgregory/sonarqube-jacoco-code-coverage.git'
            }
        }

        stage('SonarQube analysis') {
            steps {
                withSonarQubeEnv('SonarQube') {
                    sh "./gradlew sonarqube"
                }
            }
        }
        stage("Quality gate") {
            steps {
                waitForQualityGate abortPipeline: true
            }
        }
    }
}
  • in the Clone sources stage we’re now also specifying the branch attribute to point to the bad-code branch

Again, click Save.

You should now have two Jenkins jobs waiting to be run.

Any guesses as to what we’re going to do next?

SonarQube analysis and quality gate stages in action

Yes, that’s right, now it’s time to run our pipelines! ✅

Let’s run the sonarqube-good-code pipeline first.

You should get a build with all three stages passing.

Build pipeline output

If we head over to SonarQube we can see that indeed our project has passed the quality gate.

SonarQube passing quality gate

Now let’s run the sonarqube-bad-code pipeline. Remember this is running against some really bad code!

You’ll be able to see that the Quality gate stage of the pipeline has failed.

Jenkins quality gate build failure

Exactly what we wanted, blocking any future progress of this pipeline.

In the build’s Console Output you’ll see the message ERROR: Pipeline aborted due to quality gate failure: ERROR which shows that the pipeline failed for the right reason.

Over in SonarQube you’ll see that this time it’s reporting a Quality Gate failure.

Quality gate failure in SonarQube

Looks like we got some code smells on our hands!

Click on the project name for more details.

We can see that the maintainability rating has dropped to B because of the two code smells. This doesn’t meet our quality gate, which requires a minimum A rating.

Maintainability rating

Final thoughts

You’ve seen that integrating SonarQube quality gates into Jenkins is straightforward using the SonarQube Scanner Jenkins plugin. To apply this to a production setup, I suggest also to:

  • remove the SONAR_FORCEAUTHENTICATION environment variable from SonarQube & configure the webhook in Jenkins to require an authentication token (see the SonarQube Scanner plugin configuration)
  • consider running SonarQube analysis on feature branches, so developers get early feedback on whether their code changes are good before merging into master. However, multi-branch analysis does require a paid subscription to SonarQube.

Resources

  • the sonarqube-jenkins-example repository includes the example outlined in this article. Most of the manual steps from this article have been automated through configuration-as-code. All you need to do is configure SonarQube as described in the Configuring SonarQube section.
  • the SonarQube Scanner Jenkins plugin documentation has some useful details