Publishing to a central repository

Recently I needed to publish some Maven Java projects to a central repository. Tutorials on Google were sketchy and often referenced missing or outdated material, so I’ve decided to document my own method of publishing an open-source Maven Java project to a Central repository.

Tools & Environment

I did this on a Windows 7 developer machine, with Java 7 and Eclipse EE already installed. In addition, you will need these tools:

  • GPG4win which includes software to sign maven artifacts and manage certificates
  • GitHub for the gitshell that’s bundled with it
  • Maven in addition to any eclipse installation you might have

Open a command prompt and make sure gpg is installed

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

D:\Users\terabyte> gpg --version

The output should look like this:

gpg (GnuPG) 2.0.26 (Gpg4win 2.2.2)
libgcrypt 1.6.1
Copyright (C) 2013 Free Software Foundation, Inc.
....

Maven Central expects all artifacts to be signed before publication, so we need to create a public-key & private-key pair, keep the private-key secret and publish the public-key to the pgp repository by doing the following:

D:\Users\terabyte> gpg --gen-key

Go through each menu selecting the default options, then provide your identity:

Real name: Daniel Burrell
Email address: daniel@solong.co.uk
Comment: Release Owner

Passphrase it if you’re unable to properly secure your private keys. When you’re done you should be able to verify the existence of your keys by using

D:\Users\terabyte> gpg --list-keys
pub   2048R/E76B7011 2014-11-15
uid       [ultimate] Daniel Burrell (Release Owner) <daniel@solong.co.uk>
....

Finally publish the public key (identified by the 2nd hash) using:

D:\Users\terabyte> gpg --keyserver hkp://pgp.mit.edu --send-keys E76B7011
gpg: sending key E76B7011 to hkp server pgp.mit.edu

POM File Changes

We need to tighten up our project’s POM. Sonatype has some strict minimum requirements on what needs to be in your POM. Each chunk is shown below as an example.

License:

XPath: /project

<licenses>
    <license>
        <name>MIT License</name>
        <url>http://www.opensource.org/licenses/mit-license.php</url>
    </license>
</licenses>

Developers

XPath: /project

<developers>
    <developer>
        <name>Daniel Burrell</name>
        <email>daniel@solong.co.uk</email>
        <organization>So Long Software</organization>
        <organizationUrl>http://www.solong.co.uk</organizationUrl>
    </developer>
</developers>

Distribution Management

XPath: /project

<distributionManagement>
    <snapshotRepository>
        <id>sonatype-nexus-snapshots</id>
        <name>Sonatype Nexus snapshot repository</name>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
</distributionManagement>
Note: other tutorials may show a non snapshot repository, this is not necessary in this guide because we will be using the nexus plugins (which has all the information it needs from the snapshot data).

Release Profile containing GPG, Source, JavaDoc plugins

XPath: /project

<profiles>
    <profile>
        <id>release</id>
        <build>
            <!-- include sources -->
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>2.2.1</version>
                    <executions>
                        <execution>
                            <id>attach-sources</id>
                            <goals>
                                <goal>jar-no-fork</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <!-- include javadoc -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-javadoc-plugin</artifactId>
                    <version>2.9.1</version>
                    <executions>
                        <execution>
                            <id>attach-javadocs</id>
                            <goals>
                                <goal>jar</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <!-- Sign artifacts -->
                <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>

Publication

<build>
    <plugins>
        <!-- nexus staging -->
        <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>
        <!-- maven release -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-release-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <checkModificationExcludes>
                    <checkModificationExclude>pom.xml</checkModificationExclude>
                </checkModificationExcludes>
                <autoVersionSubmodules>true</autoVersionSubmodules>
                <useReleaseProfile>false</useReleaseProfile>
                <releaseProfiles>release</releaseProfiles>
                <goals>deploy</goals>
            </configuration>
        </plugin>
    </plugins>
</build>

SCM

<scm>
    <url>https://github.com/danielburrell/hatf2-pojo</url>
    <connection>scm:git:git://github.com/danielburrell/hatf2-pojo.git</connection>
    <developerConnection>scm:git:git@github.com:danielburrell/hatf2-pojo.git</developerConnection>
    <tag>HEAD</tag>
</scm>
<url>www.solong.co.uk/hatf2</url>
<description>A collection of classes which allow for Object based manipulation of data responses from Steam API's TF2 Backpack/Schema responses.</description>

Settings File

Edit your Maven settings file so it looks like this, substituting your Sonatype username/password as necessary: (find it on disk, usually somewhere like D:\Users\username\.m2\settings.xml)

<settings>
  <servers>
    <server>
      <id>ossrh</id>
      <username>sonatypeusername</username>
      <password>sonatypepassword</password>
    </server>
  </servers>
</settings>

Optional gpg passphrase

Optionally you can make a further modification to the settings.xml file so that it provides the passphrase for gpg signing without prompting you, in which case, the final state of the file should look like this (with substitutions)

<settings>
  <servers>
    <server>
      <id>ossrh</id>
      <username>sonatypeusername</username>
      <password>sonatypepassword</password>
    </server>
  </servers>
  <profiles>
    <profile>
      <id>ossrh</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <gpg.executable>gpg2</gpg.executable>
        <gpg.passphrase>the_pass_phrase_of_your_gpg_private_key</gpg.passphrase>
      </properties>
    </profile>
  </profiles>
</settings>

In order to publish to sonatype, we need to have a repository setup. Go to Sonatype’s Jira instance, and raise a Jira with these fields set:

  • Project: Community Support - Open Source Project Repository Hosting
  • Issue Type: New Project
  • Summary: Summary description of your project
  • Description: I just copy and paste the Summary
  • Group Id: (Your maven’s group ID, e.g. uk.co.solong)
  • Project URL: (I use Github http page URL) e.g. https://github.com/danielburrell/myproject
  • SCM URL: (I use Github scm URL) e.g. https://github.com/danielburrell/myproject.git
  • Username(s) leave blank
  • Already synced to Central: NO
  • Sprint: None

After a day or so, somebody will set up your repository.

Ready to publish?

You are ready to publish if the following things are true:

  • You’ve added the XML bits shown above.
  • Somebody from sonatype has setup your repository and sucessfully closed out your Jira.
  • You’ve installed a copy of Maven (external - eclipse one is not be enough), GPG,
  • You’ve created a GPG file.
  • You’ve created the local maven settings file, and referenced it from eclipse.
  • The version shown in your pom file is a snapshot version (i.e.) X.Y.Z-SNAPSHOT
  • You’ve committed your code

Then do this:

  • Open an instance of the Git Shell (regular command prompts/power shells don’t work because they don’t have the git command on the path).
  • CD into the root of the project’s directory (e.g. D:\workspace\myproject)
  • Run the following commands (ensuring each command succeeds):
mvn clean deploy
mvn release:clean release:prepare
mvn release:perform

All done.

To confirm everything was deployed, visit https://oss.sonatype.org/#nexus-search;quick~ and check that your aritfact (in solid release state) is available.

Protip

  • You only have to request the nexus repository be setup once for your organization/project, i.e. you publish multiple projects to the same nexus area provided they all keep the same group name.
  • If you need to publish from another machine, you can export your private key using the Kleopatra tool which is bundled with Gpg4win.
  • Select your private key
  • Right-click -> export secret keys. Ensure the asc box is ticked to get an ascii file.
  • Securely transport this file to your other developer machine.
  • Import the private key file using Kleopatra
.

Read more... 👓