Solid CI with GitHub, TravisCI and Sonatype Nexus
We will build a basic Project Structure for a Java Project. This simple Workflow is perfect for simple libraries and open source projects.
Identify our Problems
Dependency Management & Build System
We begin with a simple Java Library of only a dozen of classes. The most simple approach would be to add the to the class path of an application. But we want a better dependency management and build system!
Version Control System
Versioning our Codes and Releases is a central part of project management and helps us to maintain consistency, documentation, compatibility and the history of our project.
Continuous Integration
Every new version of our project should be automatically built and tested. The documentation should be updated and the build artifacts stored.
Continuous Delivery
Our Library should be publicly accessible and has therefor to be deployed somewhere everybody can reach it easily.
Identify the Solutions
Dependency Management & Build System
The maybe most famous build systems for this use case are maven and gradle. Fun fact: There is a nice gradle project template out there:
In this case we will use Maven as its absolutely perfect for little projects.
Version Control System
We will use git. No discussion. When talking about a public library and open source, there already is one big player directly in front of us: GitHub! Its powerful integration with a lot of Toolchains makes it also a perfect choice for our library.
Continuous Integration
With a powerful integration of GitHub and as its free of charge we will use Travis CI for our builds.
Continuous Delivery
As we are using Maven we need a Maven Repository to deploy our maven packages. We will use a self hosted instance of Sonatype Nexus.
Implement the Solutions
We will now implement all these solutions into one project.
Initial Project setup
The first step is initiating a new maven project locally. After adding a simple .gitignore like this:
################
### IntelliJ ###
################
.idea
*.iml
#############
### Maven ###
#############
target
We can already do our first push to GitHub! Just create a new empty repository and follow the shown instructions to do your first push. After that, it should look like this:
Travis CI Setup
Make sure you have installed Travis CI for your GitHub Account via the GitHub Marketplace. After that we visit Travis-CI.org and enabled Builds for our repository.
Now we have to tell Travis, how to build our project. This can be done via a .travis.yml file in the project root. It could look like this:
################
### Language ###
################
language: java
###########
### JDK ###
###########
jdk:
- oraclejdk11
#####################
### Notifications ###
#####################
notifications:
email: false
####################
### Build script ###
####################
script:
- mvn clean compile package install
In this case we are using Oracle JDK to build our project. You can choose whatever you want here. Furthermore we instructed GitHub to turn off E-Mail notifications.
Now we should add the Maven Compiler Plugin to make sure Maven also knows what to do:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
Continuous Delivery
Before adding the delivery we just add a simple class to the project, so we can proudly say we released something! Could look like this:
package com.felixklauke.mpt;
public class PinkFluffyUnicorn {
private final String name;
private final int age;
private final long cutenessLevel;
public PinkFluffyUnicorn(String name, int age, long cutenessLevel) {
this.name = name;
this.age = age;
this.cutenessLevel = cutenessLevel;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getCutenessLevel() {
return cutenessLevel;
}
}
Now we come to the delivery part. We have to tell Maven where we want to deliver publish our stuff. This can be done via the distribution management in the main pom. Just insert your Nexus URLs here. In my case:
<distributionManagement>
<repository>
<id>klauke-enterprises-maven-releases</id>
<name>Klauke Enterprises Maven Releases</name>
<url>https://repository.klauke-enterprises.com/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>klauke-enterprises-maven-snapshots</id>
<name>Klauke Enterprises Maven Snapshots</name>
<url>https://repository.klauke-enterprises.com/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
Now we have to tell TravisCI that it should do a maven deploy. Just insert this into your .travis.yml:
##################
### Deployment ###
##################
deploy:
provider: script
script: mvn deploy --settings deploy/maven-settings.xml
on:
branch: master
tags: true
As you can see we give some arguments to the maven deploy command and we did some more configuration here (Only deploy when we have a tagged commit on the master branch). The maven-settings.xml contains the credentials to be used to access your nexus instance. This file could look like this:
<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>
<!-- Releases -->
<server>
<id>klauke-enterprises-maven-releases</id>
<username>${env.NEXUS_USERNAME}</username>
<password>${env.NEXUS_PASSWORD}</password>
</server>
<!-- Snapshot -->
<server>
<id>klauke-enterprises-maven-snapshots</id>
<username>${env.NEXUS_USERNAME}</username>
<password>${env.NEXUS_PASSWORD}</password>
</server>
</servers>
</settings>
We don’t want to expose the credentials and Travis can help us here. It can encrypt the credentials and store them in the environment variables. You can do this using the Travis CI CLI Tool (Make a copy before as this command will remove your comments and formats):
travis encrypt NEXUS_PASSWORD=<Password> --add env.global
travis encrypt NEXUS_USERNAME=<user> --add env.global
The resulting .travis.yml will look like this:
################
### Language ###
################
language: java
###########
### JDK ###
###########
jdk:
- oraclejdk11
#####################
### Notifications ###
#####################
notifications:
email: false
####################
### Build script ###
####################
script:
- mvn clean compile package install
##################
### Deployment ###
##################
deploy:
provider: script
script: mvn deploy --settings deploy/maven-settings.xml
on:
branch: master
tags: true
env:
global:
- secure: rhZtK4COFdVq2V0YhDccHTUA5V5D9dopsDfR4skZanDT4kVb8us0Rdr3As6kCbTeo2KhP40eRqnXmBt8iLvFyBMIEcFum6nuycOkUuz7L75F+7Bcw+RHBZ/DgQiWM3Wejcxfmy2/WvMp14kRmDdKzNMr+FLZaj4UrFqdQlDhZye+FyfmCuQYux6+96KXeMhIE94OmbJvfhEfCgaZwqriPHNZsQ1jCBkNzb1TjENvf7Gq+pAusADXDCosyVHFllptbR5kv2IoKT+mCTJw/XeeSXVsMWx+mC7cX8qm2RY57CS18C7aeVohWCOAZUe/4U8zED6sC/JndfKpoBwahf894KEgbQZglTyM6qsM6F+BHAB3M+VbDo/oXtowWYHul9fhzwZC/D166GHpC5AS040BEHKSzxQyG7UaRAsRQN/ThUIqWbjdam3xfNN66R6hXVjzzy8aplHrM2A1uoarCc7oNJtbocf0xTPRqo6nD3M26ttv4hn7G9gLzh7ZVcuIvIQcoaVAKpdU6gIJKp0d4AIcHxU0+C5qfOsSj0D/I3lzKkVjt1572k8HtZ5J6F/XipdpRhZ3GYiKbEEuFScbUoF0S5t6giUwjtdHgysgGBUzK4gqHaPkCaejAxpCljZoryLVQQ5WNHBmHG6o759XB+5swpluzaxMYqwhO+IsVr1RonU=
- secure: Y1gClTrGkMFaJddSt3Wbk6hmDqCH9+GImL71XWvEv1utTu3TY81zypUM8tw5xFWFC3dNR5aIfTtlv1iF5aphAeTlou9J81lZCkkBE8ltB9m7fbAMSVCW8GYAwQvh4VnJHCb47IQU8ZcPtiQ4mhuQvPcYCRf5ljdFGSVVs87h0REBr5DnBGhJ27aEy2MJ8IsbtQdN2Yb68EVMdY5FeASD8XDRWSrJ4U+I2ayrEms519y7lRcwFzpk4VMgIvliBWc1J/JmJajWjfDPKxp7wDZpMo+MPVjjVcHCoikuAGm5G86TRcqCaLSR6Mo8GOdgGwSAs1cRCmT47oMQrwXyOE3gfLoihklI/YGPS9TsdrhvVFlGpcdQ7TBzbwtsCPRQZSYLz6XO8a2KQ6+icNXhjH/7IypWS3ZaQz2hrS46NeL9GOF3NgBHh2Qd0unWU/etv8sKc9D13hJbz9rVlSZOdZBqC7i7ngDtIO1wjwonH/rVYOSbOKDIrqQ3e2euRI9ysUnOyA9e8IChIrh5qXKwhMB7rfAOPAFx+A0nSl+iuaolQ741DhuFm0uxogCHiWCM5mcd4LFS/sz4fq9IiONWp6lnxEFu3p/oyxlqSCNEBNT4lXeQQ40vNluksWGDXvbQYzDZffMXBWLTgUXG4TTxPw3FNOObTkt4pTS11Da3ripfUeE=
Finished! Now we just push everything. That should look like this:
As we only deliver tagged versions, you should tag the current commit and push your tags. Now we wait a moment and let TravisCI do its Job! After that, the deployed Build will appear in your nexus:
By using your repositories people can now access your library. PARTY! The repositories would in my case look like this:
<repositories>
<repository>
<id>klauke-enterprises-maven-releases</id>
<name>Klauke Enterprises Maven Releases</name>
<url>https://repository.klauke-enterprises.com/repository/maven-releases/</url>
</repository>
<repository>
<id>klauke-enterprises-maven-snapshots</id>
<name>Klauke Enterprises Maven Snapshots</name>
<url>https://repository.klauke-enterprises.com/repository/maven-snapshots/</url>
</repository>
</repositories>
And the Maven Dependency:
<dependency>
<groupId>com.felixklauke.mpt</groupId>
<artifactId>maven-project-template</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
Profit
Make profit guys! You can see the full project here: https://github.com/FelixKlauke/maven-project-template
Note that I only deployed a snapshot version here, not a release version!
I hope I could demonstrate a minimal example for your next project! What are your problems our questions? Hit me up!