This project demonstrates how to publish a Gradle project to OSSRH. Refer to build.gradle for a complete working sample.
The following steps discuss how to configure the build.gradle
for deploying to OSSRH.
The first step is to apply the necessary plugins:
plugins {
id "java"
id "signing"
id "maven-publish"
id "io.github.gradle-nexus.publish-plugin" version "1.0.0"
}
-
We apply the Java Plugin to use Gradle’s standard Java conventions. This is applied to every project that is deployed.
-
We apply the Signing Plugin to sign the Maven artifacts. This is applied to every project that is deployed.
-
We apply the maven-publish adds the ability to publish our artifacts. This is applied to every project that is deployed.
-
We apply the io.github.gradle-nexus.publish-plugin to manage the staging repository in OSSRH. This is applied only to the root project.
We configure the group
so it can be used to default the group in the pom:
group = "io.rwinch.sample"
Here we configure the Gradle Publications (i.e. what will be published to the repository).
In our example, we configure a publication named maven
that contains all of the java
compoonents.
This is applied to every project that is deployed.
publishing {
publications {
maven(MavenPublication) {
from(components.java)
}
}
}
Next we must ensure that all of the OSSRH requirements are met. This is applied to every project that is deployed.
The following code generates the required Javadoc and Sources using Gradle’s Java extension.
java {
withJavadocJar()
withSourcesJar()
}
The following code configures the Signing Plugin to generate the required signatures if the Gradle property signingKey
or signingKeyId
is found.
def hasSigningKey = project.hasProperty("signingKeyId") || project.hasProperty("signingKey")
if(hasSigningKey) {
sign(project)
}
void sign(Project project) {
project.signing {
required { project.gradle.taskGraph.hasTask("required") }
def signingKeyId = project.findProperty("signingKeyId")
def signingKey = project.findProperty("signingKey")
def signingPassword = project.findProperty("signingPassword")
if (signingKeyId) {
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
} else if (signingKey) {
useInMemoryPgpKeys(signingKey, signingPassword)
}
sign publishing.publications.maven
}
}
Next we need to ensure that Gradle is configured to customize the pom in order to ensure the required metadata is present in the Maven pom.
The groupId
, artifactId
, and vession
of the pom are defaulted by the maven-publish
plugin.
project.plugins.withType(MavenPublishPlugin).all {
PublishingExtension publishing = project.extensions.getByType(PublishingExtension)
publishing.publications.withType(MavenPublication).all { mavenPublication ->
mavenPublication.pom {
name = "${project.group}:${project.name}"
description = name
url = "https://github.com/rwinch/gradle-publish-ossrh-sample"
licenses {
license {
name = "The Apache License, Version 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0"
}
}
developers {
developer {
id = "rwinch"
name = "Rob Winch"
email = "[email protected]"
}
}
scm {
connection = "scm:git:https://github.com/rwinch/gradle-publish-ossrh-sample"
developerConnection = "scm:git:ssh://github.com/rwinch/gradle-publish-ossrh-sample.git"
url = "https://github.com/rwinch/gradle-publish-ossrh-sample"
}
}
}
}
We configure the Gradle Nexus Publishing Plugin.
By default it uses credentials configured as the project properties sonatypeUsername
and sonatypePassword
.
This is only configured on the root project.
If you registered with Sonatype after 24 February 2021 to publish to Maven Central, then you must set your nexusUrl
and snapshotRepositoryUrl
manually. See https://central.sonatype.org/news/20210223_new-users-on-s01/ for more information.
nexusPublishing {
repositories {
sonatype() //sonatypeUsername and sonatypePassword properties are used automatically
// OR, if you registered with Sonatype after 24 February 2021
// sonatype {
// nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
// snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
// }
}
// these are not strictly required. The default timeouts are set to 1 minute. But Sonatype can be really slow.
// If you get the error "java.net.SocketTimeoutException: timeout", these lines will help.
connectTimeout = Duration.ofMinutes(3)
clientTimeout = Duration.ofMinutes(3)
}
Finally, we set up a repository on our local file system in the build directory, so that we can easily test publishing independant of OSSRH. This is not required, but it makes troubleshooting easier. This is applied to every project that is deployed.
publishing {
repositories {
maven {
name = "local"
// change URLs to point to your repos, e.g. http://my.org/repo
def releasesRepoUrl = "$buildDir/repos/releases"
def snapshotsRepoUrl = "$buildDir/repos/snapshots"
url = version.endsWith("SNAPSHOT") ? snapshotsRepoUrl : releasesRepoUrl
}
}
}
The configuration we use requires the following Gradle Properties to be set:
-
signingKey
the pgp used to sign the artifacts. Generate gpg key with commandgpg --export-secret-keys --armor F207B5A1 > my.gpg
.F207B5A1
is last 8 characters in the public key, you can get public key bygpg --list-keys
-
signingPassword
the password for thesigningKey
andmy.gpg.pgp
. -
sonatypeUsername
the username from sonatype used to publish artifacts. This is the token username, not the one used for logging into the UI. -
sonatypePassword
the password from sonatype used to publish artifacts. This is the token password, not the one used for logging into the UI.
The following will provide all the Gradle Properties necessary for the commands below using environment variables.
export ORG_GRADLE_PROJECT_signingKey=`cat my.gpg`
export ORG_GRADLE_PROJECT_signingPassword=password
export ORG_GRADLE_PROJECT_sonatypeUsername=<replace-with-your-token-username>
export ORG_GRADLE_PROJECT_sonatypePassword=<replace-with-your-token-password>
When you first set up the code or need to troubleshoot signing process, you can run the signMavenPublication
task.
$ ./gradlew signMavenPublication $ tree build/libs build/libs ├── publish-ossrh-sample-1.0.0.jar ├── publish-ossrh-sample-1.0.0.jar.asc ├── publish-ossrh-sample-1.0.0-javadoc.jar ├── publish-ossrh-sample-1.0.0-javadoc.jar.asc ├── publish-ossrh-sample-1.0.0-sources.jar └── publish-ossrh-sample-1.0.0-sources.jar.asc
You can also test what is going to be published using the local publication to publish to a directory on your local machine. You will notice, if we have defined the signing properties, that all of our artifacts (jars, javadoc, pom, etc) are also signed.
$ ./gradlew publishMavenPublicationToLocalRepository $ tree build/repos/releases/ build/repos/releases/ └── io └── rwinch └── sample └── publish-ossrh-sample ├── 1.0.0 │ ├── publish-ossrh-sample-1.0.0.jar │ ├── publish-ossrh-sample-1.0.0.jar.asc │ ├── publish-ossrh-sample-1.0.0.jar.asc.md5 │ ├── publish-ossrh-sample-1.0.0.jar.asc.sha1 │ ├── publish-ossrh-sample-1.0.0.jar.asc.sha256 │ ├── publish-ossrh-sample-1.0.0.jar.asc.sha512 │ ├── publish-ossrh-sample-1.0.0.jar.md5 │ ├── publish-ossrh-sample-1.0.0.jar.sha1 │ ├── publish-ossrh-sample-1.0.0.jar.sha256 │ ├── publish-ossrh-sample-1.0.0.jar.sha512 │ ├── publish-ossrh-sample-1.0.0-javadoc.jar │ ├── publish-ossrh-sample-1.0.0-javadoc.jar.asc ...
If you publish to OSSRH it will automatically create a staging repository if the version looks like a release:
$ ./gradlew publishToSonatype
Now visit https://oss.sonatype.org/#stagingRepositories and you can view the staged repository using the Sonatype username and password (the UI one not the token).
We can publish and close the repository all at once:
$ ./gradlew publishToSonatype closeSonatypeStagingRepository
If you want to release the staged repository as well, then you can perform:
$ ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
Note
|
If you want to perform some additional checks before you release the repository, you can create a task that mustRunAfter the closeSonatypeStagingRepository task and ensure that the releaseSonatypeStagingRepository task depends on it. The staging repository can be accessed at https://oss.sonatype.org/service/local/repositories/{staging-id}/content
|
To make publish automatic by action in github.
Fist to add repository secrets:
-
SIGNING_KEY
the pgp used to sign the artifacts. Generate gpg key with commandgpg --export-secret-keys --armor F207B5A1 > my.gpg
. usecat my.gpg
to output secret key in the console and copy the output as the vale ofSIGNING_KEY
.F207B5A1
is last 8 characters in the public key, you can get public key bygpg --list-keys
-
SIGNING_PASSWORD
the password for theSIGNING_KEY
. -
OSSRH_TOKEN_USERNAME
the username from sonatype used to publish artifacts. This is the token username, not the one used for logging into the UI. -
OSSRH_TOKEN_PASSWORD
the password from sonatype used to publish artifacts. This is the token password, not the one used for logging into the UI.
Second to create publish script in the github action, please add the publish.yml file in the .github/workflows
name: Publish env: ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.OSSRH_TOKEN_USERNAME }} ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.OSSRH_TOKEN_PASSWORD }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }} ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }} on: push: tags: - 'v[0-9]+\.[0-9]+' jobs: publish: name: Release build and publish runs-on: ubuntu-22.04 steps: - name: Check out code uses: actions/[email protected] - name: Set up JDK 17 uses: actions/[email protected] with: distribution: adopt java-version: 17 - name: Build run: ./gradlew signMavenPublication publishMavenPublicationToLocalRepository - name: Publish to NEXUS run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
Third to change and update the version variable in the build.gradle file. If you do not update version, there will be failed for releaseSonatypeStagingRepository, because version confilicate with the package have been released.
So if you create a new tag like v1.0, the workflow will run and the package will publish to the remote repository.