My experiences with Travis-CI

Recently I have picked up a project that uses Travis-CI for its CI environment. At first it seemed pretty easy to do what I needed to do. Add a simple .travis.yml file that told Travis what language your project was in, as well as any commands you wanted to run. Heroku even has a fancy auto deploy feature so when it see’s your latest commit passed CI it deploys your new app version. This is all great and wonderful as long as you fit into the little box that the Heroku/Travis relationship want’s you to be in.

Dependent Builds:

Dependent builds are a pretty common need inside of a CI environment. Often times you need a job to finish building before you can start the next one. Or perhaps you have some repeated work that you split out into another job and you need to run it before your current job can continue. From another CI environment such as Jenkins, you can simply set a little option inside the job config to tell it to trigger after a build has completed. I did extensive Googling to try and find an elegant solution to creating a dependent build inside Travis. The best I could come up with was a very long bash script that was way too complicated for my needs. I decided it was best to come up with my own script in Python. Full disclosure, I come from a very java/groovy heavy background so this script is probably much longer than it needs to be. Below is the script I came up with:

Now there are a few parts of this script that are seriously a pain in the ass to obtain. The first being the slugID of your job inside travis. I found it's typically something like (username or organization)%2F(repo name). The second part is your authorization token. The documentation around it is pretty vague when it comes to how exactly you should generate it. It’s not the token on your profile page because that would make way too much sense. I also could not get the token that the api passed back to work that was supposedly my api token. The only token I could get to work was generated when I sent it a GitHub basic auth token. Here is the actual documentation page. I found it easiest to use Postman to send your request with your Git information in the basic auth field. Also remember if you are using your Git token you need to write the word token before your actual token. So in your header you would pass ‘Authorization’: ‘token (git token)’.

The above script will wait for your dependent job to complete before allowing the current job to continue on. For this to work you will need to call this script using the travis_wait command. Pass some amount of time for it to wait over 10 mins. 10 mins is the default that travis will wait for no response from an action. Example: script: travis_wait 30 python

Deployment Actions:

At the end of your Travis file you can specify where you would like your application deployed. As for Heroku there is a bit of customization that you can do here as far as what you deploy and where. It also allows you to specify some initial commands to run against your app to do things like rake db or whatever else you need to do. In my case I ran into another wall here. I need to deploy my app twice in two different modes. That is two separate Heroku apps with their own configurations. You cannot have two deploy blocks at the end of the Travis file. It simply ignores the first one and then reads the second one. There’s also no error to tell you that it’s ignoring the first block it just skips over it like it’s not there. I am forced to run my Heroku deploy steps as straight Heroku CLI commands like some sort of neanderthal.

Branch Specific Configuration:

Another pitfall I ran into is branch specific configuration. You are able to exclude branches from being ran by travis, but if you want different flows based on branch you are out of luck. There is no room for logic inside your Travis file. The downfall of them choosing a filetype that is essentially a giant key-value pair block. Again I had to write a script that did the logic of checking what branch we were on. This is not ideal since you have to run all your commands inside your script and have them return the correct things instead of having Travis maintain responsibility. It is also very limiting since once you are inside your script you no longer have access to the Travis actions you would inside your yml file. Say I wanted to deploy a different Heroku app based on what branch I was on, I do not have access to the handy deploy block inside my python script. You are forced to come up with very inelegant solutions to problems. It adds so much complication to things when you have to write scripts to do things the platform should be able to do.

I understand the push toward configuration as code, and embrace it, but you should not force people into a box they don’t want to be confined to.

I have not given up my battle quite yet. I wish they allowed a bit more flexibility outside their predefined box. I understand the push toward configuration as code, and embrace it, but you should not force people into a box they don’t want to be confined to. If you happen to have a very cleanly designed application where everything is in one repo. All your tests are in the same repo written in the same language. You use Heroku to host your app and everything is following the develop -> master pipeline then Travis will be the easiest plug and play solution you can come across. As soon as you deviate from that though you are in for some fun.