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:

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 and git push --tags afterwards, you will have 0.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