Sign and publish on Maven Central a Project with the new maven-publish Gradle plugin

In this second part of this article (first part here), I will explain how to publish a Gradle based project on Maven Central using the new maven-publish plugin.

This article is in two-parts. You can find the first one here (it explains how to publish a library on Maven Central, regardless of the Build Automation Tool you are using). This one will focus on how to sign and publish a library using the new maven-publish Gradle plugin.

Acknowledgements

Thanks to Trustin Lee (Twitter & Github) and Igor Spasić (Twitter & Github). After hours of research I finally found their code that help me a lot for my project and to write this article.

Background

As stated on the first part of this article, I recently had to include in one of my project (using Maven as build system) a Java library using Gradle as build system and not available on Maven Central. I decided to publish this library on Maven Central to facilitate my developments and to keep Gradle as build tool to not have to change the project structure etc…

Not long ago, Gradle release a new plugin for Maven publishing, called maven-publish. Unfortunately, this plugin doesn’t support the signing of the POM yet (despite the fact that this feature would be very useful and claimed since 2013). Actually, Gradle recommends to use the old maven and signing module for publishing on Maven Central. So I had two choices:

  • Use the old and shortly-deprecated maven plugin.
  • Find a workaround to sign my pom.xml with the new maven-publish plugin and take advantages of all the new features.

I choose the second approach and in this tutorial, I’ll show you how to write your build.gradle to get things done.

Basic configuration

Note that all this tutorial was written using Gradle 4.4.1

We’ll begin our build.gradle file by declaring all the plugins we need. In our case:

  • The Java plugin as we are compiling a Java project.
  • The maven-publish plugin to generate our pom and publish it to a repository (Maven Central)
  • The signing plugin to sign our artifacts and pom.xml
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'signing'

If needed, you can also declare your dependencies for the project and the repositories you are using for resolving these dependencies:

repositories {
mavenCentral()
}

dependencies {
compile 'com.google.code.findbugs:jsr305:3.0.1'
compile 'com.google.guava:guava:19.0'
compile 'com.google.code.gson:gson:2.5'
testCompile 'junit:junit:4.+'
testCompile 'com.google.truth:truth:0.28'
}

We’ll also declare two tasks to create the source and javadoc jars for our project and declare the artifacts for publishing:

task sourceJar(type: Jar) {
classifier "sources"
from sourceSets.main.allJava
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier "javadoc"
from javadoc.destinationDir
}

artifacts {
archives jar
archives sourceJar
archives javadocJar
}

Publishing

Next, we’ll prepare for the publishing (without signing):

publishing {
publications {
mavenJava(MavenPublication) {
customizePom(pom)
groupId 'com.github.mautini'
artifactId 'schemaorg-java'
version '1.0.1'

from components.java

artifact(sourceJar) {
classifier = 'sources'
}
artifact(javadocJar) {
classifier = 'javadoc'
}
}
}
repositories {
maven {
url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
credentials {
username sonatypeUsername
password sonatypePassword
}
}
}
}

We configure a maven publication named mavenJava with a custom groupId, artifactId and version. We also specify the artifacts to publish.

Under, we define the repository for the publishing. Here we use the central repository from Sonatype (to publish on Maven Central). Note the sonatypeUsername and sonatypePassword variables, it’s your Jira account credentials (that you created on the first step in the first article). We’ll discuss about this later in this article.

If you look closely the code above, you can see a call to a method customizePom, it’s the piece of code that is responsible to add to the generated pom all the requirements from Sonatype. Here is what it looks like for my project:

def customizePom(pom) {
pom.withXml {
def root = asNode()

// eliminate test-scoped dependencies (no need in maven central POMs)
root.dependencies.removeAll { dep ->
dep.scope == "test"
}

// add all items necessary for maven central publication
root.children().last() + {
resolveStrategy = Closure.DELEGATE_FIRST

description 'Serialize and Deserialize Json-LD into Java entities'
name 'Schema.org Java'
url 'https://github.com/mautini/schemaorg-java'
organization {
name 'com.github.mautini'
url 'https://github.com/mautini'
}
issueManagement {
system 'GitHub'
url 'https://github.com/mautini/schemaorg-java/issues'
}
licenses {
license {
name 'Apache License 2.0'
url 'https://github.com/mautini/schemaorg-java/blob/master/LICENSE'
distribution 'repo'
}
}
scm {
url 'https://github.com/mautini/schemaorg-java'
connection 'scm:git:git://github.com/mautini/schemaorg-java.git'
developerConnection 'scm:git:ssh://git@github.com:mautini/schemaorg-java.git'
}
developers {
developer {
name 'Mautini'
}
}
}
}
}

Finally, we’ll set a task to generate the pom of the project (without publishing it) in case we need it:

model {
tasks.generatePomFileForMavenJavaPublication {
destination = file("$buildDir/generated-pom.xml")
}
}

Signing

We saw how to publish our files on Maven Central but remember, we have to sign it with our GPG key (generated in the first article) first. So, we’ll use the signing Gradle plugin for this task.

We start by specifying what we want to sign:

signing {
sign configurations.archives
}

and now, where all the magic happens, in our MavenPublication mavenJava, we’ll add the following lines

// create the sign pom artifact
pom.withXml {
def pomFile = file("${project.buildDir}/generated-pom.xml")
writeTo(pomFile)
def pomAscFile = signing.sign(pomFile).signatureFiles[0]
artifact(pomAscFile) {
classifier = null
extension = 'pom.asc'
}
}
// create the signed artifacts
project.tasks.signArchives.signatureFiles.each {
artifact(it) {
def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/
if (matcher.find()) {
classifier = matcher.group(1)
} else {
classifier = null
}
extension = 'jar.asc'
}
}

The first part will create the previously configured pom file on the disk, sign it and create the signed artifact.

The second part will iterate through the signed files generated by the signing plugin and provide the artifacts for publishing.

Now, we just have to add the dependency to the signArchives task for our publication tasks (note the tasks are called publishMavenJavaPublicationToMavenLocal and publishMavenJavaPublicationToMavenRepository as our Maven Publication is called MavenJava).

model {
tasks.publishMavenJavaPublicationToMavenLocal {
dependsOn project.tasks.signArchives
}
tasks.publishMavenJavaPublicationToMavenRepository {
dependsOn project.tasks.signArchives
}
}

Our build.gradle file is now complete.

Configuration file

Last but not least, in order to sign our pom and artifacts and publish them on Maven Central we must have the GPG key we generate in the first article and the credentials of our Sonatype account. All this things will be put in a file called gradle.properties located in our gradle home (usually ~/.gradle). If this file doesn’t exist, create it and add the following:

signing.keyId=<_GPG_KEY_SHORT_ID>
signing.password=<GPG_KEY_PASSWORD>
signing.secretKeyRingFile=<PATH_TO_YOUR_GPG_SECRET_KEY>/

sonatypeUsername=<YOUR_SONATYPE_USERNAME>
sonatypePassword=<YOUR_SONATYPE_PASSWORD>

If you haven’t set a passphrase for your GPG key, you can let a blank on this line.

Note the sonatypeUsername and sonatypePassword we previously saw on our build.gradle file.

Put all the piece together

Here is our final build.gradle file from this tutorial:

Don’t forget to edit the gradle.properties file and run the command gradle publish. The publishing must succeed and you can continue with the step Release your library and check if everything is OK on my first article.

Resources

Closing Thoughts

I hope this second article was useful for you and you enjoyed reading it. Don’t hesitate to comment and to give me some feedback. You can also read the first part of this article here.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store