Gradle's publishToMavenLocal task is not executed correctly - gradle

I'm trying to create a gradle based multi-module project. There is also an project that contains different gradle scripts that enables pluggable build configurations. One of those scripts is for publishing artifacts to maven repository. This is the content of that script:
apply plugin: 'maven-publish'
configure(subprojects.findAll()) {
if (it.name.endsWith('web')) {
publishing {
publications {
mavenWeb(MavenPublication) {
from components.web
}
}
}
} else {
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}
}
}
build.dependsOn publishToMavenLocal
This script is included in the build gradle file of other project.
apply from: '../../food-orders-online-main/artifact-publish.gradle'
When I run build task it always shows that publishToMavenLocal task is up to date and I cannot find the artifacts in the local repository. Am I doing something wrong?

By adapting answer from here, it works for me.
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
repositories {
mavenLocal()
}
}

I think this could be a manifestation of a bug with gradle, that modules can lose the publishMavenJavaPublicationToMavenLocal task when they are depended upon in a certain way.
If gradle determines that there is no publishMavenJavaPublicationToMavenLocal task for a module then the publishToMavenLocal task will always report that it is up to date.
The specific case I have found occurs in a multimodule setup, with multiple levels of nested modules. It can be summarised as follows, where shared:domain loses its publishMavenJavaPublicationToMavenLocal when depended upon by A
root
root gradle build file
->A
own gradle build file with dependency on shared:domain
-> shared
gradle build file for shared modules
-> shared:domain
-> shared:B
I have created a small example project demonstrating this behaviour available here - https://github.com/piersy/Gradle2MavenPublishBug
I have also logged a bug with gradle here - http://forums.gradle.org/gradle/topics/the-publishmavenjavapublicationtomavenlocal-task-disappears-from-a-project-when-that-project-is-depended-upon-in-a-specific-way
For now the workarounds I have found are to
Remove A's dependency on shared:domain
Make A a submodule with its configuration specified in its parent module build file
Give shared:domain its own gradle build file

Related

Excluding one or two sub-projects from publication in gradle

I have a multi-project gradle build and I have applied the maven-publish plugin to all subprojects in my master build file.
I have also defined a default publication there called mavenJava.
This is all fine and now when I run ./gradlew artifactoryPublish I deploy artifacts from all the subprojects.
However, I would like to exclude two of my subprojects from publishing this mavenJava publication.
I've tried a bunch of things such as defining a boolean (skipDefaultPublish) in the ext block in the subprojects block in my master build file and including the publication definition inside a conditional if block and overwriting this variable in the ext block of the subprojects I do not want to publish it. That doesn't work and gradle complains that it Cannot configure the 'publishing' extension after it has been accessed..
I've tried other things but nothing seems to work.
The only thing that I know will work is defining the default publication block in all subprojects except for the ones that I do not want to publish from, but I have ~20 subprojects but only two that should not publish this type of artifact so this doesn't seem like the best thing to do.
So is there any way I can configure all subprojects to publish an artifact but override that in only two of the subprojects so that they don't do that?
UPDATE
To try to clarify what my project looks like.
root build.gradle
subprojects {
apply plugin: 'maven-publish'
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifactId = getCustomProjectId()
groupId = 'com.our.group.id'
}
}
}
apply plugin: "com.jfrog.artifactory"
artifactory {
contextUrl = ourContextUrl
publish {
repository {
repoKey = "ourRepoKey"
username = "ourArtifactoryUser"
password = "ourArtifactoryPass"
}
defaults {
publications('mavenJava')
publishArtifacts = true
publishPom = true
}
}
}
}
and in a subproject which should publish an artifact, just not the default one:
project(':exsub'){
publishing {
publications {
mavenSrc(MavenPublication) {
groupId "com.our.group.id"
artifactId "stuff-src"
artifact srcStuff
}
}
}
artifactoryPublish {
// publications.removeAll() //<- putting this here doesn't work
// publications.remove('mavenJava') //<- neither does this
publications ('mavenSrc') //<- this adds mavenSrc to the publication list but does not remove the mavenJava publication from it
}
}
There are several ways to publish artifacts, depending on used plugins.
To exclude particular module from publishing with modern (Gradle 4.x-7.x) publish like tasks:
tasks.withType(PublishToMavenRepository).configureEach { it.enabled = false }
Legacy uploadArchives:
uploadArchives.enabled = false
With com.jfrog.artifactory plugin:
artifactoryPublish.skip = true
Pheww... I think I finally figured out a way to do this (the aesthetics of the solution are quite debatable).
For me, it was sufficient to put a publishing.publications.remove(publishing.publications.mavenJava) line in the subproject.
However, I had to take care to put that line below the publishing{...} block in that subproject.
So now, the subprojects that should not publish the "default publication" look something like this:
project(':exsub'){
publishing {
publications {
mavenSrc(MavenPublication) {
groupId "com.our.group.id"
artifactId "stuff-src"
artifact srcStuff
}
}
}
// Remove the default publication (note: this must be located after the publishing block above)
publishing.publications.remove(publishing.publications.mavenJava)
artifactoryPublish {
publications ('mavenSrc') //<- this adds mavenSrc to the publication list but does not remove the mavenJava publication from it
}
}
Setting the following on subprojects you want to exclude worked for me:
project.tasks.publish.enabled = false
This does still require the maven-pulish plugin to be applied to the project, but no additional configuration is needed.
I found this setting here: https://discuss.gradle.org/t/disable-maven-publish-related-tasks-not-possible/13488
Let's consider the following multi-module project structure.
photos:
-> photo-client
-> photo-model
-> photo-service
In the above multimodule project, If I want to exclude the functionality of the photo-service submodule build jar (any distributive file(jar/war)) from uploading to the configured artifactory, then we have to use the below code snippet in the photo-service's build.gradle file
build.gradle
dependencies {
.....
}
project.afterEvaluate {
project.tasks.artifactoryPublish.enabled(false)
}
This question is specifically about what to do when some general configuration introduces one publication for all subprojects, but in one specific project you want to remove that "default" publication, but still have another publication. Most of the answers don't address this.
The answer by the original questioner does, but it didn't work for me.
This did:
tasks.withType(PublishToMavenRepository) {
onlyIf {
publication != publishing.publications.mavenJava
}
}
(where mavenJava is the name of the "default" publication to prevent).
This kind of solution is suggested by the Gradle documentation here: https://docs.gradle.org/current/userguide/publishing_customization.html#sec:publishing_maven:conditional_publishing

How to use gradle Artifactory plugin in gradle subproject

I have a multi-project gradle build wherein one of the subprojects is applying the Artifactory plugin (version 4.2.0), and configuring the contextUrl and resolve repoKey.
It sets up a simple configuration and dependency entry, and then has a copy task to retrieve the dependency as a zip file and extract it into a directory.
However, when the copy task runs, I get the error below. What am I doing wrong? Is this a problem with the Artifactory plugin, or gradle, or...?
The problem does not appear to be related to whether or not this is a subproject. I get the same error if I remove the multiproject configuration and run the task from the subproject directory.
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\hoobajoob\project\subproject\package.gradle' line: 36
* What went wrong:
A problem occurred evaluating project ':subproject'.
> Could not resolve all dependencies for configuration ':subproject:runtimeDep'.
> Cannot resolve external dependency company.com:artifact-id:1.0.0 because no repositories are defined.
Here are the contents of subproject/package.gradle (Artifactory url/user/password properties are in a gradle.properties file for the subproject):
plugins {
id "com.jfrog.artifactory" version "4.2.0"
}
artifactory {
contextUrl = "${artifactory_contextUrl}"
resolve {
repository {
username = "${artifactory_user}"
password = "${artifactory_password}"
repoKey = 'some-repo'
}
}
}
configurations {
runtimeDep
}
dependencies {
runtimeDep 'company.com:artifact-id:1.0.0#zip'
}
ext.destination = null
task getDependencies(type: Copy) {
from zipTree { configurations.runtimeDep.singleFile }
into ".artifacts/runtime"
}
The root project build script is empty except for the wrapper task. Below is the settings.gradle file:
include 'subproject'
rootProject.children.each { project -> project.buildFileName = "package.gradle" }
While the task setup in my question is different, this appears to be the same symptom as described in this other SO question.
The problem seems to be related to the fact that the Artifactory plugin will not perform dependency resolution until gradle's execution phase. I had assumed that defining the argument to the zipTree step in the getDependencies task with a closure would have the effect of deferring the dependency resolution until that phase.
But, for this to be deferred by the copy task, I need to define the from configuration of the getDependencies task as a closure, and include the zipTree operation in that closure.
It's the difference between:
from zipTree { configurations.runtimeDep.singleFile } // doesn't work
...and
from { zipTree( configurations.runtimeDep.singleFile ) } // works
Making this change gets the resolve working (with no required maven repositories block).
Another solution is to drop the Artifactory configuration altogether (which I can do in this case because I do not need to utilize anything unique to Artifactory) and use the traditional gradle repositories block, as described in the other SO question and by crazyjavahacking. Doing this makes the build script shorter, and I can leave the zipTree step configured as it was originally written:
repositories {
maven {
url "${artifactory_contextUrl}/repo-key"
}
}
configurations {
runtimeDep
}
dependencies {
runtimeDep 'company.com:artifact-id:1.0.0#zip'
}
ext.destination = null
task getDependencies(type: Copy) {
from zipTree { configurations.runtimeDep.singleFile }
into ".artifacts/runtime"
}
As the Gradle print to the console:
You did not define repositories{} block and so it does not know how to download declared dependency.

How to let gradle build dependent sub-project first when using non-compile dependencies

How can I tell gradle to build a certain sub-projects first, even though I don't have a compile dependency to them? How are project dependencies handled internally?
Example:
settings.gradle:
include "app", "schema"
build.gradle:
allprojects {
apply plugin: 'java'
}
schema/build.gradle:
// empty
app/build.gradle:
configurations {
schemas
}
dependencies {
schemas project(":schema")
schemas "org.example:example-schema:1.0"
}
task extractSchema(type: Copy) {
from {
configurations.schemas.collect { zipTree(it) }
}
into "build/schemas"
}
//compileJava.dependsOn extractSchema
And when running:
$ cd app
$ gradle extractSchema
I get:
Cannot expand ZIP 'schema/build/libs/schema.jar' as it does not exist.
What I want is that gradle automatically builds all sub-projectes defined in the configurations.schemas dependency list first (if they are projects).
Note: I want to be able to share the extractSchema task across multiple gradle projects, so it is important that gradle takes the list of sub-project to be built first from the configurations.schemas list.
Thanks
Gradle build order is never on the project level, but always on the task level. from(configuration.schemas) would infer task dependencies automatically, but in case of from(configuration.schemas.collect { ... }), this doesn't work because the resulting value is no longer Buildable. Adding dependsOn configurations.schemas should solve the problem.

Gradle Maven plugin generates incorrect POM dependencies for custom configuration

I have a Gradle project which does a couple of orthogonal things:
Compile and run some Java.
Generate and publish an artifact.
This artifact is nothing to do with the Java; it's generated by a custom JavaExec task. However, the auto-generated POM (from the Maven plugin) seems to include the wrong dependencies. Question: How can I prevent this?
My build.gradle looks something like this:
apply plugin: "java"
apply plugin: "maven"
configurations {
foo // Custom configuration for the artifact I want to build and publish
}
// Dependencies for Java configurations (nothing to do with foo)
dependencies {
compile "foo:foo:1.1"
testCompile "bar:bar:2.2"
}
// Custom task
task generateFoo(type: JavaExec) {
ext.outputFile = new File(buildDir, "foo.bar")
...
}
artifacts {
foo file: generateFoo.outputFile, builtBy: generateFoo
}
uploadFoo {
repositories {
mavenDeployer { ... }
}
}
I invoke Gradle like this:
./gradlew uploadFoo
AFAICS, the foo configuration is unrelated to the Java configurations. So I expect the published POM to list no dependencies. However, I observe all the unrelated dependencies listed.
The Gradle docs for the Maven plugin hint at dependency mapping with conf2ScopeMappings, but I'm entirely unclear what (if anything) I should be doing with this.
Note: I'm using Gradle wrapper 1.6; I'll be trying the latest to see if that makes a difference...
I managed to setup similar configuration with both maven and maven-publish gradle plugins.
In my case I was using custom Jar task with custom configuration, but it should work because the artifacts { ... } are used in both cases.
With maven plugin, your build.gradle would look like:
apply plugin: "java"
apply plugin: "maven"
configurations {
foo // Custom configuration for the artifact I want to build and publish
}
// Dependencies for Java configurations (nothing to do with foo)
dependencies {
compile "foo:foo:1.1"
testCompile "bar:bar:2.2"
}
// Custom task
task generateFoo(type: JavaExec) {
ext.outputFile = new File(buildDir, "foo.bar")
...
}
artifacts {
foo file: generateFoo.outputFile, builtBy: generateFoo
}
uploadFoo {
repositories {
mavenDeployer {
pom.scopeMappings.with {
mappings.clear()
addMapping(300, configurations.foo, 'runtime')
}
pom.artifactId = 'other artifact id than main jar'
...
}
}
}
This solution was inspired by following thread:
http://gradle.1045684.n5.nabble.com/pom-generation-and-inherited-dependencies-td1436197.html
Note the line:
addMapping(300, configurations.foo, 'runtime')
You can add other configurations as well, or use other maven scope than the 'runtime' scope.
300 stands for priority, which is not significant in this case.
Advantage of this solution is that we have quite fine control over dependencies and their mappings. Disadvantage would be that this will not work for the install task. We would need to setup different poms for the install task, which might be possible, but it is beyond my knowledge.
Alternative with usage of maven-publish plugin:
apply plugin: "java"
apply plugin: "maven-publish"
configurations {
foo // Custom configuration for the artifact I want to build and publish
}
// Dependencies for Java configurations (nothing to do with foo)
dependencies {
compile "foo:foo:1.1"
testCompile "bar:bar:2.2"
}
// Custom task
task generateFoo(type: JavaExec) {
ext.outputFile = new File(buildDir, "foo.bar")
...
}
artifacts {
foo file: generateFoo.outputFile, builtBy: generateFoo
}
publishing {
publications {
foo(MavenPublication) {
from new org.gradle.api.internal.java.JavaLibrary(\
configurations.api.artifacts.toArray()[0], \
configurations.api.allDependencies)
artifactId 'other artifact id than main jar'
}
}
repositories {
maven { ... }
}
}
gradle tasks will give you possible publish commands:
publish - Publishes all publications produced by this project.
publishFooPublicationToMavenLocal - Publishes Maven publication 'foo' to the local Maven repository.
publishFooPublicationToMavenRepository - Publishes Maven publication 'foo' to Maven repository 'maven'.
publishToMavenLocal - Publishes all Maven publications produced by this project to the local Maven cache.
...
The advantege of maven-publish alternative is that it will work for both local and remote maven repositories. Disadvantage may be a fact that we are using the internal API which may change in future and that all dependencies are mapped to the 'runtime' maven scope. Also the MavenPublication snippet would be much more verbose and more internal API would be used if we would like to use some other maven scope or apply some scope mapping.
I am using Gradle 1.10

Gradle Release System Task - Calling uploadArchives on subprojects

In order to release my system, I am creating a releaser project that calls uploadArchives for a number of other projects.
In my project I have the following files:
settings.gradle
include '../proj-a', '../proj-b'
build.gradle
task releaseSystem() {
// what goes here?
}
What should the contents of the releaseSystem task be such that I can call gradle releaseSystem and have it run uploadArchives for each of my sub projects?
I've tried a number of options, but none have been successful thus far.
Thank you for any insights you can provide.
ANSWER
I'm continually impressed with the graceful solutions gradle provides to my problems. Here's my final solution (as Peter pointed out below):
settings.gradle
include 'proj-a', 'proj-b'
project (':proj-a').projectDir = new File(settingsDir, '../proj-a')
project (':proj-b').projectDir = new File(settingsDir, '../proj-b')
build.gradle
task releaseSystem() {
dependsOn {
[
subprojects.build,
subprojects.uploadArchives
]
}
}
Note, that since my repository is a maven repository, when I originally included '../proj-a' in my settings.gradle, it produced poms with artifactId ../proj-a which was invalid. I had to change my settings.gradle to the format above for the system to put the poms together correctly and for the uploadArchives task to complete successfully.
Assuming all subprojects have an uploadArchives task:
task releaseSystem {
dependsOn { subprojects.uploadArchives }
}
Note that this won't run the subprojects' tests.

Resources