Luca Arrasz
24.01.2018Using Travis CI to deploy to Maven repositories and GitHub Releases
This post outlines the steps needed to simultaneously deploy to Maven repositories and to GitHub Releases. Every time a tagged commit is pushed, a Travis CI build will be triggered automatically and start the release process. This blog post uses Sonatype Nexus as an example for a Maven repository manager.
Preparing GitHub Releases
Sergey Mashkov has written a Maven plugin that allows us to create
a new release on our project’s releases page and upload our build artifacts to a release. The following sections
describe how we need to configure our pom.xml
in order to use this plugin.
The plugin uses the scm
settings to find out for which project the new release should be created. Right now, there’s
still a bug in the plugin which restricts the format for our git URIs. The only working format is
scm:git:git@github.com:...
. Neither scm:git:https://github.com/...
nor scm:git:ssh://git@github.com/...
work, but
a pull request has been created that adds this
functionality.
So add an scm
section to your pom that looks like this:
<scm>
<url>https://github.com/example/project</url>
<connection>scm:git:git@github.com:example/project.git</connection>
<developerConnection>scm:git:git@github.com:example/project.git</developerConnection>
</scm>
The second step is to include the plugin in our pom. Right now the plugin is only available
from bintray.com so we need to add it as a plugin repository. We only want to
create a new release on GitHub when we are building a new release. Hence we configure the plugin in an extra release
profile section. This leads to the plugin being executed only if Maven is started with -Prelease
and only if the deploy
goal is invoked. For more information on how to configure the plugin options please refer to
its documentation and
the pitfalls below.
<profiles>
...
<profile>
<id>release</id>
<pluginRepositories>
<pluginRepository>
<id>bintray-cy6ergn0m-maven</id>
<name>bintray-plugins</name>
<url>http://dl.bintray.com/cy6ergn0m/maven</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>cy.github</groupId>
<artifactId>github-release-plugin</artifactId>
<version>0.5.1</version>
<configuration>
<tagName>${project.version}</tagName>
<releaseTitle>${project.artifactId}-${project.version}</releaseTitle>
<serverId>github</serverId>
</configuration>
<executions>
<execution>
<goals>
<goal>gh-upload</goal>
</goals>
<phase>deploy</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Preparing for Maven releases
If you don’t already have a repository where you want to deploy to, you need to create a release and a snapshot
repository and add them to distributionManagement
.You might also want to create a separate user that has access only
to your target repositories. This user will be used to upload the releases.
<distributionManagement>
<repository>
<id>oss</id>
<url>https://nexus.example.com/content/repositories/oss-releases</url>
</repository>
<snapshotRepository>
<id>oss</id>
<url>https://nexus.example.com/content/repositories/oss-snapshots</url>
</snapshotRepository>
</distributionManagement>
Putting it all together
So far we have configured the GitHub release plugin to deploy our artifacts to the GitHub Releases page and setup Maven
releases. Now it’s time to glue the parts together. In order to do this we have to create a settings.xml
for use with
Maven, a .travis.yml
that manages our Travis CI builds and we have to configure some environment variables in Travis
CI itself. Furthermore we need a small shell script that orchestrates our release.
Maven settings
Create a new settings.xml
file in your repository, e.g in a .travis/
directory. The content of this file should look
like the following snippet. The <server>
ids have to match the ids in <distributionManagement>
and the <serverId>
of
the GitHub release plugin exactly. Do not use static credentials here! You don’t want everyone who stumbles upon your
repository on GitHub to have write access to your Nexus/Artifactory and GitHub. We will use Travis CI’s capability to
inject environment variables into builds;
the environment variables
will be configured soon.
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
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">
<servers>
<server>
<id>oss</id>
<username>${env.NEXUS_USERNAME}</username>
<password>${env.NEXUS_PASSWORD}</password>
</server>
<server>
<id>github</id>
<username>${env.GITHUB_USERNAME}</username>
<password>${env.GITHUB_TOKEN}</password>
</server>
</servers>
</settings>
Release script
We need a small shell script that orchestrates our releases. This script sets the correct version, creates a release and
uploads it to our Maven repository and to GitHub. To release the correct version theTRAVIS_TAG
environment variable
will be used. Travis CI uses this variable to inject the value of the git tag into the build.
#!/usr/bin/env bash
set -e
echo "Ensuring that pom matches $TRAVIS_TAG"
./mvnw org.codehaus.mojo:versions-maven-plugin:2.5:set -DnewVersion=$TRAVIS_TAG
echo "Uploading to oss repo and GitHub"
./mvnw deploy --settings .travis/settings.xml -DskipTests=true --batch-mode --update-snapshots -Prelease
The script first sets the <version>
in the pom exactly to our git tag’s value. So your tag always matches the version
you want to release, e.g. 1.0
or 1.5.1
. The second part creates the release. We need to reference the Maven settings
in our repository here so that Travis CI has access rights to the Maven repositories and GitHub Releases. The important
part here is to activate therelease
profile. This tells Maven to not only create and upload a Maven release but also to
create a new GitHub Release.
Name this script release.sh
, put it inside the .travis/
directory and make it executable (chmod +x
).
Build configuration
Travis CI uses a file named .travis.yml
at the root of a GitHub repository. The snipped contains the necessary steps.
In a normal build we just want to execute a simple clean verify
. The verify
goal will execute unit and integration
tests. To make subsequent builds faster, we want to cache the m2 repositories during builds.
The most important part is the deploy section. Here we configure Travis CI to run the release.sh
script if and only if
a tag has been pushed (tags: true
) on the repo example/project
.
sudo: false
language: java
jdk:
- oraclejdk8
script: ./mvnw clean verify
cache:
directories:
- $HOME/.m2
deploy:
provider: script
script: .travis/release.sh
skip_cleanup: true
on:
repo: example/project
tags: true
jdk: oraclejdk8
Configuring Travis CI itself
Now we need to teach Travis CI the values of the environment variables used in our Maven settings. To do this navigate to the Travis CI settings for your project:
Add NEXUS_USER
and NEXUS_PASSWORD
for your
newly created user. You
also need to configure GITHUB_USERNAME
and GITHUB_TOKEN
. But even though the field is called password, what your
really want to configure here is your GitHub API token. Otherwise, the GitHub release plugin will not be able to
upload artifacts. You can obtain your personal access token here. The token needs
access to the repo scope.
And that’s it. Now you can simply create a new tag in your repository (git tag -a 1.1 -m "Release 1.1"
), push it to
GitHub and Travis CI will trigger the release process. Happy releasing!
Pitfalls
Some advice so that you do not encounter the same problems we did:
- Do not forget to make
release.sh
executable otherwise the Travis CI build will fail with a rather unhelpful message:Script failed with status 127.
- The
serverId
in the GitHub release plugin’s configuration refers to a<server>
entry in Maven’ssettings.xml
. - Do not be tempted to use your GitHub account password to configure the server settings for the GitHub release plugin. Even though the field is called password it’s really an API token that is needed here.
- Do not change the value of
<tagName>
when configuring the GitHub release plugin. Using anything different from the project’s version (i.e. the git tag’s value), will lead to recursive release builds. This happens because the release plugin will create and push a new tag if it does not already exist. Pushing a tag invokes another Travis CI build which will create a new tag and so on.