Having used continuous delivery in my professional life for years, I would never want to miss it, even with my private projects. Recently, I had the challenge of deploying an artifact to maven central. I implemented this before using Jenkins. However, there’s no way (I know of) to get a Jenkins As A Service for free for OSS projects. So it was convenient to use TravisCI.
Surprisingly, I didn’t find a turnkey solution online. So I had to come up with my own solution. Here’s how it goes:
language: java jdk: - openjdk8 script: - | cat << EOF > settings.xml <settings> <servers> <server> <id>ossrh</id> <username>\${env.OSSRH_USER}</username> <password>\${env.OSSRH_TOKEN}</password> </server> </servers> </settings> EOF - TMP_KEY="$(mktemp)" - echo "${PK_BASE64}" | base64 -d > "${TMP_KEY}" - export PGP_SECRETKEY="keyfile:${TMP_KEY}" - ./mvnw clean package deploy: skip_cleanup: true provider: script script: bash ./mvnw -Prelease -s settings.xml deploy -DskipTests on: branch: master cache: directories: - '$HOME/.m2/repository' - '$HOME/.sonar/cache'
This requires four environment variables defined in your travis-ci project and a profile in your pom.xml
:
OSSRH_USER
– Username to NexusOSSRH_TOKEN
– Access Token to NexusPGP_PASSPHRASE
in the formatliteral:PASSPHRASE
PK_BASE64
– Content of your PK, base64 encoded, create with e.g.base64 -w0 pk.asc
Details will be described in the following paragraphs.
Preface
If you haven read a guide on how to deploy to maven central, do it now. The requirements, keys, signing, etc. are not within the scope of this article.
Repository credentials / settings.xml
- The
OSSRH_USER
andOSSRH_TOKEN
are needed for authenticating against the nexus repository where the artifacts are deployed. While you could use your username and password, I recommend to create an access token, though. It could be revoked more easily. - Credentials are passed to maven via
settings.xml
. In order to make this process secure, we create asettings.xml
in thetravis.yaml
and tell it to read the actual credentials from the environment. We need to escape the$
because it is not supposed to be interpreted by the shell that writes thesettings.xml
, but by maven when reading thesettings.xml
- When calling maven, we tell it to use the local
settings.xml
via the-s
parameter.
Signing artifacts
For signing the artifacts, kohsuke’s pgp-maven-plugin (from 2014 😲) still seems the easiest way for headless use such as in CI environments.
First, we have to specify the plugin in the pom.xml
. My example pom.xml
shows how.
We then can easily pass the passphrase for the private key to the plugins using the env var: PGP_PASSPHRASE
. Note that we can’t just copy the plain passphrase, we have to tell the plugin, that’s its a literal, by using the format literal:PASSPHRASE
.
For the private key itself, there are two challenges:
- How do we get the key into travis?
- How do we get the key into maven?
How do we get the key into travis?
Unfortunately, other than Jenkins, travis does not seem to provide a way of uploading secret files. If I get the docs right, I could encrypt the private key and push it into my repo. Has anything ever felt so wrong as pushing a private key into a public repository? So I tried loading it into an env var. Here the docs tell me to “escape any Bash special characters”, which is quite a challenge for a private key. Workaround: Base64 encode the whole thing before pasting it into travis.
How do we get the key into maven?
Now that the private key is ready in an env var, it turns out that the plugin’s sole open issue (opened 2018) is that it can’t read a private key from an env var.
So, while base64 decoding the private key, it is pasted into a temporary file, which is then passed to the plugin via an env var.
Epilogue
Only 20 lines of yaml to continuously deliver artifacts to maven central, including signing and all. Not so bad, is it?
Please keep in mind that the version within your pom.xml
decides where the artifact is deployed to:
- If it ends in
-SNAPSHOT
it is deployed to the SNAPSHOT repository - Otherwise, it’s deployed to the releases repository and promoted to maven central.