How To: Add Travis CI Capability to a Github Repo
Dec 23, 2013A&D has a private Travis-CI server that is used to verify changes to our private repos. Initially, this repo did not use travis, since it didn’t really have any automated tests to run.
As I (Tamara) started adding RSpec tests, my colleague Jason recommended including the repo in the travis CI environment as well. This wasn’t quite as straight-forward as initially thought, so this attempts to document the pieces in pulling this together.
Configuring .travis.yml
The first part of getting a repository ready to run under Travis is to
create a .travis.yml
file to tell Travis how to build and test your
repository.
The Travis documentation is fairly easy to understand, but no set of documentation is going to understand your specific repository’s needs. There aren’t enough examples for every possibly scenario, thus you need to really understand the environment needs of your application.
There are various elements that you need to set up:
- The language and language version
- Environment variables
- Other applications, libraries, servers, and so on that your application needs in order to run (e.g. Postgresql)
- Travis behavioural elements, such as caching, building, and so on
.travis.yml
is (obviously) a YAML file, which is the standard for
ruby applications.
Setting the language
Since we’re testing a Rails application here, ruby is our
language. The version we’re using is 2.0.0 (with no particular build
set at this point). The two sections we need in the .travis.yml
file
are:
language: ruby
rvm:
- 2.0.0
You can set multiple values in the rvm
section, to tell travis to
run a build on each version. This can explode into a large build
matrix, though, so be circumspect. This repo contains a
.ruby-version
file so it was easy to limit the versions to build
with.
This would be the minimum required to get any Rails app running, but ours has a few more requirements.
Environment Variables
$repo has some values it needs to pick up from the
environment to run correctly. With the current set of tests, the
Devise configuration parameter secret_key_base
must be set. The
env_vars-sample.rb
file is used as a template for the env_vars.rb
file that is used to set defaults if the values aren’t set explicitly
in the environment.
For the current set of tests, the only env_var needed is
SECRET_KEY_BASE
to make the Devise gem happy. Environment variables
are set in the env
section of the .travis.yml
file:
env:
SECRET_KEY_BASE="$(bundle exec rake secret)"
There is some secret sauce going on here. The rake secret
task is
one provided by the standard Rails installation that will re-generate
a secret token, which is just a hashed string, and is quite suitable
for the secret key base. The above setting results in travis running
the following at the start of the build:
export SECRET_KEY_BASE="$(bundle exec rake secret)"
which gives us exactly what we need for the run.
Although it is a travis default, I’ve also set the RAILS_ENV
envar
to 'test'
to ensure we’re running in a test situation.
Additional Software
The database used in $repo is Postgresql version 9.2,
because it makes use of the JSON variable type that was introduced at
that version. Travis by default uses Postgresql v. 9.1, so we need to
tell it what to use. The addons
section lets us specify the version:
addons:
postgresql: 9.2
Prerequisite Steps
Travis has hooks that will run before and after certain events in the build. We need to make sure we have the right configuration files created and the database created that will work for our repo.
The before_script
section lets us give bash commands that will run
just before the test script starts.
Configuration Files
Every Rails app needs a config/database.yml
file which must be
modified by the user when starting up the application. However, a good
rails practice is to make a sample configuration file which can be
included in the repository, but not the specific configuration file
which might contain passwords or other credentials that you don’t want
to put out in public or even semi-public.
In addition, $repo has an environment variable
initialization file, that has similar credential secrets that we don’t
want explicitly in the repository, so there is a
config/env_vars.sample.rb
file that is copied and modified if
necessary.
Since the sample defaults will work for our needs in the Travis CI environment, we can just copy them over directly. If the specific values in the non-sample files need to be set, you could run the sample file through sed, awk, perl, ruby or whatever to make the appropriate edits.
We want to make sure we’re putting our work in the proper
directory. Travis provides a few environment variables to us, one of
which is the TRAVIS_BUILD_DIR
envar, that points to the directory of
the current build.
(Note these might be done quite differently when creating the production environment.)
before_script:
- /bin/cp $TRAVIS_BUILD_DIR/config/database.sample.yml $TRAVIS_BUILD_DIR/config/database.yml
- /bin/cp $TRAVIS_BUILD_DIR/config/env_vars.sample.rb $TRAVIS_BUILD_DIR/config/env_vars.rb
Creating The Testing Database
For each travis run, we must create the testing database anew (travis
does not persist databases across runs). Since we’re using Postgresql,
we simply run the client and create the database with the default
user, postgres
as specified by travis.
before_script:
- psql -c 'create database howto_repo_test;' -U postgres
The before_script
Section
Combining the previous two subsections, the resultant before_script
section that runs all these is:
before_script:
- /bin/cp $TRAVIS_BUILD_DIR/config/database.sample.yml $TRAVIS_BUILD_DIR/config/database.yml
- /bin/cp $TRAVIS_BUILD_DIR/config/env_vars.sample.rb $TRAVIS_BUILD_DIR/config/env_vars.rb
- psql -c 'create database howto_repo_test;' -U postgres
Travis Operations
Bundler Caching
One thing that Travis can do for is cache our bundle operations, thus saving considerable time each build in creating the bundle environment. For this repo, there is a huge time savings from having to recompile local versions of native code (Nokogiri in particular takes a long time to build the native version).
We can tell Travis to cache the bundle within the cache
section:
cache: bundler
Bundler Options
Normally, Travis runs bundler with just the --deployment
option, but
I’ve added guard
and pry
to the Gemfile to enable me to run
continuous testing and debugging locally. We don’t want these in the
CI run, however, so I’ve put all the local stuff inside a guard
group within the Gemfile. We need to tell Travis not to include it:
bundler_args: --without guard --deployment
which tells bundler to omit the guard
section, and install things as
if this were a deployment.
Final .travis.yml
Contents
Putting the whole thing together:
language: ruby
cache: bundler
bundler_args: --without guard --deployment
rvm:
- 2.0.0
before_script:
- /bin/cp $TRAVIS_BUILD_DIR/config/database.sample.yml $TRAVIS_BUILD_DIR/config/database.yml
- /bin/cp $TRAVIS_BUILD_DIR/config/env_vars.sample.rb $TRAVIS_BUILD_DIR/config/env_vars.rb
- psql -c 'create database howto_repo_test;' -U postgres
env:
RAILS_ENV=test
SECRET_KEY_BASE="$(bundle exec rake secret)"
addons:
postgresql: 9.2
Triggering a Travis Run
Normally, Travis will begin a build whenever code is pushed to the
repository if the repository contains a .travis.yml
file. The thing
you also need to do is turn on the service hooks for the repository to
interact with TravisCI. To do this, you need to be able to change the
settings of the repo on Github. Since I didn’t have such permissions,
Jason did this, but then it wasn’t clear why builds were not starting.
The other issue with this is that if you’ve already created a pull request for a given branch, adding to that branch does not seem to trigger Travis builds. Once that PR was merged, then Travis builds began as expected.
At present, Travis will happily build anytime there is a push to any branch on Github, including new feature, bugfix, and chore branches. This includes any new or existing branches, so we don’t have the first-time issue any more.
Acknowledgements
My thanks in particular to Jason Bucki for his help on pushing me to do this and getting this up and running. Also thanks to Kyle Kestell for his help on understanding the application and getting me up to speed.