Publication fails with generateMetaData error

Hey guys,

we just switched our project from Gradle 5 to Gradle 7.1.1. With an intermediate step to Gradle 6.
Everything went fine so far, all tests run, and ./gradlew build --warning-mode-all does not contain any warnings or errors. So far so good, but we can not publish anymore.

The publish task fails:

Execution failed for task ':generateMetadataFileForMavenJavaPublication'.
> Cannot publish module metadata where component artifacts are modified.

Our GradleFile looks like that: (Please keep in mind the xxx are just for privacy reasons.)

plugins {
    id "org.sonarqube" version "3.0"
    id "application"
    id "java"
    id "maven-publish"
    id "jacoco"
    id 'net.researchgate.release' version '2.8.1'
    id 'com.palantir.docker' version '0.26.0'
    id "org.owasp.dependencycheck" version "6.0.2"
}

def repoBase = 'http://xxxx-xxxxx.xxxxx-xxxxx.com/content/repositories'
jacoco {
    toolVersion = "0.8.7"
}

assert JavaVersion.current().isJava11(): "Java 11 is required"
sourceCompatibility = JavaVersion.VERSION_11

tasks.withType(JavaCompile).all{
    it.options.compilerArgs+=['--release','11']
}

repositories {
    maven {
        url "${repoBase}/public"
        allowInsecureProtocol = true
    }
}

compileJava.options.encoding = "UTF-8"
compileTestJava.options.encoding = "UTF-8"

/**=====================================================================================================================
 *
 * Configuration for acceptance tests
 *
 =====================================================================================================================*/
configurations {
    remoteTestImplementation { extendsFrom testImplementation }
    remoteTestRuntime { extendsFrom testRuntimeClasspath }
}

sourceSets {
    remoteTest {
        java {
            srcDir 'src/remoteTest/java'
        }
        resources {
            srcDir 'src/remoteTest/resources'
        }
        compileClasspath += sourceSets.main.runtimeClasspath
    }
}

test { testLogging.showStandardStreams = true }

task remoteTest(type: Test) {
    // disable default junit reporting
    reports.junitXml.required = false
    reports.html.required = false

    testClassesDirs = sourceSets.remoteTest.output.getClassesDirs()
    classpath += sourceSets.remoteTest.runtimeClasspath + configurations.remoteTestRuntime + sourceSets.remoteTest.resources

    systemProperty 'xxxx', System.getProperty('xxxx')
    systemProperty 'xxxx', System.getProperty('xxxx')

    systemProperty 'xxxxxx', System.getProperty('xxxxxx')
    systemProperty 'xxxxxx', System.getProperty('xxxxx')

    systemProperty 'xxxxxxx', System.getProperty('xxxxx')
    systemProperty 'xxxxx', System.getProperty('xxxxx')

    outputs.upToDateWhen { false }
}
/**===================================================================================================================*/


/**=====================================================================================================================
 *
 * Dependencies
 *
 =====================================================================================================================*/
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute module('log4j:log4j') with module("org.apache.logging.log4j:log4j-1.2-api:${log4j2Version}")
    }
}

dependencies {
    implementation 'io.netty:netty-all:4.1.16.Final'
    implementation 'org.slf4j:slf4j-api:1.7.25'
    implementation 'javax.xml.bind:jaxb-api:2.3.1'

    // configuration
    implementation "org.apache.commons:commons-configuration2:2.1.1"
    runtimeOnly "commons-beanutils:commons-beanutils:1.9.3"

    // monitoring
    implementation 'xxxx.xxxx.xxxx.xxxxx:xxxx-xxxxxx-xxxxxx-xxxx:4.0.1'
    implementation 'io.dropwizard.metrics:metrics-json:4.1.2'
    implementation 'io.dropwizard.metrics:metrics-servlets:4.1.2'

    // NPM connection
    implementation "xxxx.xxxxx.xxxxx.xxxxx:xxxx-xxxxx-xxxxx:${xxxxx}"

    implementation 'org.apache.commons:commons-lang3:3.3.2'
    implementation 'com.google.code.gson:gson:2.8.7'
    implementation 'org.ehcache:ehcache:3.4.0'
    implementation group: 'com.google.guava', name: 'guava', version: '30.1.1-jre'

    // log4j2 als slf4j backend
    runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:${log4j2Version}"
    runtimeOnly "org.apache.logging.log4j:log4j-api:${log4j2Version}"
    runtimeOnly "org.apache.logging.log4j:log4j-core:${log4j2Version}"
    runtimeOnly "org.apache.logging.log4j:log4j-1.2-api:${log4j2Version}"

    testImplementation 'com.github.dreamhead:moco-core:1.0.0'

    testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}"
    testImplementation("org.junit.vintage:junit-vintage-engine:${junitVersion}") {
        exclude group: 'org.hamcrest', module: 'hamcrest-core'
    }

    testImplementation group: 'org.hamcrest', name: 'hamcrest', version: '2.2'
    testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version: '2.2'
    testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.28.2'

    testImplementation 'commons-io:commons-io:2.8.0'
    testImplementation group: "org.apache.httpcomponents", name: "httpclient", version: "4.5.5", classifier: "tests"
    testImplementation 'nl.jqno.equalsverifier:equalsverifier:2.5.2'
    testImplementation 'com.jayway.jsonpath:json-path:2.4.0'
    testImplementation 'com.github.tomakehurst:wiremock-jre8:2.29.0'
    testImplementation 'org.assertj:assertj-core:3.20.2'
    testImplementation group: 'org.awaitility', name: 'awaitility', version: '4.1.0'

    remoteTestImplementation 'com.jayway.jsonpath:json-path:2.6.0'
    remoteTestImplementation 'org.apache.commons:commons-lang3:3.3.2'
    remoteTestImplementation 'io.cucumber:cucumber-java:6.11.0'
    remoteTestImplementation 'io.cucumber:cucumber-junit:6.11.0'
    remoteTestImplementation 'io.cucumber:cucumber-picocontainer:6.11.0'
    remoteTestImplementation "xxxxx.xxxxx.xxxxxx.xxxxx.xxxxx-xxxx-xxxxx:${Version}"
    remoteTestImplementation("xxxxx.xxxxx.xxxxx.xxxxxx.xxxxx:xxxxx-xxxx-xxx:${Version}") {
        exclude group: 'org.projectlombok', module: 'lombok'
    }
}

rootProject.tasks.named("processRemoteTestResources") {
    duplicatesStrategy = 'include'
}

tasks.withType(Test).configureEach {
    useJUnitPlatform()
}
/**===================================================================================================================*/


/**=====================================================================================================================
 *
 * Configuration for distribution
 *
 =====================================================================================================================*/
mainClassName = "xxxxx.xxxxxx.xxxxxx.xxxxxx.xxxxxxx.xxxxxxx"
applicationDefaultJvmArgs = [
        "-Dconfig=../conf/xxxxxxx.xml",
        "-Dlog4j.configurationFile=../conf/log4j2.xml"]

jar {
    doFirst {
        manifest {
            attributes(
                    'Implementation-Title': "xxxxxx xxxxxx",
                    'Implementation-Version': archiveVersion,
                    'Implementation-Timestamp': new Date(),
                    'Build-JDK': System.getProperty('java.version')
            )
        }
    }
}


distributions {
    distTar.enabled = false
    main {
        distributionBaseName = 'xxxxxx.xxxxxx.xxxxxx.xxxxxx'
        contents {
            from {'src/main/resources/xxxxx/xxxxxx/xxxxxx/xxxxxx/xxxxxxx/'} {
                include 'xxxxxxx.xml'
                rename { name -> name.replace('.xml', '-Template.xml') }
                into ("conf/")
            }
        }
    }
}
/**===================================================================================================================*/


/**=====================================================================================================================
 *
 * Configuration for publish task
 *
 =====================================================================================================================*/
artifacts {
    archives distZip
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            artifact distZip { classifier 'all' }
            from components.java
        }
    }

    repositories {
        maven {
            url = "${repoBase}/snapshots"
            credentials {
                username 'xxxxxx'
                password 'xxxxxx'
            }
        }
    }
}
/**===================================================================================================================*/


/**=====================================================================================================================
 *
 * Configuration for quality analysis
 *
 =====================================================================================================================*/
sonarqube {
    properties {
        property "sonar.host.url", "http://xxxx-sonar"
        property "sonar.projectName", "xxx.xxxx.xxxxx.xxxxx"
    }
}
project.tasks.sonarqube.dependsOn jacocoTestReport

jacocoTestReport {
    reports {
        xml.required = true
    }
}
/**===================================================================================================================*/


/**=====================================================================================================================
 *
 * Configuration for releasing
 *
 =====================================================================================================================*/
release {
    tagTemplate = '$name-$version'
    failOnUnversionedFiles = true
    failOnCommitNeeded = true
}

tasks.create(name: "useMavenReleaseRepository") {
    doLast {
        publishing.repositories.maven.url = "${repoBase}/releases/"
    }
}

task refreshVersion() {
    publishing.publications.mavenJava.version = project.version
    publishing.publications.mavenJava.setArtifacts([distZip])
}
/**===================================================================================================================*/

/**
 * Process dependencies
 */
afterReleaseBuild.dependsOn publish // publishes artefacts to nexus
beforeReleaseBuild.dependsOn(useMavenReleaseRepository)

/**=====================================================================================================================
 *
 * Configuration for docker container
 *
 =====================================================================================================================*/

docker {
    name "xxxxx:${project.version}"
    tag 'xxxx', 'xxxxx/xxxxx/xxxx/xxxxx:xxx'
    files tasks.distZip.outputs
    buildArgs(['PROJECT_VERSION': project.version])
}
project.tasks.dockerfileZip.enabled = false

/**===================================================================================================================
 *
 * Configuration for dependency check
 *
 * =====================================================================================================================*/
dependencyCheck {
    failOnError = false
}

I am fairly new to gradle but I will provide you any information you need.

Thanks!

1 Like

I have the same (or a similar) problem. Did you solve yours?

We fixed our problem now: During migration, we added a feature variant for our tests (with “registerFeature”). That led to an additional jar for the tests, and somehow this caused the problem. In our case, we could get rid of the test jar for the specific project to circumvent the problem.

In OPs case it is that strange looking refreshVersion task that most probably does not what he wants or expects.
In its configuration phase (no matter whether it is run or not) it changes the version and artifacts of the mavenJava publication and that is what then conflicts with Gradle Module Metadata generation.

Thanks for the reply. As an addition: A similar problem turned up in another subproject and disabling the test jar wasn’t an option there. But disabling the generation of gradle module metadata (Understanding Gradle Module Metadata) fixed the problem.

Tbh I wouldn’t call that a “fix” but a most a nasty work-around.
Imho disabling the module metadata should only be a very last resort “solution”.
It is usually better to fix the actual problem why it does not work as the module metadata bears more semantic information that a POM simply cannot hold and that downstream Gradle consumers can benefit from.