Publishing to Maven Central
Maven Central is the defacto artifact repository for JVM based projects. Anyone can publish artifacts to it as long as they follow the rules. Follow this guide to register an account. You also must comply with all requirements otherwise deployment will fail. Fortunately JReleaser can verify many of those requirements before any artifacts are deployed.
Before continuing make sure that all artifacts to be deployed have been staged. Missing this step will make the deployment phase fail! We recommend performing deployments from a clean state. |
Maven Central requires artifacts to be signed with PGP. The MavenCentral deployer
automatically enables applyMavenCentralRules
. The Nexus2 deployer
automatically enables applyMavenCentralRules
when the publication url contains oss.sonatype.org
. This setting
performs the following tasks:
-
verify POMs comply with publication rules (using PomChecker).
-
assert that matching
-sources.jar
and-javadoc.jar
artifacts have been staged (when applicable). -
calculate
md5
,sha1
,sha256
, andsha512
checksums for all staged artifacts. -
sign all staged artifacts.
You have the option to close and release the staged repository automatically right after deployment, or keep the staged repository open and perform close and release operations using the UI. You must login into Sonatype OSSRH using your Sonatype account to do so.
Portal Publisher API
Publishing using the Portal Publisher API requires using the MavenCentral deployer.
signing:
active: ALWAYS
armored: true
deploy:
maven:
mavenCentral:
sonatype:
active: ALWAYS
url: https://central.sonatype.com/api/v1/publisher
stagingRepositories:
- target/staging-deploy
[signing]
active = "ALWAYS"
armored = true
[deploy.maven.mavenCentral.sonatype]
active = "ALWAYS"
url = "https://central.sonatype.com/api/v1/publisher"
stagingRepositories = ["target/staging-deploy"]
{
"jreleaser": {
"signing": {
"active": "ALWAYS",
"armored": true
},
"deploy": {
"maven": {
"mavenCentral": {
"sonatype": {
"active": "ALWAYS",
"url": "https://central.sonatype.com/api/v1/publisher",
"stagingRepositories": [
"target/staging-deploy"
]
}
}
}
}
}
}
<jreleaser>
<signing>
<active>ALWAYS</active>
<armored>true</armored>
</signing>
<deploy>
<maven>
<mavenCentral>
<sonatype>
<active>ALWAYS</active>
<url>https://central.sonatype.com/api/v1/publisher</url>
<stagingRepositories>target/staging-deploy</stagingRepositories>
</sonatype>
</mavenCentral>
</maven>
</deploy>
</jreleaser>
jreleaser {
signing {
active = 'ALWAYS'
armored = true
}
deploy {
maven {
mavenCentral {
sonatype {
active = 'ALWAYS'
url = 'https://central.sonatype.com/api/v1/publisher'
stagingRepository('target/staging-deploy')
}
}
}
}
}
OSSRH
Publishing to OSSRH requires using the Nexus2 deployer.
We recommend setting releaseRepository to false for the first time. This lets you review staged artifacts in the
Sonatype UI. You’ll have to perform a manual release on the UI. Once you’re happy with the settings and the release is
successful you may switch this property to true .
|
As described here, Sonatype projects created before February 2021 may need to use "https://oss.sonatype.org/service/local" instead of "https://s01.oss.sonatype.org/service/local". Using incorrect url may cause a 'Could not find a staging profile matching …' error. |
signing:
active: ALWAYS
armored: true
deploy:
maven:
nexus2:
maven-central:
active: ALWAYS
url: https://s01.oss.sonatype.org/service/local
snapshotUrl: https://s01.oss.sonatype.org/content/repositories/snapshots/
closeRepository: true
releaseRepository: false
stagingRepositories:
- target/staging-deploy
[signing]
active = "ALWAYS"
armored = true
[deploy.maven.nexus2.maven-central]
active = "ALWAYS"
url = "https://s01.oss.sonatype.org/service/local"
snapshotUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
closeRepository = true
releaseRepository = false
stagingRepositories = ["target/staging-deploy"]
{
"jreleaser": {
"signing": {
"active": "ALWAYS",
"armored": true
},
"deploy": {
"maven": {
"nexus2": {
"maven-central": {
"active": "ALWAYS",
"url": "https://s01.oss.sonatype.org/service/local",
"snapshotUrl": "https://s01.oss.sonatype.org/content/repositories/snapshots/",
"closeRepository": true,
"releaseRepository": false,
"stagingRepositories": [
"target/staging-deploy"
]
}
}
}
}
}
}
<jreleaser>
<signing>
<active>ALWAYS</active>
<armored>true</armored>
</signing>
<deploy>
<maven>
<nexus2>
<maven-central>
<active>ALWAYS</active>
<url>https://s01.oss.sonatype.org/service/local</url>
<snapshotUrl>https://s01.oss.sonatype.org/content/repositories/snapshots/</snapshotUrl>
<closeRepository>true</closeRepository>
<releaseRepository>false</releaseRepository>
<stagingRepositories>target/staging-deploy</stagingRepositories>
</maven-central>
</nexus2>
</maven>
</deploy>
</jreleaser>
jreleaser {
signing {
active = 'ALWAYS'
armored = true
}
deploy {
maven {
nexus2 {
'maven-central' {
active = 'ALWAYS'
url = 'https://s01.oss.sonatype.org/service/local'
snapshotUrl = 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
closeRepository = true
releaseRepository = false
stagingRepository('target/staging-deploy')
}
}
}
}
}
The following secrets must be configured either using environment variables or the secrets configuration file:
-
JRELEASER_GPG_PUBLIC_KEY
, unlesssigning.verify
is set tofalse
. -
JRELEASER_GPG_SECRET_KEY
-
JRELEASER_GPG_PASSPHRASE
If using the MavenCentral deployer:
-
JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME
orJRELEASER_MAVENCENTRAL_USERNAME
-
JRELEASER_MAVENCENTRAL_SONATYPE_PASSWORD
orJRELEASER_MAVENCENTRAL_PASSWORD
Alternatively, you may set these for token based authentication:
-
JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME
orJRELEASER_MAVENCENTRAL_USERNAME
-
JRELEASER_MAVENCENTRAL_SONATYPE_TOKEN
orJRELEASER_MAVENCENTRAL_TOKEN
If using the Nexus2 deployer:
-
JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME
orJRELEASER_NEXUS2_USERNAME
-
JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD
orJRELEASER_NEXUS2_PASSWORD
Alternatively, you may set these for token based authentication:
-
JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME
orJRELEASER_NEXUS2_USERNAME
-
JRELEASER_NEXUS2_MAVEN_CENTRAL_TOKEN
orJRELEASER_NEXUS2_TOKEN
Maven
The following pom.xml
file shows the minimum required configuration
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.acme</groupId>
<artifactId>app</artifactId>
<version>1.0.0</version>
<name>app</name>
<description>Sample application</description>
<url>https://github.com/aalmiray/app</url>
<inceptionYear>2021</inceptionYear>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>11</maven.compiler.release>
</properties>
<licenses>
<license>
<name>Apache-2.0</name>
<url>https://spdx.org/licenses/Apache-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>aalmiray</id>
<name>Andres Almiray</name>
</developer>
</developers>
<scm>
<connection>scm:git:https://github.com/aalmiray/app.git</connection>
<developerConnection>scm:git:https://github.com/aalmiray/app.git</developerConnection>
<url>https://github.com/aalmiray/app.git</url>
<tag>HEAD</tag>
</scm>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>org.jreleaser</groupId>
<artifactId>jreleaser-maven-plugin</artifactId>
<version>1.16.0-SNAPSHOT</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.jreleaser</groupId>
<artifactId>jreleaser-maven-plugin</artifactId>
<configuration>
<jreleaser>
<signing>
<active>ALWAYS</active>
<armored>true</armored>
</signing>
<deploy>
<maven>
<nexus2>
<maven-central>
<active>ALWAYS</active>
<url>https://s01.oss.sonatype.org/service/local</url>
<snapshotUrl>https://s01.oss.sonatype.org/content/repositories/snapshots/</snapshotUrl>
<closeRepository>true</closeRepository>
<releaseRepository>true</releaseRepository>
<stagingRepositories>target/staging-deploy</stagingRepositories>
</maven-central>
</nexus2>
<!-- Portal Publisher API
<mavenCentral>
<sonatype>
<active>ALWAYS</active>
<url>https://central.sonatype.com/api/v1/publisher</url>
<stagingRepositories>target/staging-deploy</stagingRepositories>
</sonatype>
</mavenCentral>
-->
</maven>
</deploy>
</jreleaser>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>publication</id>
<properties>
<altDeploymentRepository>local::file:./target/staging-deploy</altDeploymentRepository>
</properties>
<build>
<defaultGoal>deploy</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<attach>true</attach>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<attach>true</attach>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Store secrets in ~/.jreleaser/config.toml. The TOML format is chosen as it allows multiline strings which are required for defining both public and secret keys. You may opt instead to use the YAML format in which case be aware of indentation or use environment variables.
JRELEASER_MAVENCENTRAL_USERNAME = "<your-publisher-portal-username>"
JRELEASER_MAVENCENTRAL_PASSWORD = "<your-publisher-portal-password>"
JRELEASER_NEXUS2_USERNAME = "<your-sonatype-account-username>"
JRELEASER_NEXUS2_PASSWORD = "<your-sonatype-account-password>"
JRELEASER_GPG_PASSPHRASE = "<your-pgp-passphrase>"
JRELEASER_GPG_PUBLIC_KEY="""-----BEGIN PGP PUBLIC KEY BLOCK-----
<contents-of-your-public-key>
-----END PGP PUBLIC KEY BLOCK-----"""
JRELEASER_GPG_SECRET_KEY="""-----BEGIN PGP PRIVATE KEY BLOCK-----
<contents-of-your-private-key>
-----END PGP PRIVATE KEY BLOCK-----"""
You may export public and private keys with the following commands:
$ gpg --output public.pgp --armor --export username@email
$ gpg --output private.pgp --armor --export-secret-key username@email
If you do not wish to configure public and private keys in the secrets configuration file you may directly refer to the exported key files, in which case signing configuration should be updated to the following:
<jreleaser>
<signing>
<active>ALWAYS</active>
<armored>true</armored>
<mode>FILE</mode>
<publicKey>path/to/public.pgp</publicKey>
<secretKey>path/to/private.pgp</secretKey>
</signing>
</jreleaser>
DO NOT commit public and private key files to source control! |
Once all configuration is in place you may execute the following commands:
1) Verify release & deploy configuration
$ mvn jreleaser:config
2) Ensure a clean deployment
$ mvn clean
3) Stage all artifacts to a local directory
$ mvn -Ppublication
4) Deploy and release
$ mvn jreleaser:full-release
Gradle
The following build.gradle
file shows the minimum required configuration
plugins {
id 'java-library'
id 'maven-publish'
id 'org.jreleaser' version '1.16.0-SNAPSHOT'
}
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
maven(MavenPublication) {
groupId = 'com.acme'
artifactId = 'app'
from components.java
pom {
name = 'app'
description = 'Sample application'
url = 'https://github.com/aalmiray/app'
inceptionYear = '2021'
licenses {
license {
name = 'Apache-2.0'
url = 'https://spdx.org/licenses/Apache-2.0.html'
}
}
developers {
developer {
id = 'aalmiray'
name = 'Andres Almiray'
}
}
scm {
connection = 'scm:git:https://github.com/aalmiray/app.git'
developerConnection = 'scm:git:ssh://github.com/aalmiray/app.git'
url = 'http://github.com/aalmiray/app'
}
}
}
}
repositories {
maven {
url = layout.buildDirectory.dir('staging-deploy')
}
}
}
jreleaser {
signing {
active = 'ALWAYS'
armored = true
}
deploy {
maven {
nexus2 {
'maven-central' {
active = 'ALWAYS'
url = 'https://s01.oss.sonatype.org/service/local'
snapshotUrl = 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
closeRepository = true
releaseRepository = true
stagingRepository('build/staging-deploy')
}
}
/* Portal Publisher API
mavenCentral {
sonatype {
active = 'ALWAYS'
url = 'https://central.sonatype.com/api/v1/publisher'
stagingRepository('target/staging-deploy')
}
}
*/
}
}
}
Store secrets in ~/.jreleaser/config.toml. The TOML format is chosen as it allows multiline strings which are required for defining both public and secret keys. You may opt instead to use the YAML format in which case be aware of indentation or use environment variables.
JRELEASER_MAVENCENTRAL_USERNAME = "<your-publisher-portal-username>"
JRELEASER_MAVENCENTRAL_PASSWORD = "<your-publisher-portal-password>"
JRELEASER_NEXUS2_USERNAME = "<your-sonatype-account-username>"
JRELEASER_NEXUS2_PASSWORD = "<your-sonatype-account-password>"
JRELEASER_GPG_PASSPHRASE = "<your-pgp-passphrase>"
JRELEASER_GPG_PUBLIC_KEY="""-----BEGIN PGP PUBLIC KEY BLOCK-----
<contents-of-your-public-key>
-----END PGP PUBLIC KEY BLOCK-----"""
JRELEASER_GPG_SECRET_KEY="""-----BEGIN PGP PRIVATE KEY BLOCK-----
<contents-of-your-private-key>
-----END PGP PRIVATE KEY BLOCK-----"""
You may export public and private keys with the following commands:
$ gpg --output public.pgp --armor --export username@email
$ gpg --output private.pgp --armor --export-secret-key username@email
If you do not wish to configure public and private keys in the secrets configuration file you may directly refer to the exported key files, in which case signing configuration should be updated to the following:
jreleaser {
signing {
active = 'ALWAYS'
armored = true
mode = 'FILE'
publicKey = 'path/to/public.pgp'
secretKey = 'path/to/private.pgp'
}
}
DO NOT commit public and private key files to source control! |
Once all configuration is in place you may execute the following commands:
1) Verify release & deploy configuration
$ ./gradlew jreleaserConfig
2) Ensure a clean deployment
$ ./gradlew clean
3) Stage all artifacts to a local directory
$ ./gradlew publish
4) Deploy and release
$ ./gradlew jreleaserFullRelease