At RightScale, we use Travis CI heavily and we love it, especially compared to our previous Jenkins CI environment. It does lose out to Jenkins in one area, though: dependent builds. (Travis has this feature on their roadmap, so hopefully that’ll be wrapped up soon and this post will be obsolete.)
We have a repo which contains Selenium and integration tests for one of our products. This repo then has four submodules, one for each repo it directly depends on. (Throughout the rest of this post, the submodules of the tests repo will be called ‘upstream repos’.) When another of the upstream repos has a change which is merged to master, we want to run a build on the tests repo (called the ‘downstream repo’ below) with the new code.
What the Travis API permits
The Travis API doesn’t currently support creating a new build, but we can be a little creative. The last build of the master branch should be the last known good state of the downstream repo, and the Travis API does allow restarting builds, so that will work: when an upstream repo’s master build succeeds, restart the last master build of the downstream repo.
The only remaining issue is that, unless we want to update all of the submodules every time, the build of the downstream repo won’t use the new code; instead, it will just use the state of the submodules in the downstream repo’s last master commit. So we need to pass an argument to the new build. Again, the Travis API doesn’t support this directly, but it does have something almost as good: environment variables!
Tying it all together
We’ll need three pieces of information in the environment variables to set this up:
- Is this a dependent build? (This can be inferred from the presence of the other two; we’ll pass it separately to be explicit.)
- Which upstream repo triggered this build.
- The commit SHA which triggered this build.
trigger-dependent-build script below does this, and is
explained more details in its comments. (Warning: it uses
grep to manipulate
the Travis API’s JSON responses. This is because
jq, or a similar tool, isn’t
installed by default on Travis images.)
Now that we have upstream base repos set up to do the triggering, we just need to handle
those new environment variables in our downstream repo. There are a number of ways that we
could do this, and it really depends on the use case. If we had a downstream repo which
had tests just using the latest upstream repo, we could have skipped the environment
variables completely. In our case, as we have submodules, we could add a
to the downstream repo that does something like this:
We also use the
$TRIGGER_REPO variables for
Slack notifications if this build fails, because Travis will only notify
the original committer when a build is restarted. If they are out of the office, then we
don’t want to miss a failure.
What could go wrong?
There are some limitations to this: if an upstream change is known to require changes to the downstream repo, then we may receive spurious failure notifications. (Or we can just disable the notifications until that’s done.) Similarly, if one story is spread across multiple upstream repos, we might get a failure - updating all of the submodules might solve this, but introduce other issues.
However, we find the benefits more than make up for these small inconveniences, as since we’ve been doing this, we’ve found several bugs which would have blocked a release, without needing to remember to run anything manually.