Using travis-ci with miniconda, scipy, and nose
Twice in the past year I have struggled to get continuous builds and test coverage set up on Travis and Coveralls (respectively). The first time was so frustrating that I just gave up, but last week I finally got things working! I thought it would be helpful to describe what worked (and why).
Probably the information I put here will slowly become outdated, but for the
most recent copy of a working .travis.yml
file, please see the setup at
the theanets
repository.
Here are the contents of the Travis config file; I’ll break things down (at least as far as I understand them) bit by bit.
sudo: false
language: python
python:
- "2.7"
- "3.4"
cache: apt
addons:
apt:
packages:
- libatlas-dev
- libatlas-base-dev
- liblapack-dev
- gfortran
before_install:
- wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
- conda update --yes conda
install:
- conda install --yes python=$TRAVIS_PYTHON_VERSION pip numpy scipy nose
- pip install pep8 python-coveralls
- pip install .
script:
- THEANO_FLAGS=floatX=float32 nosetests --with-coverage --cover-package=theanets --logging-level=INFO
after_success:
- coveralls
The sudo: false
line enables builds on the new Docker infrastructure for
Travis. The language: python
line and the python:
section enable a
Python test.
The next two sections (the cache: apt
line and the addons: apt:
section)
enable us to install some additional Ubuntu packages to our test environment. In
this case, the config file installs packages that are needed to support the
numpy
and scipy
libraries.
The before_install
section is where the magic starts. It first downloads the
latest version of the miniconda
installation script:
- wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
Next it runs the script and adds the installed environment to the PATH
:
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
Finally, the config updates the Conda environment to the latest release. This
appears to be necessary because miniconda
releases sometimes lag behind conda
releases:
- conda update --yes conda
So far, everything has come from the Travis CI Python docs
and the Conda Travis docs. The
Travis docs are not super helpful by themselves if your project requires
scipy
—installing scipy
manually using pip
makes your builds take
forever, not to mention being a colossal waste of a fantastic, free
computational resource. I found the Conda docs to be super confusing since the
focus is more on using Conda to distribute packages, while I just wanted to use
Conda for Travis builds.
Anyway, the build environment is set up and ready to install things! Here is where things got pretty tricky.
I use the install
section of the build config to install packages that
theanets
needs for testing. This is confusing because now that conda
is
set up on the Travis build machine, there are actually two environments where
Python packages can be installed: the Travis environment, and the Conda one. The
Conda environment is sort of a neglected stepchild in the Travis environment; if
you don’t explicitly enable it, the Travis environment will be used.
With that in mind, the config file ventures into risky territory by installing some packages built specially for Conda:
- conda install --yes python=$TRAVIS_PYTHON_VERSION pip numpy scipy nose
There are two very important packages on this line: pip
and nose
. These
are both important for related reasons that seem to revolve around paths in the
build environment.
- It’s really important to
conda install pip
. If you don’t, then if you later try topip install foo
, the version ofpip
that serves your request will install things into the Travis environment, not the Conda one! Installingpip
usingconda
ensures that thepip
that serves your request will know to put things in the Conda environment. - If you use
nose
to run your tests, then you also need toconda install nose
! The reason here seems to be quite similar to the first point: if you don’t installnose
usingconda
, then the version ofnose
that runs your tests will look in the Travis environment, not the Conda one! (This took me a few hours of banging-head-on-desk to figure out.)
The other two packages are just dependencies of my project. No biggie.
Next, the config installs a couple of additional dependencies using pip
:
- pip install pep8 python-coveralls
Why pip
? As far as I understand, it’s because Conda doesn’t have access to
all of the packages on the Python package index. I’m not sure why this is the
case, but if you try to conda install
these, it won’t work. Again, this only
works here because we did conda install pip
above.
Finally, the config sets up the actual theanets
package for testing:
- pip install .
With that, the tests are ready to run. I use nose
to run tests for
theanets
; until I installed nose
using conda install
, this step was
giving me all sorts of grief.
script:
- nosetests --with-coverage --cover-package=theanets --logging-level=INFO
This line is prefaced by THEANO_FLAGS=floatX=float32
to set up an important
Theano configuration variable—if you don’t set this explicitly, then your
tests involving Theano might fail in different environments based on the types
of floating-point data that you use.
Finally, the coveralls tool uploads code coverage results from your tests, and then you get lovely clickable source maps showing where your test coverage needs work.
after_success:
- coveralls
After I got things working, I spent a couple hours increasing test coverage for the project. It’s quite the motivator to see that big red number come up for your project!
Happy coding (and testing)!