When setting up multiple projects as a monorepo, how can I target Jenkins' scans to only the project which contains the code change?

Using Sonarqube 8.7 Enterprise Edition in a Docker container:

I am trying to scan a single monorepo. I have split each piece of the monorepo into a separate Project and encapsulated all the Projects having to do with the monorepo into a Portfolio. If I target a Pull Request (PR) that has changed code covered by the particular project everything works fine and the PR is decorated. However, if I run a Project against a PR where the code covered by the Project is unchanged Sonarqube doesn’t “ignore” or “fail”. Instead it successfully runs, adds the PR to the project, and decorates the PR with “0 Changes, Pass”.

When trying to automate these scans in Jenkins, how should I only scan a Project if there is a code change? The only way I can think to set up Sonarqube scans is to simply list every Project in a Jenkins pipeline and go down the list one-by-one. Can I have Sonarqube quit/fail/ignore if there are no code changes?


It sounds like you set your monorepo up manually before migrating to 8.7…? 8.7 adds native support in Enterprise Edition for monorepos. You don’t mention your ALM. At a guess, here’s the documentation for GitHub


I am using Github Enterprise. From your link:

In a mono repository setup, multiple SonarQube projects, each corresponding to a separate mono repository project, are all bound to the same GitHub repository. You’ll need to set up pull request decoration for each SonarQube project that is part of a mono repository.

So I did that and setup multiple SonarQube projects bound to the same GitHub repository. However, in Jenkins it seems you still must run sonar-scanner to scan each Project, not a single “Scan all Projects linked to this repository”. If I have a single Jenkins pipeline I want to use to scan the entire monorepo, the only way I can think is to scan each separate project in a list. I have created a Groovy function to loop through a list of projects:

def other_analysis(list){
    for (int i = 0; i < list.size(); i++){
        path = list[i].replace(":","/") // Replace the colons with '/' to get the relative path
        PROJECT_CHECK = sh(
            script: "git --no-pager diff --name-only \$(git merge-base --fork-point refs/remotes/origin/${env.CHANGE_TARGET})", // Get a list of the changed files in this branch/PR
            returnStdout: true
        if(PROJECT_CHECK.contains(path)){ // If the project path is contained in the changes...
            withSonarQubeEnv('SonarQube'){ //  run the SonarQube scan.
                sh """
                sonar-scanner \
                -Dsonar.projectKey=${list[i]} \
        } else { // Otherwise...
            echo "Skipping ${list[i]}..."
        echo "Completed ${i+1}/${list.size()}!"
        echo "==========================================================================="

Basically the way it works is you set the ID of each project to the directory path of files included in that project. So if you have test/Project1 and test/Project2 then each Project ID would be test:Project1 and test:Project2 respectively and in Jenkins I would run this with other_analysis(["test:Project1", "test:Project2"]). The function then checks the changed files in the branch/PR to see if there are changes within the scope of each Project, and if so it runs the scan.

I was hoping for a cleaner solution, but this is what I’ve come up with so far. Am I doing this wrong? :sweat_smile:

EDIT: I also did migrate from an older version of SonarQube, however the server was blank (no Projects or Portfolios). I set up everything from scratch for the mono-repo support.


I’m now officially out of my depth. I’ve referred this internally.


When running in Jenkins I see the following:

INFO: Auto-configuring with CI 'Jenkins'
INFO: Load active rules
INFO: Load active rules (done) | time=1495ms
INFO: Pull request 1234 for merge into master from feature_branch
INFO: SCM collecting changed files in the branch
INFO: SCM collecting changed files in the branch (done) | time=338ms

What exactly is happening here? Is there a setting where SonarQube can skip the scan if there are no changes in the Project relative to the base branch?