Publishing to Maven Central using OSSRH

I had a period where I thought I might not be good enough to publish.

Stephen King

This article summarizes the set of steps which should be completed to publish a package to Maven Central. It uses a combination of the maven-release and nexus-staging-maven plugins to accomplish this. A good example of the end result can be found in the dwolla-java respository on Github.

Distributing your Public Key

Artifacts need to be GPG signed. Generate a key if you have not already done so.

Send your public key to a keyserver so other people can use it to verify your files. To do this you will need to determine your key-id. These ids found on the left-hand-side of the output printed out below directly to the right of any sub instances:

$ gpg --list-keys
/Users/coreyoliver/.gnupg/pubring.gpg
-------------------------------------
pub   2048R/AAAAAAAA 2014-01-10
uid                  Corey Oliver   <corey.jon.oliver@gmail.com>
sub   2048R/BBBBBBBB 2014-01-10

pub   2048R/CCCCCCCC 2014-11-24
uid                  Corey Oliver <corey@dwolla.com>
sub   2048R/DDDDDDDD 2014-11-24

For example, if you wanted to send the key associated with corey@dwolla.com, you would run:

$ gpg --keyserver pool.sks-keyservers.net --send-keys CCCCCCCC

Optional GPG Configuration

If you have more than one GPG key pair, you can specify which one the maven-release plugin will use by editing your settings.xml file. Add the following:

<settings>  
  ...
  <profiles>
    <profile>
      <id>project-name-release</id>
      <properties>
        <gpg.keyname>CCCCCCCC</gpg.keyname>
      </properties>
    </profile>
  </profiles>
  ...
</settings>  

Where project-name is the name of your project and CCCCCCCC is your GPG key-id.

Configuring OSSRH Credentials

Configure your settings.xml file to have the appropriate Sonatype OSSRH credentials. Specifically, this is your OSSRH Jira account. For instructions on creating a Jira account, refer to the to the OSSRH Guide.

<settings>  
  ...
  <servers>
    <server>
      <id>ossrh</id>
      <username>jira-id</username>
      <password>jira-password</password>
    </server>
  </servers>
  ...
</settings>  

For security reasons, you will likely wish to encrypt your Jira password.

Editing the POM

Now it is time to edit the pom.xml file for your project.

Add the distributionManagement element. Note the id specified for the following repositories must match the id of the server given in your settings.xml file.

<distributionManagement>  
  <snapshotRepository>
    <id>ossrh</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  </snapshotRepository>
  <repository>
    <id>ossrh</id>
    <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
  </repository>
</distributionManagement>  

Add the maven-release plugin to the pluginManagement element. The maven-release plugin is used to automatically generate what is often mistake ridden commits and tags associated with a packages release.

Note the following customizations:

  1. pushChanges is set to false. This means maven-release is not configured to automatically push up commits or tags it creates to the remote. This avoids potential issues that arise when the release fails, and having to manually fix upstream history.
  2. localCheckout is set to true. Meaning a local checkout is used instead of doing one from the upstream repository. This is required because automatic pushing is turned off.
  3. arguments is set to -Pproject-name-release. This activates the project-name-release profile when the maven-release plugin is used.
<pluginManagement>  
  <plugins>
    ...
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-release-plugin</artifactId>
      <version>2.5</version>
      <configuration>
        <useReleaseProfile>false</useReleaseProfile>
        <arguments>-Pproject-name-release</arguments>
        <pushChanges>false</pushChanges>
        <localCheckout>true</localCheckout>
        <goals>deploy</goals>
      </configuration>
    </plugin>
    ...
  </plugins>
</pluginManagement>  

Where project-name is the name of your project.

Next, add the nexus-staging-maven plugin which is used to control Nexus Staging workflow.

<plugin>  
  <groupId>org.sonatype.plugins</groupId>
  <artifactId>nexus-staging-maven-plugin</artifactId>
  <version>1.6.3</version>
  <extensions>true</extensions>
  <configuration>
    <serverId>ossrh</serverId>
    <nexusUrl>https://oss.sonatype.org/</nexusUrl>
    <autoReleaseAfterClose>true</autoReleaseAfterClose>
  </configuration>
</plugin>  

Finally, add the project-name-release profile for signing with your GPG key.

<profiles>  
  <profile>
    <id>project-name-release</id>
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-gpg-plugin</artifactId>
          <version>1.5</version>
          <executions>
            <execution>
              <id>sign-artifacts</id>
              <phase>verify</phase>
              <goals>
                <goal>sign</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>  

Publishing a Snapshot

To publish a snapshot of your project run:

$ mvn clean deploy

Performing a Release

Complete the following to perform a release:

$ mvn release:clean release:prepare

If for any reason prepare fails, run:

$ mvn release:rollback

Finally:

$ mvn release:perform

Push up the tag and commits generated by the maven-release plugin:

$ git push origin master && git push origin <tag-name>

Appendix

Generating a GPG Key Pair

Install GPG and execute the following command:

$ gpg --gen-key

Select the default value when asked for the type, then size, and the time of validity for the key. Provide additional information as appropriate including a passphrase.

Though not required, it is wise to backup up the generated keys and passphrase in a password manager.

Backing up GPG Keys

To export the keys associated with corey@dwolla.com you would run:

$ gpg --export -a "corey@dwolla.com" > public.key
$ gpg --export-secret-key -a "corey@dwolla.com" > private.key

The corresponding commands to import the keys would be:

$ gpg --import public.key
$ gpg --allow-secret-key-import --import private.key

Encrypting Passwords for Maven

Create a master password by running:

$ mvn -emp master-password
{S/Fcg0gizpcHCeYMdAapSKtccedKM6mYifesMj9sSNI=}

Where master-password is your master password.

A encrypted copy of the password will be printed out. Copy this to !/.m2/settings-security.xml.

<settingsSecurity>  
   <master{S/Fcg0gizpcHCeYMdAapSKtccedKM6mYifesMj9sSNI=}</master>
</settingsSecurity>  

Now, run the following to encrypt your particular password:

$ mvn -ep password
{sZHbnzYHUkEHvw393pIextAJpZ6wdcN471Hfn2Xdx5M=}

This will be the value that you provide in the desired password element.