Markus Bergh

How Travis helps with maintaining good code quality

How Travis helps with maintaining good code quality

If you are using GitHub to host your code, it can be of great benefit to look into Travis (CI) that easily synchronize with your repository and helps you to keep good code quality. And for open source projects it is completely free to use.

The idea behind continuous integration is merging smaller chunks of your code rather than a large change. The effect of this is a healthier code base because you can test your code in tiny parts continously and this is where Travis comes in and supports you.

When using Travis it will clone your GitHub repository, and use a virtual environment where it will do what you configure it to; such as testing and bundling your application. If any or all tasks of your build will fail, the process is broken. But if everything runs okay, it will be a passed build. Travis can then, when needed, deploy your application to a certain host.

You are always given feedback of the build process and this is of great help, especially when doing code reviews with pull requests.

Travis and pull requests

When a pull request is created you will most likely receive input regarding your changes, but now you can also rely on Travis to automatically run tests on your branch and GitHub will allow or disallow a merge depending on the build result.

In other words, if any of your changes will break any tests you will keep the targeted branch to be stable and working by not being able to pollute it with broken code.

It can look like this in GitHub when everything is fine:

Setting up Travis

It is pretty straight foward to set up Travis, but you have a few requirements to begin with:

  • A GitHub login
  • A repository hosted on GitHub
  • A working script for running your build or test(s)

Travis will ask for permission to access your GitHub data to sync your repository the first time you will use it.

To control how Travis will function you will need to put a YAML file in the root of your repository, named .travis.yml.

An example of such file can look like this:

# .travis.yml

language: node_js
node_js:
  - "7"
git:
  depth: 1
cache:
  directories:
    - ./server/src/node_modules
    - ./client/src/node_modules
env:
  - TEST_DIR=./server/src
  - TEST_DIR=./client/src
script:
  - cd $TEST_DIR && npm install && npm test

In this file I have specified that I want to use Node.js in the environment because the project is written in JavaScript, and I also specify which version of Node.js. I set the depth of the git clone becasue I don't need more history, and cache up the node modules path for speeding up things. The env part is useful if you have multiple projects in your repository, and need to specify sub folders. Now the script will work in both paths, install dependencies and run the unit test(s) in parallel.

Using a helper script

Now all of the tests will run, regardless of what the pull request will target. Running all tests always is good if you have set up a daily task on the master branch for example, but you can speed things up if you run only related unit tests to what you've changed in a pull request (when doing integration testing however it is often different with relations).

To do this it is much easier if we handle the logic in a script file, so lets create one and change the configuration for Travis.

# .travis.yml
...
script:
  - ./travis-helper.sh
#!/bin/bash

# Make the script exit as soon as possible on non-zero
# status and display readline variable names and values
set -ev

# Variable coming from Travis configuration
ROOT=${TEST_DIR}

# Set source path
SRC="$ROOT/src"

# Set current branch depending if a pull request or not
if [[ -z ${TRAVIS_PULL_REQUEST_BRANCH} ]]; then
  CURRENT_BRANCH=${TRAVIS_BRANCH}
else
  CURRENT_BRANCH=${TRAVIS_PULL_REQUEST_BRANCH}
fi

# If we are running on `master` branch we always run the tests regardless of changes
if [[ "$CURRENT_BRANCH" == "master" ]]; then
  # Move into path
  cd $ROOT

  # Install dependencies
  npm install

  # Run test(s)
  npm test
else
 # Fixing issue with Travis being detached to origin
 git remote set-branches --add origin $CURRENT_BRANCH
 git fetch

 # If the branch has changes related to source files, run the tests
 if git diff origin/master..origin/$CURRENT_BRANCH --name-only | grep --silent $SRC
 then
   # Move into path
   cd $ROOT

   # Install dependencies
   npm install

   # Run test(s)
   npm test
 fi
fi

What happens here is that when Travis is ready to start building, it will run your script and decide which branch it is building on. If it is on master, it will run all of the unit test(s) in the specified paths which will be either server or client.

However if on a pull request, it will first do a check if updates are made in the source code of the specified path. Since Travis works in a detached mode with git we need to feed it with relevant updates in the git tree, and that is why we add the current branch in the list of branches tracked by remote.

Now Travis can decide whether to run test(s) or not, depending on which files you've changed. So if you would for example only update a README file, it would skip running the test(s) and the build time would be short for those cases.

I think this works great, but it would depend on your application structure.