7
votes

I've written a Gradle plugin in groovy and used Gradle to build it. I've got a local network Artifactory server that I publish the results to using the Gradle Artifactory plugin and the maven-publish plugin in Gradle. I have another Gradle build script that relies on this plugin as a dependency. I've been able to get this all working if I list my dependency with a specific version. I've tried to use a maven version range (ex. '[1.0,2.0)'), but this fails saying it can't find maven-metadata.xml. I checked Artifactory, and sure enough, it isn't there. What do I need to do to produce it, preferably during the build of the plugin?

Here is the build.gradle file for my custom gradle plugin:

buildscript {
    repositories {
        maven {
            url "${artifactory_contextUrl}/plugins-release"
            credentials {
                username = "${artifactory_user}"
                password = "${artifactory_password}"
            }
        }
    }
    dependencies {
        classpath group: 'org.apache.directory.studio', name: 'org.apache.commons.io', version: '2.4'
        classpath group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '2.0.9'
    }
}

plugins {
    id 'com.jfrog.artifactory' version '3.0.1'
}

apply plugin: 'groovy'
apply plugin: 'maven-publish'

artifactory {
    contextUrl = "${artifactory_contextUrl}"
    publish {
        repository {
            repoKey = 'plugins-snapshot-local'
            username = "${artifactory_user}"
            password = "${artifactory_password}"
            maven = true
        }
        defaults {
            publications ('mavenJava')
        }
    }
    resolve {
        repository {
            repoKey = 'libs-release'
            username = "${artifactory_user}"
            password = "${artifactory_password}"
            maven = true
        }
    }
}

dependencies {
    compile gradleApi()
    compile localGroovy()
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
}

I've searched the Gradle, Artifactory, and Maven documentation to understand maven-metadata.xml and how to generate and deploy. It makes sense what it is, and I could probably build one manually, but I can't find anything that explains specifically how to automatically generate it in Gradle with either the maven-publish plugin or the artifactory-gradle-plugin. I don't want to have to manually update the file since that would defeat the automation effort, and I don't want to switch to mvn since I've already invested so much in Gradle.

4

4 Answers

5
votes

A groupId had to be added to the publications section. Once implemented, a maven-metadata.xml file was published to the artifact repository.

publishing {
    publications {
        mavenJava(MavenPublication) {
            groupId = 'com.group'
        }
    }
}
1
votes

I had the same problem and it turned out that the Artifactory repository was not a Maven repo, it was a Generic repo. It took me forever to notice because I didn't create the repo and I assumed it was a Maven repo, and deploying/resolving otherwise was working as expected.
After switching to a Maven repo, the maven-metadata.xml's were generated upon publishing.

0
votes

maven-metadata.xml should be handled by Artifactory. What is your local repository layout in Artifactory?

0
votes

The accepted answer is correct. And I upvoted it. However, there is also this caveat.

I have a multi module project, so I will use "allprojects". If you have a monolith/single-jar ( :( ).. you can use use a different scope than "allprojects".

They key here is that you set the "group". (and version as well)

allprojects {

apply plugin: 'java-library'
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.artifactory'


repositories {
    jcenter()
}

group = 'com.group'

version = '1.0-SNAPSHOT'

}

Ok, now build.gradle (which in my multi-module project is not the root-build.gradle) (but values in a root build.gradle would be similar)

below is the entire contents of my non-root build.gradle file

// the "name" variable inside the publications/myPublicationName block is getting overwritten.  so create a variable here to capture the name (as the artifactid)
def artifactIdForPublicationBlockHolder = "${name}"


dependencies {
    testImplementation group: 'junit', name: 'junit', version: junitVersion
}

println("hey.here.read.me")
println("group=${group}")
println("version=${version}")
println("artifactId=${name}")


publishing {
    publications {
        myCustomPublicationName(MavenPublication) {
            // groupId, artifactId and version have defaults, so do not arbitrarily override : https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:publications

//your value below could be slightly different, look for *.jar after you do ./gradlew. build (note, this path value (of "./") is relative to the non-root-build.gradle path, not the overall root-build.gradle
"./build/libs/${artifactIdForPublicationBlockHolder}-${version}.jar"
        }
    }
}

As the link says, you'll get defaults for

// groupId, artifactId and version have defaults, so do not arbitrarily override : https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:publications

You just have to set these values, like I show above with the

group = 'com.group' 
version = '1.0-SNAPSHOT'

code

After going thru the grinder a few times with the

myCustomPublicationName(MavenPublication)

I find the least amount of stuff I custom-set, the better. and prefer to piggy back on the defaults...which means setting the values which drive the defaults... in the build.gradle .. and not to set the myCustomPublicationName(MavenPublication)

altering the values inside

myCustomPublicationName(MavenPublication)

should be reserved (IMHO) to when the defaults don't work for you. which usually is a very small minority of time.

note:

"${name}" at the top of my non-root-gradle.build is getting populated by the directory structure of my multi-module project.

i don't know how it works in a non multi-module since i never write monoliths.

settings.gradle example in my code:

rootProject.name = 'com.me.myproject-rootProjectName'

include ':source:java:mydatalayer'
include ':source:java:mybizlogic'
include ':source:java:mydomain'

Bonus Quote below:

Moreover, modular decomposition represents a key component of software quality. If you have a tightly coupled system, when you tweak one component, the whole system breaks. If you thought in terms of APIs, the intermodular boundaries are clear, so you can maintain and improve one module without affecting the others.

Massive refactorings prove difficult. If you built something as a monolithic system and then find you had repeated code all over the place, and you want to refactor it properly, you'll have a massive job. In contrast, if you wrote it as components, but you got some of the component boundaries a little wrong, you can tweak them easily.

-- Joshua Bloch, former Chief Java Architect at Google. Modular Decomposition Link