How to do Maven release builds from DEV@cloud to central
The holy grail of Jenkins jobs building Maven projects is the push button release build. If you want to do this on DEV@cloud, this post will let you in on all the tricks that you need to follow to make it all happen.
Keeping things secret
When cutting release builds from any CI server there are some things that you need to put on the CI server.
In order to push releases to central you will need to have your artifacts signed with a GPG key, thus you will need to give your CI server the GPG private key and the passphrase for the GPG private key. If you are not happy giving your CI server a GPG key that Sonatype have configured oss.sonatype.org to accept, then stick to cutting release builds from your desktop.
In order to push releases to central you will need to give Maven a username and password for pushing the artifacts to oss.sonatype.org. Again if you are not happy giving your CI server these details then stick to cutting release builds from your desktop.
We don’t want people to be able to see these files via the Jenkins UI, so we will store these secrets on your CloudBees /private WebDAV share (and we will cringe at the CloudBees UI that calls these WebDAV shares “Maven repositories” when we all know they are no such thing… but that is another story… they are just perfect for what we need).
So connect to your /private WebDAV share and create a folder to hold a settings.xml file and a .gnupg directory.
Here is one I created for anyone following along.
The release settings
The settings.xml is going to be somewhat bare bones:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
cloudbees-oss-snapshots
********
********
cloudbees-oss-staging
********
********
cloudbees-oss-release
4A2F92BB
********
false
true
never
/private/cloudbees/central-release/.gnupg
/private/cloudbees/central-release/.gnupg/pubring.gpg
/private/cloudbees/central-release/.gnupg/secring.gpg
Now obviously I have masked all the passwords. I didn’t mask our gpg keyname as anyone checking our OSS releases on central will be verifying that the GPG key has that hex signature anyway and more importantly to illustrate that it is the hex signature of the key that is required and not some other human readable key name that you may have assigned to the key.
It is important that you specify a lockMode of never as the /private WebDAV share is mounted read only on the DEV@cloud slaves. The mount point path will be /private/{your DEV@cloud account name} / In our case the account name we are using for this example is cloudbees .
Note:
When you have developers working on different OSS projects, they will thank you if you use a unique name for your server IDs and your release profile. In our case we are using two server IDs: cloudbees-oss-snapshots and cloudbees-oss-staging and our release profile is called cloudbees-oss-release. The reason for doing this is so that if I have to cut a release from my laptop for some reason or other I can have everything setup in a settings.xml that also includes profiles for our own internal closed source release, Jenkins plugin releases, Apache Maven releases as well as other OSS projects that I work on. I thus can have one single settings.xml that I just use all the time. If we used the default server ids and release profile id configured by Sonatype’s oss-parent pom then I would have to keep swapping settings.xml files to cut releases of different projects… which is a nighmare!
The (parent) pom
Now the next step is to make sure that our pom.xml file is setup right for a release. Here is the oss parent pom that we use:
4.0.0
com.cloudbees
cloudbees-oss-parent
pom
4
...
2.2.1
...
cloudbees-oss-snapshots
Sonatype Nexus Snapshots
${sonatypeOssDistMgmtSnapshotsUrl}
cloudbees-oss-staging
Nexus Release Repository
https://oss.sonatype.org/service/local/staging/deploy/maven2/
cloudbees-oss-snapshots
Sonatype Nexus Snapshots
https://oss.sonatype.org/content/repositories/snapshots
false
true
UTF-8
UTF-8
UTF-8
https://oss.sonatype.org/content/repositories/snapshots/
git
maven-enforcer-plugin
enforce-maven
enforce
(,2.1.0),(2.1.0,2.2.0),(2.2.0,)
Maven 2.1.0 and 2.2.0 produce incorrect GPG signatures and checksums respectively.
(,3.0),[3.0.4,)
Maven 3.0 through 3.0.3 inclusive do not pass correct settings.xml to Maven Release Plugin.
...
maven-enforcer-plugin
1.3.1
maven-gpg-plugin
1.5
...
maven-release-plugin
2.4.2
forked-path
false
${arguments} -Pcloudbees-oss-release
${git.provider}
org.apache.maven.scm
maven-scm-provider-jgit
1.9
org.apache.maven.scm
maven-scm-provider-gitexe
1.9
...
cloudbees-oss-release
maven-source-plugin
attach-sources
jar-no-fork
maven-javadoc-plugin
attach-javadocs
jar
maven-gpg-plugin
sign-artifacts
verify
sign
Most of this is just lifted from Sonatype’s oss-parent with a change of server IDs and the release profile name. I also added an extra enforcer rule to ensure that we have a version of Maven that will allow us to pass the effective settings.xml through to the forked Maven process that the release plugin invokes to do the release work.
The Jenkins job
First off. Please do not do release builds with a Maven project type job. The Jenkins Maven project type does not and cannot instrument the forked Maven process so all the integrations that it provides will either be capturing the wrong artifacts / test results or no artifacts / test results. In short, you are better off with a FreeStyle project with a Maven build step (or a literate style project).
The key part of the job configuration is as follows:
You need to ensure that the private WebDAV share is mounted.
In our case our source code for this project is hosted on GitHub, so we use the SSH Agent plugin to ensure that the Maven release plugin has a SSH key that can push to the GitHub project.
Finally we ensure that the settings.xml file is the one from the private WebDAV share.
Note: if you really insist on using the Maven job type, you can use an analogous configuration… it will however add a layer of complexity to your build.
Now try a build
At this point you should have your Jenkins job ready to go… so why not try a build...
Hopefully you too will get the final Finished: SUCCESS of such a build.
Extra finesses
There are some finesses you can add:
- Make the build parameterised so that you don’t cut a release by accident
- Use Sonatype’s staging plugins to close the staging repository at the end of the release
Summary
It’s not too hard when you know what to do. Most of the tricks are around ensuring that you have the correct versions of the GPG and Release plugins so that your custom settings.xml can be used and the GNUPGHOME can function when on a read-only filesystem…
Hopefully this post has made your quest a lot easier to complete
- Stephen Connolly