How to publish a Gradle shadow jar without a shadow pom - gradle

I use the shadow jar plugin (https://github.com/johnrengelman/shadow) to create an extra jar file with all my dependencies packaged inside. I also would like to keep the default generated jar that has only my code & resources. This is pretty simple and straightforward until it comes to publishing the jars.
As my shadow jar has all its deps inside, its pom file is not a priority for me in terms of publishing. However if I follow the instructions laid out in the shadowJar plugin documentation (https://imperceptiblethoughts.com/shadow/publishing/#publishing-with-maven-publish-plugin)
publishing {
publications {
shadow(MavenPublication) { publication ->
project.shadow.component(publication)
}
}
I end up with two different pom files (one for the regular jar file and one for the shadow jar) published to the same URL with one overriding the other. This behavior causes customers to download the default jar but without any dependencies in the pom file.
I tried many ways to disable shadowJar pom file but without any success. How do I get it to work?

Eventually I just ignored the instructions in the plugin's documentation and published the shadow jar just like I publish my sources jar:
Simplest way to do this for a single project is:
publishing {
publications {
Library(MavenPublication) {
from project.components.java
artifact tasks.sourceJar
artifact tasks.shadowJar
}
}
}
If you have multiple projects (not all having shadowJar plugin applied) and want to add this logic in the root project, you can do this:
subprojects {
afterEvaluate {
publishing {
publications {
Library(MavenPublication) {
from project.components.java
artifact tasks.sourceJar
if (project.tasks.findByName("shadowJar") != null) {
artifact tasks.shadowJar
}
}
}
}
}
}

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 can I get gradle to populate jars with dependency metadata?

So if I build a jar in Maven, say for example jackson-core-2.5.1.jar, I find the following in the artifact:
META-INF/maven/
META-INF/maven/com.fasterxml.jackson.core/
META-INF/maven/com.fasterxml.jackson.core/jackson-core/
META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.properties
META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.xml
Gradle, however, does not seem to create this data. Problem is that we have several components of our build, including a parent project, that aren't hosted in the same SCM location. For our large and complex build, how would Gradle know that a locally built artifact in one SCM location depends on a locally built artifact in another, if there's no metadata? What is the Gradle way to manage this situation?
Repositories contain a separate copy of pom.xml. It usually lives next to the JAR file in the physical structure on the disk. This applies to binary repositories like Nexus or Artifatory and also to your local Maven repository (under $HOME/.m2/repo).
If for some reason you want to copy the behavior of Maven you can tell Gradle to do create those files. We use this customization in subprojects closure that configures our multi-project Gradle build.
jar {
// until we have better solution
// https://discuss.gradle.org/t/jar-task-does-not-see-dependency-when-using-maven-publish/11091
if (project.tasks.findByName('generatePomFileForMavenJavaPublication')) {
dependsOn 'generatePomFileForMavenJavaPublication'
} else {
project.tasks.whenTaskAdded { addedTask ->
if (addedTask.name == 'generatePomFileForMavenJavaPublication') {
project.tasks.jar.dependsOn 'generatePomFileForMavenJavaPublication'
}
}
}
into("META-INF/maven/$project.group/$project.archivesBaseName") {
/*
from generatePomFileForMavenJavaPublication
*/
from new File(project.buildDir, 'publications/mavenJava')
rename ".*", "pom.xml"
}
}
It would be simpler if there wasn't a problem with generatePomFileForMavenJavaPublication task being created lazily sometimes. And you need to check how to create your properties file. I guess you can dump properties from a running Gradle process there.

Gradle: publish an artifact with a subset of files/dependencies of a project

I am lost in all these plugins :(
What I have so far, looks something like this:
artifactory {
contextUrl = artifactoryUrl
publish {
repository {
repoKey = project.repo
username = artifactoryUser
password = artifactoryPassword
maven = true
defaults {
publications('mavenJava')
}
}
}
version = "${majorVersion}${buildNumber}${snapshot}"
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifact sourceJar {
classifier = 'sources'
}
}
}
}
I am sorry, I did write this myself, but, mostly by copying and pasting things from various examples I found on the web, so I am not sure what this is actually doing :(
Surprisingly, this works: I can do gradle artifactoryPublish, and uploads a pom file and two jars (classes and sources) to artifactory.
But, I need to modify it, so that I can publish a subset of the project as a different artifact, and only include the dependencies that it requires.
I managed to build a jar file:
task utilJar(type: Jar) {
from sourceSets.main.output.classesDir
include '**/util/*.class'
baseName 'basic-util'
}
I also defined a Configuration with a subset of dependencies:
configurations {
util
}
dependencies {
util "org.apache.commons:commons-lang3:3.4"
util "com.google.guava:guava:18.0"
}
artifacts {
util utilJar
}
This is where I get stuck. How do I get this new artifact published?
I tried following the same strategy:
publishing {
publications {
mavenUtil(MavenPublication) {
from components.java
artifact utilJar {
classifier = 'util'
}
}
}
}
artifactory {
defaults {
publications('mavenUtil')
}
}
(this is in a subproject, so I need it do be incremental)
This does not work:
Could not find method defaults() for arguments [build_8axxghkylu3559p5lal6spy1u$_run_closure7$_closure11#38abcbd4]
But, even if it did, it would still be not be quite what I need, because I don't know how I could then publish a particular artifact: the only way I could ever publish anything at all is gradle artifactoryPublish, but that does not ask me which artifacts to publish. Will it publish everything every time?
As you can see, I am hopelessly lost here :(
Could someone with a clue please show me the light?
UPDATE So, I removed the last artifactory closure from the build, and now I not getting the error, but still can't get it to do what I need.
My artifactory seems to be not in good mood right now, so I am publishing locally.
gradle publishToMavenLocal creates a single artifact as before, but adds my new "-util" jar to it. This is not at all what I want. I need a new, separate artifact, with that jar file, and its own set of dependencies.
Here: https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:repositories I found a suggestion, that "The created task is named “publish«PUBNAME»PublicationTo«REPONAME»Repository”". This does not seem to to be true, because gradle publishUtilMavenPublicationToMavenLocalRepository (with all the case variations I could think off) says that there is no such task. :(
Have you tried using the set me up feature? It available for all version of Artifactory. It should provide a good starting point to configure you gradle project to work with Artifactory.
Specifically for Gradle it can assist you with generating the relevant configuration according to the repositories you wish to resolve / publish from / to, defining the relevant dependencies to install the jfrog plugin etc.

Gradle's publishToMavenLocal task is not executed correctly

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

Uploading multiple existing JARs to Maven repository with Gradle

I need to implement a Gradle task that will upload multiple existing JARs to the Maven repository.
The tricky part is that list of JARs is not known in advance. The way I want it to work is to dowload certain ".tar.gz" file first, untar it, then scan for JARs and upload them with some sort of naming convention (like use JAR names as artifactId's and version of the .tar.gz as version).
What would be the easiest way to do that with Gradle? Currently it is a simple bash script which searches for JARs and runs Maven deploy:deploy-file for each of them, but I need to incorporate that functionality in gradle build script.
Ideally, I need task like "deployJars" that would upload everything and depend on "downloadTarGz" task.
Update:
How about uploading POMs without any JAR attached to it? I need to generate several POMs that will depend on these dynamically detected JARs and upload them, too. "artifacts" requires me to specify file for upload.
To upload a jar via gradle you must declare that jar as a publish artifact and add it to a specific configuration using the artifacts closure:
apply plugin:'maven'
configurations{
allJars
}
artifacts{
allJars file("path/to/jarFile.jar")
}
now you can configure the dynamically created uploadAllJars task:
uploadAllJars {
repositories {
mavenDeployer {
repository(url: 'http://localhost:8081/artifactory/acme') {
authentication(userName: 'admin', password: 'password');
}
}
}
The problem is that you want to upload multiple artifacts. To achieve that you need some more dynamic in your build script. The dynamic creation of publishartifacts for all discovered jars can be wrapped in a task. In my example the discoverAllJars task simply looks in a specified folder for jar files. Here you need to implement your own logic to lookup the jars in your tgz archive.
group = "org.acme"
version = "1.0-SNAPSHOT"
task discoverAllJars{
ext.discoveredFiles = []
doLast{
file("jars").eachFile{file ->
if(file.name.endsWith("jar")){
println "found file ${file.name}"
discoveredFiles << file
artifacts{
allJars file
}
}
}
}
}
To be able to upload multiple artifacts within the uploadAllJars task you have to use pom filter. For details about pom filter have a look at the gradle userguide at http://www.gradle.org/docs/current/userguide/maven_plugin.html#uploading_to_maven_repositories
Since we moved the configuration of the published artifacts into the execution phase of gradle we must configure the uploadAllJars in the execution phase too. Therefore I'd create a configureUploadAllJars task. Note how we reference the jar files discovered by using 'discoverAllJars.discoveredFiles':
task configureUploadAllJars{
dependsOn discoverAllJars
doLast{
uploadAllJars {
repositories {
mavenDeployer {
repository(url: 'http://yourrepository/') {
authentication(userName: 'admin', password: 'password');
}
discoverAllJars.discoveredFiles.each{discoveredFile ->
def filterName = discoveredFile.name - ".jar"
addFilter(filterName) { artifact, file ->
file.name == discoveredFile.name
}
pom(filterName).artifactId = filterName
}
}
}
}
}
}
Now you just need to add a dependency between uploadAllJars and configureUploadAllJars:
uploadAllJars.dependsOn configureUploadAllJars
This example uses the same group and version for all discovered jar files and the jar name as artifactId. you can change this as you like using the pom filter mechanism.
hope that helped,
cheers,
René

Resources