Custom SonarQube plugin throws dependency error

I am trying to extend communitygosu plugin for sonarQube 10.x, but I am unable to compile the code successfully. I am getting the below error when trying to build using Gradle.

* What went wrong:
Execution failed for task ':compileTestJava'.
> Could not resolve all files for configuration ':testCompileClasspath'.
   > Could not find org.sonarsource.sonarqube:sonar-duplications:10.6.0.92116.
     Searched in the following locations:
       - https://repo.maven.apache.org/maven2/org/sonarsource/sonarqube/sonar-duplications/10.6.0.92116/sonar-duplications-10.6.0.92116.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project : > org.sonarsource.sonarqube:sonar-scanner-engine:10.6.0.92116

Here is my grade file.

import com.hierynomus.gradle.license.tasks.LicenseFormat

plugins {
    id('antlr')
    id('java')
    id('jacoco')
    id('maven-publish')
    id('org.sonarqube') version ('5.1.0.4882')
    id('org.jetbrains.changelog') version('2.0.0')
    // id('org.kordamp.gradle.markdown') version('2.2.0')
    id('com.github.johnrengelman.shadow') version('7.1.2')
    id('com.github.hierynomus.license') version('0.16.1')
    id('net.researchgate.release') version('2.6.0')
}

repositories {
    mavenCentral()
}

group 'de.friday'

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17

dependencies {
    antlr "org.antlr:antlr4:${antlrVersion}"

    compileOnlyApi "org.sonarsource.sonarqube:sonar-plugin-api:${sonarPluginApiVersion}"

    implementation "org.antlr:antlr4-runtime:${antlrVersion}"
    implementation "org.sonarsource.analyzer-commons:sonar-analyzer-commons:${sonarAnalyzerCommonsVersion}"
    implementation "org.sonarsource.java:java-surefire:${sonarJavaVersion}"

    implementation "org.apache.commons:commons-lang3:3.17.0"
    implementation "org.reflections:reflections:0.10.2"
    implementation "com.google.inject:guice:7.0.0"
    implementation "commons-io:commons-io:2.16.1"

    testImplementation "org.sonarsource.sonarqube:sonar-testing-harness:${sonarHarnessVersion}"
    testImplementation 'org.assertj:assertj-core:3.26.3'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0'
    testImplementation "org.sonarsource.sonarqube:sonar-scanner-engine:${sonarScannerEngineVersion}"
    testImplementation "org.sonarsource.sonarqube:sonar-ws:${sonarqubeWsVersion}"
    testImplementation 'com.google.protobuf:protobuf-java-util:4.28.0'
    testImplementation 'org.sonarsource.orchestrator:sonar-orchestrator-junit5:5.0.0.2065'
    testImplementation 'org.mockito:mockito-core:5.13.0'
    testRuntimeOnly "org.sonarsource.sonarqube:sonar-plugin-api:${sonarPluginApiVersion}"
    testRuntimeOnly 'ch.qos.logback:logback-classic:1.5.7'
}

def antlrGeneratedSourcesPath = ['de', 'friday', 'sonarqube', 'gosu', 'antlr']

generateGrammarSource {
    maxHeapSize = "128m"
    outputDirectory = file("${buildDir.path}/generated-src/antlr/main/${antlrGeneratedSourcesPath.join("/")}")
    arguments += ["-visitor", "-listener", "-package", antlrGeneratedSourcesPath.join(".")]

    finalizedBy("licenseFormatGeneratedSources")
}

tasks.register('licenseFormatGeneratedSources', LicenseFormat) {
    source = generateGrammarSource.outputDirectory
    dependsOn generateGrammarSource
    outputs.upToDateWhen { false }
}

compileJava {
    options.encoding = 'UTF-8'
    dependsOn generateGrammarSource
}

compileTestJava {
    options.encoding = 'UTF-8'
    dependsOn generateTestGrammarSource
}

tasks.licenseMain.shouldRunAfter generateGrammarSource

license {
    header = rootProject.file('LICENSE_HEADER')
    ext.year = Calendar.getInstance().get(Calendar.YEAR)
    strictCheck true
    mapping {
        java = 'SLASHSTAR_STYLE'
        g4 = 'DOUBLESLASH_STYLE'
    }
    excludes(['**/*.gs', '**/*.txt', '**/*.properties', '**/*.xml', '**/*.json', '**/*.xsd', '**/*.html', '**/*.vm', '**/*.snap', '**/*.svg', '**/*.jar', '**/*.zip', '**/*.log', '**/*.cnf', '**/*.jks', '**/README', '**/*.interp', '**/*.tokens', '**/*.gradle'])
}

// htmlToMarkdown {
//     sourceDir = file("${sourceSets.main.output.resourcesDir.path}/sonar")
//     outputDir = file("$projectDir/docs/rules")

//     doLast {
//         outputDir.listFiles().findAll {
//             it.name.endsWith('.json') // remove all JSON from generated HTML directory
//         }.forEach { it.delete() }
//         generateRulesIndexFile(outputDir.listFiles())
//     }
// }

void generateRulesIndexFile(File[] ruleFiles) {
    def rulesIndexText = new StringBuilder()
    rulesIndexText.append("""# Sonarqube Gosu Plugin Rules  
:warning: `This file is automatically generated by the htmlToMarkdown Gradle task. Do not change it directly.`

Follows a list of all the rules included on the plugin:

""".stripIndent())

    ruleFiles.toSorted().each {
        rulesIndexText.append("- [${it.name.replace('.md', '')}](rules/${it.name})\n")
    }

    new File("$projectDir/docs", 'RULES.md').text = rulesIndexText.toString()
}


test {
    useJUnitPlatform()
    testLogging {
        showExceptions = true
        exceptionFormat = 'full'
        showStandardStreams = true
        events 'passed', 'skipped', 'failed'
    }
    finalizedBy jacocoTestReport
}

jacocoTestReport {
    reports {
        xml.required = true
        csv.required = false
        html.required = true
        html.outputLocation.set(file("$buildDir/jacocoHtml"))
    }

    //Excludes ANTLR generated sources
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: [
                    "${antlrGeneratedSourcesPath.join("/")}/**",
            ])
        }))
    }
}

sourceSets {
    create("testIntegration") {
        java {
            srcDir("src/testIntegration/java")
            compileClasspath += configurations["testRuntimeClasspath"]
            runtimeClasspath += configurations["testRuntimeClasspath"]
        }
    }
}

abstract class IntegrationTest extends Test { }

tasks.withType(IntegrationTest).configureEach {
    testClassesDirs = sourceSets["testIntegration"].output.classesDirs
    classpath = sourceSets["testIntegration"].runtimeClasspath
    dependsOn(shadowJar)
}

tasks.register("testIntegration", IntegrationTest) {
    description = "Runs integration tests"
    group = "verification"
    systemProperties([
            "sonarServerVersion": providers.gradleProperty("sonarqubeServerVersion").getOrElse(sonarqubeVersion),
            "gosuPluginVersion": providers.gradleProperty("sonarGosuPluginVersion").getOrElse(version)
    ])
    useJUnitPlatform {
        includeTags("integration")
    }
    testLogging {
        showExceptions = true
        exceptionFormat = 'full'
        showStandardStreams = true
        events 'passed', 'skipped', 'failed'
    }
}

sonarqube {
    properties {
        property "sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/jacoco/test/jacocoTestReport.xml"
        //Excludes ANTLR generated files
        property "sonar.exclusions", "src/**/${antlrGeneratedSourcesPath.join("/")}/**/*"
    }
}

assemble {
    dependsOn(tasks.shadowJar)
}

shadowJar {
    archiveBaseName = project.name
    archiveClassifier = ''
    configurations = [project.configurations.compileClasspath]
    mergeServiceFiles()
    dependencies {
        exclude(dependency('org.sonarsource.sonarqube:.*'))
        exclude(dependency('org.slf4j:.*'))
    }
}

jar {
    archiveBaseName = project.name
    archiveClassifier = 'no-deps' //no dependencies included
    enabled = false
    manifest {
        def pluginVersion = project.version
        def displayVersion = project.hasProperty('buildNumber') ? pluginVersion.substring(0, pluginVersion.lastIndexOf('.')) + " (build ${project.buildNumber})" : pluginVersion
        def buildDate = new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
        attributes(
                'Build-Time': "${buildDate}",
                'Implementation-Build': 'git rev-parse HEAD'.execute().text.trim(),
                'Plugin-BuildDate': "${buildDate}",
                'Plugin-ChildFirstClassLoader': 'false',
                'Plugin-Class': 'de.friday.sonarqube.gosu.GosuPlugin',
                'Plugin-Description': 'Gosu Programming Language Plugin for SonarQube',
                'Plugin-Display-Version': "${displayVersion}",
                'Plugin-IssueTrackerUrl': 'https://github.com/FRI-DAY/sonar-gosu-plugin/issues',
                'Plugin-Key': 'communitygosu',
                'Plugin-License': 'GNU AGPL 3',
                'Plugin-Name': 'Community Gosu Plugin',
                'Plugin-Homepage': 'https://github.com/FRI-DAY/sonar-gosu-plugin',
                'Plugin-Organization': 'FRIDAY Insurance S.A.',
                'Plugin-OrganizationUrl': 'https://www.friday.de',
                'Plugin-SourcesUrl': "${pluginRepositoryUrl}",
                'Plugin-Version': "${pluginVersion}",
                'Sonar-Version': "${sonarqubeVersion}",
                'SonarLint-Supported': 'true',
                'Version': "${pluginVersion}",
        )
    }
}

changelog {
    version = project.version
    header = "${project.version}"
    groups = []
    repositoryUrl = pluginRepositoryUrl
}

release {
    tagTemplate = 'v${version}'
    git {
        requireBranch = 'main'
        commitVersionFileOnly = true
    }
}

Hey there.

You should probably raise an issue on their Github repository: GitHub - FRI-DAY/sonar-gosu-plugin: Gosu Programming Language Plugin for SonarQube

Isn’t the issue with the sonar repo? I have upgraded the references so the plugin gets the latest dependencies. The plugin uses testImplementation org.sonarsource.sonarqube:sonar-scanner-engine:10.6.0.92116 (upgraded to newer version) and it has a dependency for sonar-duplications-10.6.0.92116. And sonar-duplications-10.6.0.92116 is not found in maven central. @Colin @josimar-silva

Thanks for the additional context. It does seem curious. Can you tell me exactly what you updated the values in gradle.properties to (the versions?)

version=1.2.4
description=Gosu Programming Language Plugin for SonarQube.
projectTitle=SonarQube Gosu Plugin

Here are the values.

antlrVersion=4.9.3
jdkMinVersion=1.8
sonarAnalyzerCommonsVersion=2.13.0.3004
sonarqubeVersion=10.6.0.92116
sonarqubeWsVersion=10.6.0.92116
sonarHarnessVersion=10.6.0.92116
sonarPluginApiVersion=9.4.0.54424
sonarScannerEngineVersion=10.6.0.92116
sonarJavaVersion=8.3.0.36747

pluginRepositoryUrl=GitHub - FRI-DAY/sonar-gosu-plugin: Gosu Programming Language Plugin for SonarQube

Hi @Colin do you have any updates on this issue? Any help would be greatly appreciated. Thank you!

Hey @koolhuman

I do mean to come back to you with some updates – but I think the crux of the issue is that the plugin requires significant updates to move from a version of the Plugin API that was compatible with SonarQube v6.7.7 (released in 2017), to a supported version of SonarQube. I want to try and find any helpful documentation on API changes I can, but I haven’t gotten around to it. I’ll try to come back by the end of the week.

1 Like