Maven publication closure is evaluated too early

Links:

Description

I have a task (randomArtifacts) that builds multiple RPM files and puts them under the buildDir. The rpm files have a timestamp and some other qualifiers (e.g. my-rpm-20161010-qa.rpm). So I want the maven publication closure (i.e. mavenRpm(MavenPublication) { ... }) to be executed after successful execution of randomArtifacts.

But as it stands, the mavenRpm closure is evaluated too early (i.e. during configuration phase):

$ gradle clean randomArtifacts publishToMavenLocal
mavenRpm                                   
:clean                      
clean
:randomArtifacts
randomArtifacts
Created 4 artifacts.
:generatePomFileForMavenRpmPublication                 
:publishMavenRpmPublicationToMavenLocal                 
:publishToMavenLocal

However I need the tasks to be executed in this order:

$ gradle clean randomArtifacts publishToMavenLocal
:clean                      
clean
:randomArtifacts
randomArtifacts
Created 4 artifacts.
mavenRpm                                   
:generatePomFileForMavenRpmPublication                 
:publishMavenRpmPublicationToMavenLocal                 
:publishToMavenLocal

Is there a way to execute mavenRpm lazily, in the execution phase?

Thanks in advance,
Behrang

From the documentation

The PublishingExtension is a DeferredConfigurable model element, meaning that extension will be configured as late as possible in the build

I’d try

tasks.withType(PublishToMavenRepository) { task ->
   task.dependsOn randomArtifacts
}

Hi Lance,

Unfortunately that means the configuration phase takes place as late as possible, not the execution phase of the plugin. I tried your suggestion and it leads to the same result:

build.gradle

group 'org.behrang.labs'
version '1.0-SNAPSHOT'

apply plugin: 'maven-publish'

repositories {
    mavenCentral()
}

task clean {
    doLast {
        println "clean"

        if (buildDir.exists()) {
            buildDir.listFiles().each { f -> f.delete() }
        }
    }
}

task randomArtifacts {
    doLast {
        println "randomArtifacts"

        buildDir.mkdirs();

        def artifactCount = (int) (Math.random() * 9) + 1
        (1..artifactCount).each {
            new File(buildDir, "artifact-${it}.rpm").text = "Artifact ${it}"
        }

        println "Created ${artifactCount} artifacts."
    }
}

publishing {
    println "publishing"

    publications {
        println "publications"

        mavenRpm(MavenPublication) {
            println "mavenRpm"

            def rpms = buildDir.listFiles({ f -> f.name.endsWith(".rpm")} as FileFilter)
            rpms.each { f ->
                println f.absolutePath
            }
        }
    }
}

tasks.withType(PublishToMavenRepository) { task ->
   task.dependsOn randomArtifacts
}

Output

$ gradle clean randomArtifacts publishToMavenLocal
publishing
publications
mavenRpm
:clean
clean
:randomArtifacts
randomArtifacts
Created 4 artifacts.
:generatePomFileForMavenRpmPublication
:publishMavenRpmPublicationToMavenLocal
:publishToMavenLocal

Other than println’s, what is it that you are actually trying to achieve? You should try and use doFirst and dependsOn to order your logic in the execution phase rather than tying yourself to the timing of the mavenRpm closure invocation. I’m guessing you could put your logic in PublishToMavenRepository.doFirst(...). Please also note that the mavenRpm closure is defined in the configuration phase but it’s likely executed in the execution phase.

A couple of style points:

  1. If you apply the base plugin (or another plugin that applies base) you’ll get the clean task for free
  2. It’s usually cleaner to separate files into subdirectories, I’d use a $buildDir/randomArtifacts directory if it were me. I’m not 100% sure what you’re trying to achieve but a directory might allow lazy evaluation of the actual file set

eg:

task randomArtifacts {
    def outputDir = "${buildDir}/randomArtifacts" 
    outputs.dir outputDir
    doLast {
        println "randomArtifacts"

        file(outputDir).mkdirs();

        def artifactCount = (int) (Math.random() * 9) + 1
        (1..artifactCount).each {
            file("${outputDir}/artifact-${it}.rpm").text = "Artifact ${it}"
        }

        println "Created ${artifactCount} artifacts."
    }
}
tasks.withType(PublishToMavenRepository) { task ->
   task.dependsOn randomArtifacts
   task.inputs.files randomArtifacts.outputs.files
   task.doFirst {
      randomArtifacts.outputs.files.each {
         println f.absolutePath
      } 
   }
}

I have vastly simplified this example.

Basically I have a task that builds, let’s say, a few .rpm files and stores them in $buildDir/rpms. Then I want the Maven Publish plugin to list all the files in $buildDir/rpms and publish them to Nexus.

However as publishing, publications, and mavenRpm closures are executed before the task that produces the RPM files has started, publishing doesn’t work as expected.

I think the outputs property might help me here. I will look at it tomorrow.

Thanks.

Were you able to get this scenario working?

I am having the same problem related to publishing RPMs with the ‘maven-publish’ plugin. If the normal publishing closure is used (like you would do with a jar artifact), during the configuration phase the RPM files do not yet exist so the publish fails. If you delay the configuration as Lance_Java suggested, the publish task reports ‘UP-TO-DATE’ and the files are not published.

Although I am also trying to publish RPMs, the issue is not related to an RPM. It could be any file created by the build. The full path name of the RPM built is dynamic so it cannot be known before it is built.

To summarize, this is the question:
How does one use the ‘maven-publish’ plugin to publish a dynamically named set of files produced during the build?

Thanks in advance for any replies or suggestions.

2 Likes

As these steps were happening inside CI/CD, I removed the publication logic from Gradle and added a script to publish the generated RPM files using mvn deploy:deploy-file. :roll_eyes: