Breaking the SonarQube Analysis with Jenkins Pipelines

by julien henry|

    One of the most requested feature regarding SonarQube Scanners is the ability to fail the build when quality level is not at the expected level. We have this built-in concept of quality gate in SonarQube, and we used to have a BuildBreaker plugin for this exact use case. But starting from version 5.2, aggregation of metrics is done asynchronously on SonarQube server side. It means build/scanner process would finish successfully just after publishing raw data to the SonarQube server, without waiting for the aggregation to complete.

    Some people tried to resurrect the BuildBreaker feature by implementing some active polling at the end of the scanner execution. We never supported this solution, since it defeats one of the benefit of having asynchronous aggregation on SonarQube server side. Indeed it means your CI executors/agents will be occupied "just" for a wait.

    The cleanest pattern to achieve this is to release the CI executor, and have the SonarQube server send a notification when aggregation is completed. The CI job would then be resumed, and take the appropriate actions (not only mark the job as failed, but it could also send email notifications for example).

    All of this is now possible, thanks to the webhook feature introduced in SonarQube 6.2. We are also taking benefit of Jenkins pipeline feature, that allow some part of a job logic to be executed without occupying an executor.

    Let's see it in action.

    First, you need SonarQube server 6.2+. In your Jenkins instance, install latest version of the SonarQube Scanner for Jenkins (2.6.1+). You should of course configure in Jenkins administration section the credentials to connect to the SonarQube server.

    In your SonarQube server administration page, add a webhook entry:

    https://<your Jenkins instance>/sonarqube-webhook/

    Now you can configure a pipeline job using the two SonarQube keywords 'withSonarQubeEnv' and 'waitForQualityGate'.

    The first one should wrap the execution of the scanner (that will occupy an executor) and the second one will 'pause' the pipeline in a very light way, waiting for the webhook payload.

    node {
      stage('SCM') {
        git 'https://github.com/foo/bar.git'
      }
      stage('build & SonarQube Scan') {
        withSonarQubeEnv('My SonarQube Server') {
          sh 'mvn clean package sonar:sonar'
        } // SonarQube taskId is automatically attached to the pipeline context
      }
    }
     
    // No need to occupy a node
    stage("Quality Gate") {
      timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout
        def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
        if (qg.status != 'OK') {
          error "Pipeline aborted due to quality gate failure: ${qg.status}"
        }
      }
    }
    

    Here you are:

    That's all Folks!