Releasing to Anaconda.org via Travis-CI

I’ve spent the last few days figuring out how to co-opt Travis CI as a build server for Anaconda.org (the package management service previously known as Binstar). I need to be able release five conda packages to Anaconda built for both Linux and OS X. Travis is not the right tool for this job, but I have managed to make it work. Since I’ve talked to others who are wrestling with this same problem, I am sharing my solution here.

Each of my five projects lives in a separate repository on Github. We have each of them as a submodule in a common repository called release. Each of these projects has a build.sh and meta.yaml file (required by Anaconda.org) in a subdirectory called conda/PACKAGE-NAME.

In my release directory, I have a simple Python script called build.py. It takes the name of a submodule and an Anaconda.org channel as command line arguments. It then uses the Python sh module to call the conda build and binstar upload commands.

binstar upload requires authentication at Anaconda.org. I did this by using a Binstar token which I add to the binstar command after fetching it from an encrypted Travis environmental variable.

I call build.py for each of the five projects from the script: section of my .travis.yml file. This will build the each package on the Travis workers and then release to Anaconda.org.

There is no easy way to get Travis to build for Linux and OS X simultaneously. However, it can be tricked into building for one or the other by changing the language: specified in the .travis.yml file. (language: objective-c will force Travis to use an OS X work; by default, it uses a Linux worker.) I wrote a fabric script that provides command line commands which will modify the language value in my .travis.yml file and then push the release repository to Github. Github triggers a Travis CI build which then deploys the repository to Anaconda.org!

If I want to cut a new release for OS X, I simply call $ fab release_osx, for Linux, I call $ fab release_linux. By default, this will release to the “main” channel on Anaconda.org. I can release to a different channel (e.g. “dev”) with $ fab release_linux:dev. When specifying the channel, the fabfile will modify my .travis.yml file to set an environmental variable that is picked up when calling build.py.

Finally, my .travis.yml file instructs Travis on preparing the build environment differently between operating systems by using Travis’s built in $TRAVIS_OS_NAME environmental variable and calling appropriate setup scripts.1

Also, to update the submodules to origin/master, I created a fabric command: $ fab update. This command calls git submodule update --remote --rebase.

This certainly isn’t a perfect solution, but it’ll work for the time being. I certainly look forward to easier solutions being developed in the future!2

Thanks to my colleague Stephen Tu who laid a lot of the groundwork for this!


  1. You may notice I have files called travis.yml and .travis.yml. Originally, my fabfile just modified the latter on the fly. For clarity, I started using travis.yml as the canonical file and .travis.yml is what is generated by the fabfile commands and used by Travis. [return]
  2. Continuum is starting to provide paid, hosted build servers to do this very task [return]