Advanced uses of Travis CI with Nim

There have been a few guides describing the use of Circle CI, originating from here. But in my opinion Travis CI is a superior service, because it has more options and is more reliable.

Basic setup¶

Let's start with the minimal Travis configuration that allows you to test Nim projects.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 .travis.yml language: c install: - | # Download the latest release of Nim into the "nim-master" folder git clone -b master --depth 1 git://github.com/nim-lang/nim nim-master/ cd nim-master # Download the latest release of Nim's prepared C sources, for bootstrapping git clone -b master --depth 1 git://github.com/nim-lang/csources csources/ cd csources # Build C sources sh build.sh cd .. # This concludes the first step of bootstrapping, don't need C sources anymore rm -rf csources # Use the executable built from C sources to compile the build tool bin/nim c koch # Compile Nim in release mode, using the Nim compiler we already have ./koch boot -d:release cd .. before_script: # Add the 'bin' folder to PATH - export PATH = "nim-master/bin: $PATH " script: # Run 'tests/all.nim'. Feel free to change this, but it needs to be a program # that returns a non-zero status code in case of failure. The testing facilities # in Nim's standard library do this. - nim compile --verbosity:0 --run tests/all

Look at this guide to get an idea about how to set up the actual tests.

You may think that the folder name nim-master is redundant, but you'll see later why I didn't call it just nim.

All this custom stuff is needed because Travis doesn't officially support Nim. And we need to pick some language. The choice of C language could be called arbitrary, but we do use C compilers here, so it's nice to make sure they're available.

Now, downloading, bootstrapping and recompiling everything every time is slow and wasteful. Let's add caching!

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 .travis.yml language: c install: - | if [ ! -x nim-master/bin/nim ] ; then # If the Nim executable does not exist (means we haven't installed Nim yet) # (do what we did before) git clone -b master --depth 1 git://github.com/nim-lang/nim nim-master/ cd nim-master git clone -b master --depth 1 git://github.com/nim-lang/csources csources/ cd csources sh build.sh cd .. rm -rf csources bin/nim c koch ./koch boot -d:release else # We already have the repository, go to it cd nim-master # Download latest commits from the repository git fetch origin if ! git merge FETCH_HEAD | grep "Already up-to-date" ; then # Recompile Nim (using itself), only if there were new changes bin/nim c koch ./koch boot -d:release fi fi cd .. before_script: - export PATH = "nim-master/bin ${ PATH :+: $PATH } " script: - nim compile --verbosity:0 --run tests/all cache: directories: - nim-master

So, after successful builds, the directory nim-master will be stored in the cache, and we can reuse it in the next builds. This is especially important when testing using Nim's master branch which rarely changes.

Another change is how the directory is added to PATH. We avoid the stray colon if PATH is empty, which would mean that we also have an empty entry in PATH (current directory)

Multiple Nim versions¶

Our next step is testing the project under multiple different configurations.

Make sure you understand what the Build matrix is.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 travis.yml language: c # Run builds with 2 different values of the `nim_branch` environment variable env: - nim_branch = master - nim_branch = devel # Run builds with 2 different choices of a C compiler compiler: - gcc - clang # This meams we get a 2x2 build matrix, with a total of 4 builds matrix: # It's OK if our project fails to build with Nim devel, but we still want to check it allow_failures: - env: nim_branch = devel # Call the commit successful as soon as the builds with master branch complete. fast_finish: true install: - | # Simply replacing "master" with `$nim_branch` everywhere means we can reuse # this installation script for both branches. if [ ! -x nim- $nim_branch /bin/nim ] ; then git clone -b $nim_branch --depth 1 git://github.com/nim-lang/nim nim- $nim_branch / cd nim- $nim_branch git clone -b $nim_branch --depth 1 git://github.com/nim-lang/csources csources/ cd csources sh build.sh cd .. rm -rf csources bin/nim c koch ./koch boot -d:release else cd nim- $nim_branch git fetch origin if ! git merge FETCH_HEAD | grep "Already up-to-date" ; then bin/nim c koch ./koch boot -d:release fi fi cd .. before_script: # `$nim_branch` is used here as well, to add the specific compiler to PATH - export PATH = "nim- $nim_branch /bin ${ PATH :+: $PATH } " script: # Specify the C compiler to Nim # (the `compiler` option of the build matrix sets the `$CC` variable) - nim compile --cc: $CC --verbosity:0 --run tests/all cache: # Cache both compilers easily directories: - nim-master - nim-devel branches: except: - gh-pages

One more thing I packed into this example is 'build all branches except for gh-pages'. But that's the default behavior of Travis anyway.

See this configuration in action:

Different configurations per branch¶

Another thing you may want to do is maintain 2 branches of your project:

master, which is supposed to work with Nim master

devel, which is supposed to work with Nim devel

So let's get to action!

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 .travis.yml language: c before_install: - | # Test the master branch with Nim master and other branches with Nim devel if [ " $TRAVIS_BRANCH " = master ] ; then export branch = master else export branch = devel fi install: - | git clone -b $branch --depth 1 git://github.com/nim-lang/nim nim- $branch / cd nim- $branch git clone -b $branch --depth 1 git://github.com/nim-lang/csources csources/ cd csources sh build.sh cd .. rm -rf csources bin/nim c koch ./koch boot -d:release cd .. before_script: - export PATH = "nim- $branch /bin: $PATH " script: - nim compile --verbosity:0 --run test/test sudo: required dist: trusty

Another change here is the last two lines. They enable The Trusty beta Build Environment. I needed this because the default environment has a very outdated version of PCRE, and Nim's regular expression support relies on a newer one. Sadly, cache doesn't work in this "sudo" environment, so that's gone.

See this configuration in action: