Free Java Continuous Delivery

I’ve been sharing open source code since… well since tar balls on Usenet. Obviously it’s an ever evolving process, and I like refining how my projects do it.  My latest approach combines tools I’ve previously discussed on this blog, so  really this article is simply a slightly more coherent recap.

Delivering A Java Artifact

I’m going to walk through the tools I employ to go from my Java source, to a community usable artifact. The goal is a deployed package and resources on widely recognized community sites at no cost and with minimal effort. 

The end results will be:

  • A home page with access to source control and issue tracking on Github.
  • The complete distribution, jar, source, docs on Bintray (aka jcenter). That will allow anyone with any real build tool to use them effortlessly.
  • The plumbing to get from source to artifact with code coverage via Travis CI and Codecov.

All of it driven continuously without any intervention from code check in. All of it free.

Host The Source

While it’s not without its issues, GitHub is my preferred source control hosting site.

  • Great free tier available
  • Uses widely available/known source control tools
  • Provides static content hosting, release tracking, issue tracking, wiki etc.
  • Use gradle to build your project. It’s my preferred tool and the plumbing section below gives examples for its use.

Github will do a lot for you but over time I’ve streamlined my use of it to the following:

  1. Get a Github account if you haven’t got one
  2. Create a repository
  3. Set up a README.md in your repository. GitHub will display this on the repositories landing page. Github provides tools like “github pages” for rich static content hosting, but over time I’ve found that for a light weight project the README.md will generally do the job with the least effort.
  4. Start committing code.

So at this point you have a publicly available source code control host. Now you and your community can produce together.

Host The Artifacts

You’re project will produce artifacts: jars, source snapshots, javadocs. To share these widely you’ll want to host them on one of the centralized repositories incorporated by the major build tools.  Maven Central used to be my preferred choice but over time I’ve become a JCenter convert.  JCenter isn’t perfect, but in the end, it’s integrates into deployment tools more simply and that’s what won me over.

From Source to Artifact: The Plumbing

Once you have your project in GitHub and your maven repository set up in Bintray the next step is moving artifacts from the one to the other. For that I use Travis CI. Travis is a hosted continuous integration tool with a great free tier.  What Travis will  do is pick up you code automatically on git push, build it, run tests, code coverage and if all is well deploy it. No manual intervention is required.  So here are the steps…

  1. Get a Travis CI account linking it to your GitHub account
  2. Then add a “.travis.yml” file to you project.
  3. Add a bintray release task to your “build.gradle”
  4. Set your bintray user and API key up as environment variable for the repository on Travis
  5. Push and watch the Travis logs for the repo

The “.travis.yml” File

Here is a sample “.travis.yml” file:

language: java
jdk:
  - oraclejdk8
env:
  - TERM=dumb
after_success:
  - gradle clean jar bintray -x test

The above will build the project, run your tests, and if the tests succeed it will release the artifacts to bintray.

The Bintray Release Task

To add the Bintray task to your build you need the following additions to your “build.gradle”:


buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
  }
}

apply plugin: 'com.jfrog.bintray'
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'maven'

task sourceJar(type: Jar) {
    from sourceSets.main.allSource
    classifier = 'sources'
}

task javadocJar(type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
            artifact sourceJar
            artifact javadocJar
        }
    }
}

bintray {
    user = System.getenv('BINTRAY_USER')
    key = System.getenv('BINTRAY_API_KEY')

    dryRun = false
    publish = true
    publications = ['mavenJava']
    pkg {
        repo = 'maven'
        name = project.name
        desc = 'blah blah blah'
        websiteUrl = 'https://github.com/your/url'
        issueTrackerUrl = 'https://github.com/your/url/issues'
        vcsUrl = 'https://github.com/your/url.git'
        publicDownloadNumbers = true
    }
}

bintrayUpload.onlyIf { !project.version.toString().endsWith('SNAPSHOT') }

Ok there’s a fair bit going on here, so lets go over it.

  • You need the various plugins to package and deploy.
  • You need the task to create the source jar and javadoc.
  • You  need to define what you are publishing.
  • You need to configure the bintray task to deploy it all.
  • Lastly we’re making the upload skip SNAPSHOT versions.

Note the two environment variables used above, BINTRAY_USER and BINTRAY_API_KEY. Those names should correlate to the ones you set up on Travis, and the values should come from bintray.  You can also export them locally and try the “gradle clean jar bintray” locally and see if the project deploys. NB: Once you’ve pushed a release to Bintray, go to your “package” in the repository and submit a “Link to JCenter” request. That will mirror your releases to JCenter for the simplest sharing.

Adding Code Coverage

If you want to run test coverage on your project, this can be done lots of ways but my current favorite is Codecov.  Adding this requires just:

  1. Setting up a Codecov account linked tou your GitHub
  2. Adding a couple lines to your “.travis.yml”
  3. Adding plugins and tasks to you “build.gradle”

To update your “.travis.yml” just change  your “after_success” to:

after_success:
  - gradle jacocoTestReport
  - bash <(curl -s https://codecov.io/bash)
  - gradle clean jar bintray -x test

That will make Travis create a Jacoco report and push it to Codecov. Then you need to add the following to your “gradle.build”:


apply plugin: "jacoco"

jacocoTestReport {
  reports {
    xml.enabled = true
    html.enabled = true
  }
}

You can test this locally by running “gradle build jacocoTestReport”. Once you’ve made these changes commit and push to GitHub and watch the Travis log. You’re test coverage should appear on Codecov assuming your tests pass.

Examples

All of the open source Java I make available now employs this path to delivery.  You can look at almost-functional,  fun-jdbc, or jdk_contract_tests as examples.

Advertisements