In this blog post I will describe how we set up automatic deployment of our rabbitmq-client library to Sonatype’s Nexus Repository Manager.
For a refresher on what Sonatype Nexus Repository Manager is, check out this excellent post.
Some recommended reading
First and foremost, Alex Nedelcu’s excellent post Automatic Releases to Maven Central with Travis and SBT which was the source for most of the information described in this post.
I also found these two Sonatype articles very helpful:
– OSSRH Guide
– Releasing the Deployment
Overview of our CI pipeline to Sonatype Nexus
We use Travis-ci to power the continuous delivery pipeline of the rabbitmq-client
repo.
A .travis.yml
file, located in the root folder of the project, describes a sequence of build steps that comprise this project’s pipeline. For example code instrumentation, static-analysis, and code coverage report generation.
The final line of the .travis.yml
file reads as follows:
after_success: - ./project/publish
The file ./project/publish
is a ruby script which I copied from the Monix/shade project.
When this script is called from within sbt, the newly-built jar file is signed and uploaded to Sonatype.
It took much trial and error before I successfully completed this step; read on to learn more.
The 30,000 foot view of deploying to Sonatype Nexus
This section is a brief overview. The following section will discuss each step in more detail.
To deploy artifacts to Sonatype, you will need:
- a Sonatype account (see the following section)
- a maven
groupId
for your project.
Your maven groupId
must be requested by creating a new Jira issue.
Travis-ci will use the above information to publish newly built artifacts on your behalf.
Sonatype Nexus requires that new artifacts are cryptographically signed.
To fulfil this requirement a new public/private key pair must be created. Your new public/private key pair will be stored in your github project. The private key will be encrypted.
The build.sbt
file must be updated with values that sbt will use to successfully deploy the jar file to Sonatype.
Finally, the Sonatype username, Sonatype password, and PGP passphrase of the private key will be stored in Travis-ci as environment variables.
The details of deploying to Sonatype Nexus
Creating a Sonatype Account
A Sonatype Account is created when you first sign up with Sonatype’s Jira page. You may then log into Sonatype’s Nexus Respository Manager console using the same username and password as your Jira account.
Request a repository groupId for your project
Navigate to https://issues.sonatype.org and create a new user account which you will use to request a new maven groupId
for your project.
Open a new Jira issue requesting a new groupId
for your project. As an example, this is the Jira issue we opened to request a groupId for the rabbitmq-client project.
Create a public/private key pair
As already mentioned, Sonatype Nexus requires that artifacts are cryptographically signed. To fulfil this requirement a new public/private key pair must be created.
I happen to use an Apple MacBook so I chose to use an OSX application called “GPG Keychain” to generate a new public/private key pair.
The new key is visible from within the terminal:
$ gpg --list-secret-keys /Users/xxxxx/.gnupg/pubring.kbx ----------------------------------- sec rsa4096 2017-11-08 [SC] 4BEF11849D8638711107EB75B76CCB046AAA0BF2 uid [ unknown] PaddyPowerBetfair (Used to sign artifacts which are uploaded to central.sonartype.org) ssb rsa4096 2017-11-08 [E]
The public and private keys must be exported to separate keyrings and then stored in the github project.
$ gpg -a --export 4BEF11849D8638711107EB75B76CCB046AAA0BF2 > my-key.asc $ gpg --no-default-keyring --primary-keyring pubring.gpg --keyring pubring.gpg --fingerprint --import ./my-key.asc $ gpg --export-secret-keys > secring.gpg $ cd /path/to/github/project/.gnupg $ cp pubring.gpg /path/to/github/project/.gnupg/ $ cp secring.gpg /path/to/github/project/.gnupg/
Update build.sbt
Update your build.sbt
file with the following:
useGpg := false usePgpKeyHex("4BEF11849D8638711107EB75B76CCB046AAA0BF2") pgpPublicRing := baseDirectory.value / "project" / ".gnupg" / "pubring.gpg" pgpSecretRing := baseDirectory.value / "project" / ".gnupg" / "secring.gpg" pgpPassphrase := sys.env.get("PGP_PASSWORD").map(_.toArray) credentials += Credentials( "Sonatype Nexus Repository Manager", "oss.sonatype.org", sys.env.getOrElse("SONATYPE_USER", ""), sys.env.getOrElse("SONATYPE_PASSWORD", "") ) isSnapshot := version.value endsWith "SNAPSHOT" publishTo := Some( if (isSnapshot.value) Opts.resolver.sonatypeSnapshots else Opts.resolver.sonatypeStaging )
Add the following to project/plugins.sbt:
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.0")
Add environment variables to your Travis-ci project settings.
Log into Travis CI, navigate to your project’s home page and click “More Options | Settings”.
Scroll down to environment variables and add the following variable names and their corresponding values: SONATYPE_USERNAME
, SONATYPE_PASSWORD
and PGP_PASSWORD
.
And you’re done …
At this point all the machinery is in place for you to test automatic deployment of your project to Sonatype Nexus.
Let’s test it
The scripts we have added in the preceding steps, work as follows:
Publishing snapshots
If the git branch is called “snapshot” and the value of version in the build.sbt file ends with “SNAPSHOT” (e.g. 1.0.1-SNAPSHOT) then the following command, when executed in a local terminal, should publish your library to the snapshot repository in Sonatype Nexus.
$ PGP_PASS="" sbt publishSigned
You may confirm this by navigating to your equivalent of this url: https://oss.sonatype.org/content/repositories/snapshots/com/paddypowerbetfair/rabbitmq-client_2.12/.
Publishing releases
If the git branch is called something like “v1.0.1” and the value of version in the build.sbt
file is 1.0.1
then the artifact will be uploaded to the equivalent of https://oss.sonatype.org/content/repositories/releases/com/paddypowerbetfair/rabbitmq-client_2.12/1.0.1.
According to the OSSRH Guide it may take two hours before your library is searchable in the Central Repository or mvnrepository.com.
Conclusion
With this pipeline in place, new releases of our open source libaries are a trivial matter. It only requires that the git branch is correctly named (e.g. v1.0.2
or SNAPSHOT
).
I would strongly recommend reading the articles that are linked above in the Recommended Reading section. Without them I would not have known where to begin.