Running Travis Scripts Locally

Natalie Weizenbaum
3 min readFeb 23, 2017

Working on a young language is exciting. It starts out as a vast empty space full of nothing but potential, and anyone approaching it gets to realize that potential in whatever way they see fit. I personally helped shape many of Dart’s core packages, and thereby in some sense the very texture of working with the language.

But there are also ways in which working on a young language sucks. This blog post is about one of those ways. Very little is free in a young language’s ecosystem. If you need something done, chances are good you’ll have to do it yourself, even if it’s a slog. So it was when I realized several months ago that the Travis CI support for Dart had fallen behind the tooling in the rest of the language. It integrated poorly with Dart’s test package, and didn’t support the static analyzer or code formatter at all. I decided to roll up my sleeves and do what I could to fix it.

In order to update Travis’s Dart support, I needed a way to test my changes locally. I hoped this would be easy, but it turned out to be quite an ordeal. This post chronicles that ordeal, so that those who follow in my footsteps may feel less pain.

Step 1: Compiling

The component of Travis that converts .travis.yml files to shell scripts is called travis-build, and it even has its own GitHub repo. I got really excited when I first saw this, because I expected it to be cleanly separated from the rest of the infrastructure so I could easily pass in a configuration file and get a functioning script out the other end.

It soon became clear, though, that this code was not at all suited to being run outside Travis’s specialized worker setup. In order to even get it to compile a repository, I had to hook it into Travis’s larger command-line interface:

$ git clone git://github.com/travis-ci/travis-build
$ cd travis-build
$ bundle install
$ gem install travis
$ mkdir -p ~/.travis
$ ln -s `pwd` ~/.travis/build

This worked well enough to produce output when I ran travis compile, but that output was busted in a bunch of ways. First, it tried to check out the repo over SSH, which failed in an isolated environment. I sent out a PR to fix that. Then, it tried to check out a branch named "", which obviously didn’t exist. I sent out a PR to fix that too.

It also choked on matrix fields—any field like env or rvm that takes multiple values and creates multiple builds on the Travis website. These would get added as Ruby arrays to the output, which would be a syntax error and crash the whole thing. The only solution I could find there was to just use a single value instead.

# This broke.
env:
- FOO=1 BAR=2
- FOO=2 BAR=1
# This worked.
env: FOO=1 BAR=2

Finally, it generated some code that modified /etc/hosts for reasons I didn’t understand. This code didn’t work at all in my Docker container, so I just commented out the apply methods in the etc_hosts_pinning.rb, fix_etc_hosts.rb, no_ipv6_localhost.rb, and put_localhost_first.rb files in lib/travis/build/appliances. That seemed to work.

Once all this was done, travis compile > travis.sh produced a valid Bash file that ran the tests for the current package.

Step 2: Running

But where do you run that Bash file? In a Docker container, of course! Figuring out where the canonical Travis bot images lived and how to properly load the travis.sh file into them was a huge pain, but that was mostly caused by my unfamiliarity with Docker. I’ll spare you the details and instead give you a simple Dockerfile that does everything for you. Behold:

FROM quay.io/travisci/travis-rubyCOPY travis.sh /home/travis/travis.sh
RUN chmod a+rx /home/travis/travis.sh
USER travis
RUN (cd ~; bash ./travis.sh)

Put this in the same directory as the travis.sh from step 1, and run docker build .. It’ll download the source image, add the script, and run it. Then you, like me, will be free to tweak travis-build to your heart’s content.

--

--

Natalie Weizenbaum

Code gardener. Lead designer/developer of @SassCSS, working for @google on @dart_lang. Occasionally likes media.