Monday, May 18, 2015

How To Publish Software Artifacts To Central Repository

I have had to release software artifacts to the Central Repository, which used to be mainly referred to as Maven Central a couple of times now, and each time I have found myself fumbling at one step or another in the process, and had to resort to Googling to sort things out. This post is basically a compilation of all my “groping in the dark”, because unfortunately the process of moving the piece of software you have written to having it available for the whole world to download from the Central Repository is not yet a matter of just pushing a button; or running a simple command.

So I write this post to serve as a reference for myself and for anyone else out there that might find it useful: a place to document the necessary steps needed to publish Software artifacts to Central Repository; while iterating the recurrent hurdles that I kept on encountering in the process of getting the publishing to Central Repository successful. I have also included a couple of useful links at the end of the post.

A rundown of the steps involved can be roughly summarized to be:
  1. Create an account on oss.sonatype.org
  2. Create and share a PGP signature.
  3. Update settings.xml and pom.xml appropriately.
  4. Upload your artifact to oss.sonatype.org.
  5. Promote the release.


Before proceeding with the steps, I would like to highlight some of the principal entities that come to play in the process. Sonatype, Nexus, OSSHR...as there is that small possibility that these entities might not be known...So I will quickly touch upon on what they represent, and how they relate to our mission of moving software to Central Repository.

So here we go:

Sonatype is a company that makes and sells software used to host and manage artifacts. These kinds of software are referred to as “repository managers”. The repository manager made and sold by Sonatype is Nexus.

Apart from making and selling Nexus, Sonatype also has a community initiative that provides a free instance of the Nexus software (the Professional edition) for hosting and managing Open Source Software. This initiative is called OSSHR (Opensource Software Hosting Repository). So, next time you come by OSSHR, you now know what it means. You can access the hosted Nexus here https://oss.sonatype.org/

Sonatype also provides hosting for Opensource sofware via http://central.sonatype.org/ - this is the Central Repository. For instance if you go to http://central.maven.org, and click on the "About Link", you would be redirected to http://central.sonatype.org/. Simply put, our access to the Central Repository is courtesy of the good people at Sonatype.

To move your software to Central Repository therefore involves first moving it to the instance of Nexus provided by Sonatype, from where, you can then choose to delete the artifact, or proceed to making it available on Central Repository by publishing it.

With this bit of introductory information provided, let us now go over the individual steps involved:

1. Create an account on oss.sonatype.org

The process of creating an account on oss.sonatype.org is, unfortunately, not automatic and self driven. You would have to go through a staff at Sonatype to get this done. Sonatype uses Jira to manage the requests for account creation, so to get your account you would have to create a project issue here. Well, it also means you would have to create an account on Jira first. The maximum time they promise you would have to wait before your account is created is 2 business days. I got mine created though under a couple of hours. And if you are wondering why you have to go through this process with the chance of waiting 2 days before an account is created? Sonatype explains why here.

2. Generate and share a PGP signature

You would have to generate a PGP signature that would be used to sign the artifacts you want to release. Artifacts need to be signed so as to provide assurance to people consuming them, a form of guarantee that they have not been tampered with in any way.

This link contains necessary information on how to generate and share a PGP signature.

One  hurdle while generating the keys was the:
"gpg - not enough random bytes available, please do some other work to give the OS a chance to collect more"
message I encountered in the process.

There are a couple of suggestions to sorting this out. The one that finally sped up the process for me was running

"ls / -R"

in another terminal while the key generation was going on. To get the level of speed I wanted, I had to run the command in 3 different terminals at the same time.

After the key is generated, the next steps would be to package your source code, have it signed, and then have it uploaded to oss.sonatype.org. Achieving this requires that you update your configuration in a couple of places. I’ll quickly walk you through these configuration requirement in the next step.

On Mac machines, while building for release you might encounter a build failure with error similar to gpg-agent[94002]: command get_passphrase failed: Inappropriate ioctl for device
. To fix this, install pinentry-mac. Check this issue on Github for more details

3. Update settings.xml and pom.xml appropriately

Various parts of the POM.xml needs to be updated in other to be able to upload to Central Repository.

Most of the updates needed would be plugin configurations. And this is because, in Maven land, plugins run the world, literally. Maven itself is nothing but a simple but powerful engine at its core with a host of plugins that enables it to achieve various tasks; even the default tasks like compiling source codes. Thus to have Maven be able to take care of the whole process of packaging and uploading artifacts, we need to provide it with certain configurations, mostly plugin configurations.

A quick run down of the updates needed to be made to the POM follows:

POM.xml Configurations
1. Your github information (or whichever SCM you use): so it can be in sync with the release. Creating tags, releases, etc.
2. Add plugin configuration for signing the artifacts with the signature generated in the previous step. The plugin for that is maven-gpg-plugin
3. Add plugin configuration for attaching Javadoc to the artifact. The plugin for that is maven-javadoc-plugin
4. Add plugin configuration for attaching source to the artifact. The plugin for that is maven-source-plugin
5. Add distribution information: so maven knows the repository manager to attempt to upload your artifacts to. This is done with the <distributionManagement/>
6. Add the plugin configuration for packaging your artifact and uploading/staging it.

There are two plugins I know that can help with staging your artifacts. The Maven Release Plugin and Nexus Staging Maven Plugin. The configuration for the two would look thus:

Maven Release Plugin:
<build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-release-plugin</artifactId>
    <version>${plugin.version}</version>
    <configuration>
    <mavenExecutorId>forked-path</mavenExecutorId>
    <useReleaseProfile>false</useReleaseProfile>
    <arguments>-Psonatype-oss-release</arguments>
    </configuration>
    </plugin>
    ...
  </plugins>
</build>

Consult the Maven Release plugin page for more information on the configuration options

Nexus Staging Maven plugin:
<build>
  <plugins>
    <plugin>
      <groupId>org.sonatype.plugins</groupId>
      <artifactId>nexus-staging-maven-plugin</artifactId>
      <version>${plugin.version}</version>
      <extensions>true</extensions>
      <configuration>
        <serverId>ossrh</serverId>
        <nexusUrl>https://oss.sonatype.org/</nexusUrl>
        <autoReleaseAfterClose>true</autoReleaseAfterClose>
      </configuration>
    </plugin>
    ...
  </plugins>
</build>

The configuration for the <distributionManagement/> would slightly differ depending on if you are using the Maven Release Plugin or Nexus Staging Maven Plugin.

When using Nexus Maven Plugin, you have:
<distributionManagement>
  <snapshotRepository>
    <id>ossrh</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  </snapshotRepository>
</distributionManagement>

When using Maven Release Plugin, you have a slightly verbose configuration:
<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>

That is, you have to provide the configuration seperately for the snapshot and main release.

I have only used the maven release plugin. The Nexus Maven plugin would be a more straightforward option though, as it was created specifically for the use case of uploading artifact to the Nexus repository manager at oss.sonatype.org. Baeldung has a tutorial on how to use the plugin here

Settings.xml Configurations
Uploading artifacts from your machine to oss.sonatype.org means that you have to provide authentication information so your Maven can authenticate you against the running nexus before uploading your stuff to it. In maven, to do this, you provide authentication information in your settings.xml.

So for example, find the settings.xml your installed maven is using and include the following snippets:

<servers>
<server>
      <id>ossrh</id>
      <username>your-username-here</username>
      <password>your-password-here</password>
</server>
</servers>

With your username and password being the same ones you used to create the Jira issue. To confirm your credentials are right, you can try using them to login at http://oss.sonatype.org

It should be noted that some instructions might have the server settings split into two. One for the snapshot releases and another for main releases with the settings.xml having two <server/> sections with the <id/> as <id>sonatype-nexus-snapshots</id> and <id>sonatype-nexus-staging</id>. This is outdated and no longer the requirement. With one single <id/> section set to <id>ossrh</id>, you are good to go.

A complete rundown of all the configurations and steps to perform the release are articulated in the article here.

As can be seen, the necessary plugin configurations and settings needed to be made to the POM could be an handful. A recommended way to manage this, is to create a "parent-pom" which contains all the boiler-plate configurations which individual projects can then inherit, with the option of modifying any settings, if need be, in the project POM.

An example of such a "parent-pom" is the Progressive Organization POM
Note, You may run (or might have already ran) into another such parent-pom called the OSS parent POM.It should be noted that the OSS parent POM is no longer being maintained by Sonatype. So it is not an advisable option. Either you create your own "parent-pom" or take inspiration from the Progressing Organization POM.

The next thing to do is to upload to the remote repository manager.

4. Uploading to oss.sonatype.org

The procedure for this step depends on the method you use to perform the release. The steps needed are different if you choose to use the Maven release plugin compared to choosing to use the Nexus maven plugin. You would find information about the various steps in the respective instructions for these plugins, but the idea is the same. Move your artifacts from your computer to the hosted Nexus artifact repository at oss.sonartype.org

Another hurdle I ran into a couple of times during the process of uploading artifacts to the repository was the “401, ReasonPhrase: Unauthorized” exception. Basically this:
Exceptions
Failed to transfer file: https://oss.sonatype.org/content/repositories/snapshots/com/path/to/artifact/0.1.2-SNAPSHOT/artifact-0.1.2-20150516.100056-1.jar. Return code is: 401, ReasonPhrase: Unauthorized. -> [Help 1]
This simply means the remote repository can’t authenticate rightly with the credentials you provided in your settings.xml. If you get this, make sure the credentials you entered in settings.xml are correct and you can use it to login manually. Also make sure the strings within </id> are also correct.  They must be ossrh.

A useful way to confirm that you have the settings right and that Maven is using the settings you provided is to run the following command:

mvn help:effective-settings

This would display the actual settings being used by Maven. Running this command bailed me out a couple of times and made it easier for me to spot little typos in configurations.

After an artifact is pushed to the remote repository, the next thing to do is to promote the release. Which is what the next step is about.

5. Promoting The Release

To promote the release; which would publish your artifact to Central Repository, you would have to login to the hosted Nexus at oss.sonatype.org, find the artifact you uploaded and click the necessary buttons to initiate the release. The steps required can be found in the Releasing the Deployment article.

Promoting the release, marks the end of the steps required to get your software artifact on Central Repository. It might take a couple of hours for the synchronization to be complete and for your artifacts to be found via search.maven.org. But at this point, there is nothing left to be done.

Glossary of Links

The official OSSRH guide: http://central.sonatype.org/pages/ossrh-guide.html
The configuration and Release steps using Maven: http://central.sonatype.org/pages/apache-maven.html
The configuration and release steps using Gradle: http://central.sonatype.org/pages/gradle.html
The Manual Staging and Release steps: http://central.sonatype.org/pages/manual-staging-bundle-creation-and-deployment.html
Working with PGP Signatures - Generating and sharing: http://central.sonatype.org/pages/working-with-pgp-signatures.html
Using Nexus Maven Plugin: http://www.baeldung.com/maven-deploy-nexus
Pushing to Central Repository With Sonatype - blogpost by Sean Laurent: http://bealetech.com/blog/2013/04/10/pushing-to-maven-central-with-sonatype/
How to Release to Central Repository, in One Click: http://www.yegor256.com/2014/08/19/how-to-release-to-maven-central.html

No comments: