diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 0f785396bb..93dd885ac9 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -61,6 +61,30 @@ pipeline { " allParallelSlowIntegrationTest --stacktrace" } } + + stage('Generate sonarqube report') { + steps { + // running this step here is the only way to not majorly affect the distributed test plugin, + // as now that neither returns build files nor runs jacoco reports + sh "./gradlew --no-daemon build jacocoRootReport --stacktrace" + withSonarQubeEnv('sq01') { + sh "./gradlew --no-daemon sonarqube -x test --stacktrace" + } + timeout(time: 3, unit: 'MINUTES') { + script { + try { + def qg = waitForQualityGate(); + if (qg.status != 'OK') { + error "Pipeline aborted due to quality gate failure: ${qg.status}" + } + } catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) { + println('No sonarqube webhook response within timeout. Please check the webhook configuration in sonarqube.') + // continue the pipeline + } + } + } + } + } } } } diff --git a/build.gradle b/build.gradle index e6029d06b6..833766c97f 100644 --- a/build.gradle +++ b/build.gradle @@ -189,6 +189,7 @@ buildscript { classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: "1.2-LOCAL-K8S-SHARED-CACHE-SNAPSHOT", changing: true classpath group: "com.r3.dependx", name: "gradle-dependx", version: "0.1.13", changing: true classpath "com.bmuschko:gradle-docker-plugin:5.0.0" + classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" } } @@ -241,6 +242,7 @@ allprojects { apply plugin: 'jacoco' apply plugin: 'org.owasp.dependencycheck' apply plugin: 'kotlin-allopen' + apply plugin: 'org.sonarqube' allOpen { annotations( @@ -400,6 +402,16 @@ allprojects { } } } +sonarqube { + properties { + property "sonar.projectName", "Corda" + property "sonar.projectKey", "corda" + property 'sonar.tests', '**/src/test/**,**/src/smoke-test/**,**/src/integration-test/**,**/src/integration-test-slow/**' + property 'sonar.coverage.jacoco.xmlReportPaths', "${rootDir.path}/build/reports/jacoco/jacocoRootReport/jacocoRootReport.xml" + property 'detekt.sonar.kotlin.baseline.path', "${rootDir.path}/detekt-baseline.xml" + property 'detekt.sonar.kotlin.config.path', "${rootDir.path}/detekt-config.yml" + } +} // Check that we are running on a Java 8 JDK. The source/targetCompatibility values above aren't sufficient to // guarantee this because those are properties checked by the Java plugin, but we're using Kotlin. @@ -459,6 +471,28 @@ task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { it.exists() }) } + afterEvaluate { + classDirectories = files(classDirectories.files.collect { + fileTree(dir: it, + // these exclusions are necessary because jacoco gets confused by same class names + // which occur due to deterministic versions of non deterministic classes + exclude: ['**/net/corda/core/crypto/SHA256DigestSupplier**', + '**/net/corda/core/crypto/DelegatingSecureRandomService', + '**/net/corda/core/internal/ThreadLocalToggleField**', + '**/net/corda/core/internal/InheritableThreadLocalToggleField**', + '**/net/corda/core/internal/ToggleField**', + 'net/corda/core/internal/rules/StateContractValidationEnforcementRule**', + 'net/corda/core/internal/SimpleToggleField**', + 'net/corda/core/serialization/SerializationFactory**', + 'net/corda/serialization/internal/amqp/AMQPStreams**', + 'net/corda/serialization/internal/amqp/AMQPSerializerFactories**', + 'net/corda/serialization/internal/amqp/AMQPSerializationThreadContext**', + 'net/corda/serialization/internal/ByteBufferStreams**', + 'net/corda/serialization/internal/model/DefaultCacheProvider**', + 'net/corda/serialization/internal/DefaultWhitelist**' + ]) + }) + } } tasks.register('detekt', JavaExec) {