Add SonarQube quality gates to your Jenkins build pipeline

Add SonarQube quality gates to your Jenkins builds

SonarQube is an excellent tool for measuring code quality, using static analysis to find code smells, bugs, vulnerabilities, and poor test coverage. 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:

  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.

Fast track – if you want to just get things up and running quickly, check out this GitHub repository where everything is setup through configuration-as-code, except the steps under Configure SonarQube below.

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
  jenkins:
    image: jenkins/jenkins:2.249.2-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

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.

Networking in Docker Compose – the reason the SonarQube URL is http://sonarqube:9000 is because by default Docker Compose allows any service to call any other service in the same network. The way you do this is by using the service name as the hostname in the request URL, as defined in docker-compose.yml. This is why we use a host of sonarqube.

Configuring SonarQube

Let’s jump over to SonarQube, click on Log in at the top-right of the page, and log in with the default credentials of admin/admin.

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.

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 😉

Click Save then on the next screen click Add Condition. 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.

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. For more information about running SonarQube analysis in a Gradle build see this article
  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.

SonarQube magic – all the withSonarQubeEnv method is doing is exporting some environment variables that the project’s build understands. By adding an additional pipeline step which runs the command printenv wrapped in withSonarQubeEnv, you’ll be able to see environment variables such as SONAR_HOST_URL being set. These will get picked up by the Gradle build of the code project to tell it which SonarQube server to connect to.

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.

If we head over to SonarQube we can see that indeed our project has passed the 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. 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.

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.

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:

  • configuring the webhook in Jenkins to require an authentication token (find this in the SonarQube Scanner plugin configuration)
  • considering 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.

For full details about setting up SonarQube analysis in a Gradle code project, see How To Measure Code Coverage Using SonarQube and Jacoco. If you’re using Maven, check out this documentation from 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 configure SonarQube as described in the Configure SonarQube section.
  • the SonarQube Scanner Jenkins plugin documentation has some useful details
Add SonarQube quality gates to your Jenkins build pipeline

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top

Get the newsletter

Found this article helpful? Subscribe for monthly updates.

✅ All of my latest articles for the month
✅ Access to video tutorials
✅ Exclusive tips not found on my website