'Sonar with multi-module gradle project

I have java and kotlin based multi module gradle project. I am trying to setup sonar analysis for the same. I configured sonar at root project and ran analysis with CircleCI. The result in sonarcloud gets for only one of the sub-project.

My project structure is as below:

  • projectA/build.gradle
  • ProjectB/build.gradle
  • ProjectC/build.gradle
  • build.gradle

Here is my root build.gradle.

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61'
        classpath 'io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE'
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.2.4.RELEASE'
        classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7"
    }
}

ext {
    springCloudVersion = 'Hoxton.SR1'
    springBootVersion = '2.2.4.RELEASE'
}

configurations.all {
    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}

allprojects {
    group = 'com.organiz'
    version = '1.0.0-SNAPSHOT'

    repositories {
        mavenCentral()
        maven {
            //        url ="s3url"  //   only for releases
            url ="s3url"  //  only for snapshots
            credentials(AwsCredentials) {
                accessKey project.accessKey
                secretKey project.secretKey
            }
        }
    }
}

subprojects {

    apply plugin: 'java'
    apply plugin: 'kotlin'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'idea'
    apply plugin: 'org.sonarqube'
    sourceCompatibility = '1.8'

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter'
        testImplementation('org.springframework.boot:spring-boot-starter-test') {
            exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
        }
        implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
        implementation('someappcommon:1.0.0-SNAPSHOT') { changing = true }
        implementation("com.h2database:h2:1.4.200")
    }

    repositories {
        mavenCentral()
        jcenter()
        maven {
            url "someurlhere"
        }
        someappMavenRepoUrl.split(',').each { repoUrl -> maven { url repoUrl } }
    }

    test {
        useJUnitPlatform()
    }

    compileKotlin {
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    compileTestKotlin {
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    sonarqube {
         properties {
    property "sonar.projectKey", "projectKey"
    property "sonar.organization", "org"
    property "sonar.host.url", "https://sonarcloud.io"
    property "sonar.verbose", "true"
  }
}
}

project(':project1') {
    dependencies {
        implementation project(":common")
    }
}

project(':project2') {
    dependencies {
        implementation project(":common")
    }
}


Solution 1:[1]

Need to include sonarqube outside the subproject block and then all the sub modules will be analysed and report will be exported.

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61'
        classpath 'io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE'
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.2.4.RELEASE'
        classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7"
    }
}

ext {
    springCloudVersion = 'Hoxton.SR1'
    springBootVersion = '2.2.4.RELEASE'
}

configurations.all {
    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}

allprojects {
    group = 'com.organiz'
    version = '1.0.0-SNAPSHOT'

    repositories {
        mavenCentral()
        maven {
            //        url ="s3url"  //   only for releases
            url = "s3url"  //  only for snapshots
            credentials(AwsCredentials) {
                accessKey project.accessKey
                secretKey project.secretKey
            }
        }
    }
}

apply plugin: 'org.sonarqube'
sonarqube {
    properties {
        property "sonar.projectKey", "projectKey"
        property "sonar.organization", "org"
        property "sonar.host.url", "https://sonarcloud.io"
        property "sonar.verbose", "true"
    }
}

subprojects {

    apply plugin: 'java'
    apply plugin: 'kotlin'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'idea'
    sourceCompatibility = '1.8'

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter'
        testImplementation('org.springframework.boot:spring-boot-starter-test') {
            exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
        }
        implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
        implementation('someappcommon:1.0.0-SNAPSHOT') { changing = true }
        implementation("com.h2database:h2:1.4.200")
    }

    repositories {
        mavenCentral()
        jcenter()
        maven {
            url "someurlhere"
        }
        someappMavenRepoUrl.split(',').each { repoUrl -> maven { url repoUrl } }
    }

    test {
        useJUnitPlatform()
    }

    compileKotlin {
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    compileTestKotlin {
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
}

project(':project1') {
    dependencies {
        implementation project(":common")
    }
}

project(':project2') {
    dependencies {
        implementation project(":common")
    }
}

Solution 2:[2]

My solution for kotlin multi-module project with junit 5 & spring boot, using gradle-jacoco-log plugin:

1.?? In your root build.gradle.kts:

plugins {
    kotlin("jvm") version "1.5.0"
    id("org.sonarqube") version "3.2.0"
    id ("org.barfuin.gradle.jacocolog") version "1.2.4"
}


sonarqube {
    val sonarOrganization: String by project
    val sonarProjectKey: String by project
    val sonarLogin: String by project

    properties {
        properties(
            hashMapOf<String, String>(
                "sonar.login" to sonarLogin,
                "sonar.projectKey" to sonarProjectKey,
                "sonar.organization" to sonarOrganization,
                "sonar.host.url" to "https://sonarcloud.io",
                "sonar.coverage.jacoco.xmlReportPaths" to "${buildDir}/reports/jacoco/jacocoAggregatedReport/jacocoAggregatedReport.xml"
            )
        )
    }
}

2.?? In backend suprojects which have tests:

plugins{
    jacoco
}

jacoco { toolVersion = "0.8.7" }

tasks{
    test { useJUnitPlatform()
    jacocoTestReport { reports { xml.required.set( true ) } }
    finalizedBy(jacocoTestReport) 
    }
}


Optional step (example how to use)

3.?? In your CI file (.github/workflows/master.yml in my case):

name: Master CI
on:
  push:
    branches:
      - master


jobs:
  DEPLOY:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: ? Set up JDK 16
        uses: actions/setup-java@v1
        with:
          java-version: 16

      - name: ? Test
        run: |
          chmod +x ./gradlew
          ./gradlew test

      # Create `build/jacoco/jacocoMergeSubprojects.exec` 
      - name: ? 1) Jacoco merge subprojects reports
        run: ./gradlew jacocoMergeSubprojects -x test

      # Create `build/reports/jacoco/jacocoAggregatedReport/jacocoAggregatedReport.xml`
      - name: ? 2) Jacoco Jacoco aggregated report
        run: ./gradlew jacocoAggregatedReport -x test

      - name: ? 3) Sonarqube report
        run: ./gradlew sonarqube -x test
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 saw303
Solution 2 Dmitry Kaltovich