Publish Maven bits to Maven Central using the Standard Deploy Plugin (1)

There are several automated options to publish artifacts that have been created in a Maven build to a Maven repository. The deploy process may depend somewhat on the repository being used. For example, if a project is not hosted by one of the big players, it might be necessary to deploy through a “proxy” mechanism that eventually forwards the artifacts to Maven Central, rather than deploying there directly.

One of these proxy services is provided by Sonatype as their “OSS Repository Hosting” or “OSSRH” service. The OSSRH service provided by Sonatype is free, and allows staging of one’s artifacts and then promoting them to Maven Central.

Sonatype provides detailed instructions on how to use the service, heavily promoting their “Nexus Staging Maven Plugin” for Maven based builds in combination with the Maven Release plugin. While there is nothing wrong with using these plugins, I find it hard to integrate into enterprise builds where the build process is a little more formalized and a well-defined branching and tagging process should be obeyed.

Following the eight steps outlined below, projects can be deployed to Maven Central using the standard Maven deploy process. The client side (i.e. the Maven build, tagging, branching, etc) can still be easily automated by employing a build system such as Jenkins, giving the build engineer tight control over how the build is performed. However, once the artifacts have been deployed, it is still necessary to “promote” them from the OSSRH Staging Area to Maven Central. While this step can be fully automated when using the Nexus Staging and the Maven Release plugins as recommended by Sonatype, I do not mind this last manual step and find it useful to be able to review the staged artifacts before they are published to Maven – in particular as releases are not that frequent. On the other hand, if a project uses OSSRH to also publish SNAPSHOT artifacts, which are commonly released with a much higher frequency than regular releases, it might be prudent to either follow a hybrid approach (using the staging plugin for SNAPSHOTS, and the regular Maven process for other releases), or to adopt a project workflow that fits in the boundaries defined by the Nexus Staging Maven Plugin.

The examples below are based on a utility library that I published through this process and commonly use as a Maven dependency. This particular project is a multi-module Maven build.

Step 1: Create a GnuPG key pair, and publish the public key

Sonatype has an excellent guide on how to generate and manage keys with GnuPG. They are very clear about the requirement that the key used to sign the artifacts cannot be a subkey. This is not very desirable from a security and key management perspective, but unfortunately necessary due to limitations of Maven and Nexus, which can only verify signatures against a primary key.

Step 2: Set up the Maven build so that it meets the OSSRH requirements

Like for the key management guide with GnuPG, Sonatype has published very detailed instructions on the requirements a project must meet so that it can be published to Maven Central through Sonatype’s OSSRH.

I noticed a few things than merit additional clarification in addition to what is mentioned in Sonatype’s guide:

  • The build must produce a source and a Javadoc package in addition to the artifact. This is tricky in case a project is a mere resource project, that is, a container for resource files. In this case, it will usually not contain Javadocs. However, as Sonatype requires Javadocs to be present, it is possible to satisfy this requirement by creating a “fake” Javadocs file. This can be done by creating a src/main/javadoc directory and adding a README.txt file. The fake Javadoc can then be included by adding the plugin below. See the “build-tools” project POM and related source file here for an example.
    <build>
      ...
      <plugins>
      ...
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <executions>
            <execution>
              <id>empty-javadoc-jar</id>
              <phase>package</phase>
              <goals>
                <goal>jar</goal>
              </goals>
              <configuration>
                <classifier>javadoc</classifier>
                <classesDirectory>${basedir}/src/main/javadoc</classesDirectory>
                <includes>
                  <include>**/*</include>
                </includes>
              </configuration>
            </execution>
          </executions>
        </plugin>
      ...
      </plugins>
      ...
    </build>
    
  • The group ID must be owned by the project. For example, a project cannot write to the google.com group ID unless it is authorized to do so. If necessary, reserve a group ID for the project when requesting access to OSSRH.
  • The parent POM of the project must contain a “developers” section, but that section does not necessarily have to contain an email address that is trivially machine readable. You may try to protect your email address against UBE, like in the example below.
        <developers>
        <developer>
          <name>Mike Beiter</name>
          <email>michael AT beiter.org</email>
          <url>https://www.michael.beiter.org</url>
        </developer>
      </developers>
    

Edit: continue reading…