When I was developing on the http-response-headers open source library for spring boot, I was curious how difficult it would be to make this available at maven central. And of course: everything should work automatically with travis ;).
To make your jars available at http://search.maven.org/, you can submit your open source project to "Sonatype OSSRH".
Basically you have 3 steps (OSSRH Guide):
- Create a Jira Account at Sonatype
- Create a Project Ticket
- Deploy to https://oss.sonatype.org/content/repositories/snapshots or https://oss.sonatype.org/service/local/staging/deploy/maven2/.
Therefor you need to:
- Modify your pom.xml
- Add build plugins for binaries, javadoc and sources jar files and gpg signing
- Create a gpg key
- Sign your builds
- Integrate this with travis secured environment variables
The steps 1 and 2 took me some minutes and the response of the sonatype staff came a workday later. Nice.
Since I wanted to automate step 3, the following lines are more like a reminder for myself how I got this working. You can follow the following steps to set this up for your self, too.
Add general information to your pom.xml
You need to add the following parts to your pom.xml
. There are detailed explanations of these configuration values at
sonatype.org.
This general information needs to be available (e.g. a missing description tag will make deployment to maven central impossible):
<groupId>org.example.spring</groupId>
<artifactId>my-library</artifactId>
<packaging>jar</packaging>
<version>0.1.0-SNAPSHOT</version>
<name>my-library</name>
<url>https://example.org</url>
<description>This small java library is really nice if you want to do something.</description>
Also the developer and license information is necessary:
<developers>
<developer>
<id>jd</id>
<name>Joe Doe</name>
<email>[email protected]</email>
<url>https://example.org</url>
</developer>
</developers>
<licenses>
<license>
<name>MIT</name>
<url>https://opensource.org/licenses/MIT</url>
<distribution>repo</distribution>
</license>
</licenses>
Add distributionManagement for ossrh to your pom.xml
The following two entries are given to you, as soon as you finish step 1+2 at sonatype's jira:
<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>
Once set up, the maven deploy task will know the target for uploads.
Add maven build plugins
For a successful upload to maven central you need your jar, a java doc jar, a java sources jar and all of those need to
be signed with a gpg key. The following configuration in your pom.xml
will take care of those steps:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.3</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Generate gpg key
To sign your packages, you have to add a gpg key. To create a gpg key follow the steps at gpg guide at sonatype.org.
$ gpg --gen-key
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection? 1
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
Real name: Hans
Name must be at least 5 characters long
Real name: Joe Doe
Email address: [email protected]
Comment: JD
You selected this USER-ID:
"Joe Doe (JD) <[email protected]>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.
gpg: key 750E67A6 marked as ultimately trusted
public and secret key created and signed.
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u
pub 2048R/750E67A6 2016-01-31
Key fingerprint = 6E70 DA17 1F4C 7DB8 586D A100 6B3D 388D 750E 67A6
uid Joe Doe (JD) <[email protected]>
sub 2048R/533B368A 2016-01-31
This process will ask your for a passphrase, which is necessary to configure your settings.xml for the build.
Create a settings.xml for the travis build
The following settings.xml should be available in your git repository at .travis/settings.xml
:
<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>
<!-- Maven Central Deployment -->
<id>ossrh</id>
<username>${env.SONATYPE_USERNAME}</username>
<password>${env.SONATYPE_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<id>ossrh</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.executable>${env.GPG_EXECUTABLE}</gpg.executable>
<gpg.passphrase>${env.GPG_PASSPHRASE}</gpg.passphrase>
</properties>
</profile>
</profiles>
</settings>
As you can see we'll use environment-Variables to configure passphrase and sonatype password, so you don't need to commit those to your source repository (which would be a very stupid thing to do!).
Add the secrets to your Travis Settings Page
If your project is already on travis, you need to add the environment variables on the settings page. For the http-response-headers
project under DracoBlue
namespace, the url looks like this: https://travis-ci.org/DracoBlue/http-response-headers/settings
.
Fill SONATYPE_USERNAME
and SONATYPE_PASSWORD
with your jira credentials and GPG_PASSPHRASE
with your gpg passphrase.
The GPG_EXECUTABLE
should be filled with gpg
.
Create base64 of public / secret key
Since we don't want to commit the secret keys for gpg signing, we want to add them as environment variables, too.
Therefor we need to generate the key secrets as base64 encoded version with:
$ gpg -a --export-secret-keys [email protected] | base64
and store it as the environment variable GPG_SECRET_KEYS
in travis.
Now generate ownertrust with:
$ gpg --export-ownertrust | base64
and store it as the environment variable GPG_OWNERTRUST
in travis.
Add .travis.yml
Your .travis.yml file could look like this:
language: java
jdk:
- oraclejdk8
- oraclejdk7
- openjdk7
install:
- mvn --settings .travis/settings.xml install -DskipTests=true -Dgpg.skip -Dmaven.javadoc.skip=true -B -V
before_install:
- if [ ! -z "$GPG_SECRET_KEYS" ]; then echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import; fi
- if [ ! -z "$GPG_OWNERTRUST" ]; then echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust; fi
deploy:
-
provider: script
script: .travis/deploy.sh
skip_cleanup: true
on:
repo: ExampleOrg/my-library
branch: master
jdk: oraclejdk8
-
provider: script
script: .travis/deploy.sh
skip_cleanup: true
on:
repo: ExampleOrg/my-library
tags: true
jdk: oraclejdk8
It's very important to override the install
instruction for mvn with --settings .travis/settings.xml
, otherwise your
settings.xml
will be ignored and the configuration would be useless.
The idea of this setup is:
- Import the GPG Secret Keys and Ownertrust at the beginning
- Each master commit, should deploy to snapshots
- Each tagged commit, should deploy to releases
I usually use the repo
condition, to avoid that forks of my repository accidently try to publish things to maven.
Add .travis/deploy.sh
Since it's easier to read if you have all deploy steps in a seperate file, I created a .travis/deploy.sh
for this:
if [ ! -z "$TRAVIS_TAG" ]
then
echo "on a tag -> set pom.xml <version> to $TRAVIS_TAG"
mvn --settings .travis/settings.xml org.codehaus.mojo:versions-maven-plugin:2.1:set -DnewVersion=$TRAVIS_TAG 1>/dev/null 2>/dev/null
else
echo "not on a tag -> keep snapshot version in pom.xml"
fi
mvn clean deploy --settings .travis/settings.xml -DskipTests=true -B -U
This snippet sets the version in the pom file to the tag version (if it's a git tag). Afterwards a deploy will be triggered.
That's why in my pom.xml
there is always a -SNAPSHOT
qualifier and no final MAJOR.MINOR.PATCH version, yet. This
part takes care of creating a SemVer version of the pom.xml.
Make sure that the deploy.sh file is executable by running:
$ chmod +x .travis/deploy.sh
Result
If you set this up correctly, your setup will work like this:
- In your
pom.xml
you have<version>0.1.0-SNAPSHOT</version>
- If you push a commit to master of your repository, a new snapshot will be uploaded and is available as
0.1.0-SNAPSHOT
. - If you
git tag 0.1.0
andgit push --tags
afterwards, you will have0.1.0
of your library available at maven central.
You can see this in action at my library http-response-headers.
What do you think about this process? Is there an easier or different way to handle this?
Changelog
- 2016/11/28: added info about
chmod +x .travis/deploy.sh
to ensure that the deployment script is executable - 2016/11/28: added
skip_cleanup: true
- 2016/11/28: added
-Dgpg.skip
to the mvn install command, since signing is not necessary here, yet. - 2016/11/28: added
if [ ! -z "$GPG_SECRET_KEYS" ];
andif [ ! -z "$GPG_OWNERTRUST" ];
tobefore_install
commands to make PRs without the GPG Environment Variables possible