Setting up Travis CI with sbt 

Travis CI is a hosted continuous integration service for open source and private projects. Many of the OSS projects hosted on GitHub uses open source edition of Travis CI to validate pushes and pull requests. We’ll discuss some of the best practices setting up Travis CI.

Set project/build.properties 

Continuous integration is a great way of checking that your code works outside of your machine. If you haven’t created one already, make sure to create project/build.properties and explicitly set the sbt.version number:

sbt.version=1.0.0-M6

Your build will now use 1.0.0-M6.

Read the Travis manual 

A treasure trove of Travis tricks can be found in the Travis’s official documentation. Use this guide as an inspiration, but consult the official source for more details.

Basic setup 

Setting up your build for Travis CI is mostly about setting up .travis.yml. Scala page says the basic file can look like:

language: scala

scala:
   - 2.10.4
   - 2.12.1

By default Travis CI executes sbt ++$TRAVIS_SCALA_VERSION test. Let’s specify that explicitly:

language: scala

scala:
   - 2.10.4
   - 2.12.1

script:
   - sbt ++$TRAVIS_SCALA_VERSION test

More info on script section can be found in Configuring your build.

As noted on the Scala page, Travis CI uses paulp/sbt-extras as the sbt command. This becomes relevant when you want to override JVM options, which we’ll see later.

Plugin build setup 

For sbt plugins, there is no need for cross building on Scala, so the following is all you need:

language: scala

script:
   - sbt scripted

Another source of good information is to read the output by Travis CI itself to learn about how the virtual environment is set up. For example, from the following output we learn that it is using JVM_OPTS environment variable to pass in the JVM options.

$ export JVM_OPTS=@/etc/sbt/jvmopts
$ export SBT_OPTS=@/etc/sbt/sbtopts

Custom JVM options 

The default sbt and JVM options are set by Travis CI people, and it should work for most cases. If you do decide to customize it, read what they currently use as the defaults first. Because Travis is already using the environment variable JVM_OPTS, we can instead create a file travis/jvmopts:

-Dfile.encoding=UTF8
-Xms2048M
-Xmx2048M
-Xss6M
-XX:MaxPermSize=512M
-XX:ReservedCodeCacheSize=256M

and then write out the script section with -jvm-opts option:

script:
   - sbt ++$TRAVIS_SCALA_VERSION -jvm-opts travis/jvmopts test

After making the change, confirm on the Travis log to see if the flags are taking effect:

# Executing command line:
/usr/lib/jvm/java-7-oracle/bin/java
-Dfile.encoding=UTF8
-Xms2048M
-Xmx2048M
-Xss6M
-XX:MaxPermSize=512M
-XX:ReservedCodeCacheSize=256M
-jar
/home/travis/.sbt/launchers/0.13.6/sbt-launch.jar

It seems to be working. One downside of setting all of the parameters is that we might be left behind when the environment updates and the default values gives us more memory in the future.

Here’s how we can add just a few JVM options:

script:
   - sbt ++$TRAVIS_SCALA_VERSION -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M -J-Xms1024M test

sbt-extra script passes any arguments starting with either -D or -J directly to JVM.

Again, let’s check the Travis log to see if the flags are taking effect:

# Executing command line:
/usr/lib/jvm/java-7-oracle/bin/java
-Xms2048M
-Xmx2048M
-Xss6M
-XX:MaxPermSize=512M
-Dfile.encoding=UTF8
-XX:ReservedCodeCacheSize=256M
-Xms1024M
-jar
/home/travis/.sbt/launchers/0.13.6/sbt-launch.jar

Note: This duplicates the -Xms flag as intended, which might not the best thing to do.

Caching 

In late 2014, thanks to Travis CI members sending pull requests on GitHub, we learned that Ivy cache can be shared across the Travis builds. The public availability of caching is part of the benefit for trying the new container-based infrastructure.

Jobs running on container-based infrastructure:

  1. start up faster
  2. allow the use of caches for public repositories
  3. disallow the use of sudo, setuid and setgid executables

To opt into the container-based infrastructure, put the following in .travis.yml:

# Use container-based infrastructure
sudo: false

Next, we can put cache section as follows:

# These directories are cached to S3 at the end of the build
cache:
  directories:
    - $HOME/.ivy2/cache
    - $HOME/.sbt

Finally, the following a few lines of cleanup script are added:

before_cache:
  # Cleanup the cached directories to avoid unnecessary cache updates
  - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete
  - find $HOME/.sbt        -name "*.lock"               -print -delete

With the above changes combined Travis CI will tar up the cached directories and uploads them to Amazon S3. Overall, the use of the new infrastructure and caching seems to shave off a few minutes of build time per job.

Note: The Travis documentation states caching features are still experimental.

Build matrix 

We’ve already seen the example of Scala cross building.

language: scala

scala:
   - 2.10.4
   - 2.12.1

script:
   - sbt ++$TRAVIS_SCALA_VERSION test

This is a form of a build matrix. Travis CI comes with variety of the ways to run builds against different runtimes and parameters. Here’s how to build on OpenJDK 6, OpenJDK 7, and Oracle JDK 8.

jdk:
  - openjdk6
  - openjdk7
  - oraclejdk8

We can also form a build matrix using environment variables:

env:
  global:
    - SOME_VAR="1"

  # This splits the build into two parts 
  matrix:
    - TEST_COMMAND="scripted sbt-assembly/*"
    - TEST_COMMAND="scripted merging/* caching/*"

script:
   - sbt "$TEST_COMMAND"

Now two jobs will be created to build this sbt plugin, simultaneously running different integration tests. This technique is described in Parallelizing your builds across virtual machines.

Notification 

You can configure Travis CI to notify you.

By default, email notifications will be sent to the committer and the commit author, if they are members of the repository[…].

And it will by default send emails when, on the given branch:

  • a build was just broken or still is broken
  • a previously broken build was just fixed

The default behavior looks reasonable, but if you want, we can override the notifications section to email you on successful builds too, or to use some other channel of communication like IRC.

# Email specific recipient all the time
notifications:
  email:
    recipients:
      - one@example.com
  on_success: always # default: change

This might also be a good time to read up on encryption using the command line travis tool.

$ travis encrypt one@example.com

Dealing with flaky network or tests 

For builds that are more prone to flaky network or tests, Travis CI has created some tricks described in the page My builds is timing out.

Starting your command with travis_retry retries the command three times if the return code is non-zero. With caching, hopefully the effect of flaky network is reduced, but it’s an interesting one nonetheless. Here are some cautionary words from the documentation:

We recommend careful use of travis_retry, as overusing it can extend your build time when there could be a deeper underlying issue.

Another tidbit about Travis is the output timeout:

Our builds have a global timeout and a timeout that’s based on the output. If no output is received from a build for 10 minutes, it’s assumed to have stalled for unknown reasons and is subsequently killed.

There’s a function called travis_wait that can extend this to 20 minutes.

More things 

There are more thing you can do, such as set up databases, installing Ubuntu packages, and deploy continuously.

Sample setting 

Here’s a sample that puts them all together. Remember, most of the sections are optional.

# Use container-based infrastructure
sudo: false

language: scala

# These directories are cached to S3 at the end of the build
cache:
  directories:
    - $HOME/.ivy2/cache
    - $HOME/.sbt/boot/

# This is an sbt plugin, so this section is for demo purpose
scala:
   - 2.10.4

jdk:
  - openjdk7

env:
  # This splits the build into two parts
  matrix:
    - TEST_COMMAND="scripted sbt-assembly/*"
    - TEST_COMMAND="scripted merging/* caching/*"

script:
  - sbt ++$TRAVIS_SCALA_VERSION -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M "$TEST_COMMAND"

before_cache:
  # Tricks to avoid unnecessary cache updates
  - find $HOME/.sbt -name "*.lock" | xargs rm
  - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm

# Email specific recipient all the time
notifications:
  email:
    recipients:
      secure: "Some/BASE64/STUFF="
    on_success: always # default: change

Contents

sbt Reference Manual
      1. Setting up Travis CI with sbt