For my new open statistics book (Statistical Thinking for the 21st Century), I used Bookdown which is a great tool for writing a book using RMarkdown. However, as the book came together, the time to build the book grew to more than 10 mins due to the many simulations and Bayesian model estimation. And since each output type (of which there are currently three: Gitbook, PDF, and EPUB) requires a separate build run, rebuilding the full book distribution became quite an undertaking. For this reason, I decided to implement an automated solution using the CircleCI continuous integration service. We already use this service for many of the software development projects in our lab (such as fMRIPrep and MRIQC), so it was a natural choice for this project as well.
The use of CircleCI for this project is made particularly easy by the fact that both the book source and the web site for the book are hosted on Github — the ability to set up hooks between Github and CircleCI allows two important features. First, it allows us to automatically trigger a rebuild of the site whenever there is a new push to the source repo. Second, it allows CircleCI to push a new copy of the book files to the separate repo that the site is served from.
Here are the steps to setting this up - see the Makefile and CircleCI config.yml file in the repo for questions. And if you come across anything that I missed please leave a comment below!
- Create a CircleCI account linked to the relevant GitHub account.
- Add the source repo to CircleCI.
- Create the CircleCI config.yml file. Here is the content of my config file, with comments added to explain each step:
# this is my custom Docker image
- image: poldrack/statsthinking21
CircleCI spins up a VM specified by a Docker image, to which we can then add any necessary additional software pieces. I initially started with an image with R and the tidyverse preinstalled (https://hub.docker.com/r/rocker/tidyverse/) but installing all of the R packages as well as the TeX distribution needed to compile the PDF took a very long time, quickly using up the 1,000 build minutes per month that come with the CircleCI free plan. In order to save this time I build a custom Docker container (Dockerfile) that incorporates all of the dependencies needed to build the book; this way, CircleCI can simply pull the container from my DockerHub repo and run it straight away rather than having to build a bunch of R packages.
In order to be able to push to a github repo, CircleCI needs a way to authenticate itself. A relatively easy way to do this is to generate an SSH key and install the public key portion as a “deploy key” on the Github repo, then install the private key as an SSH key on CircleCI. I had problems with this until I realized that it requires a very specific type of SSH key (a PEM key using RSA encryption), which I generated on my Mac using the following command:
ssh-keygen -m PEM -t rsa -C "firstname.lastname@example.org”
# check out the repo to the VM - it also becomes the working directory
# I forgot to install ssh in the docker image, so install it here as we will need it for the github push below
- run: apt-get install -y ssh
# now run all of the rendering commands
name: rendering pdf
name: rendering epub
name: rendering gitbook
The Makefile in the source repo contains the commands to render the book in each of the three formats that we distribute: Gitbook, PDF, and EPUB. Here we build each of those.
# push the rendered site files to its repo on github
name: check out site repo
ssh-keyscan github.com >> ~/.ssh/known_hosts
The ssh-keyscan command is necessary in order to allow headless operation of the ssh command necessary to access github below. Otherwise the git clone command will sit and wait at the host authentication prompt for a keypress that will never come.
# clone the site repo into a separate directory
git clone email@example.com:psych10/thinkstats.git
# copy all of the site files into the site repo directory
cp -r ~/project/_book/* .
git add .
# necessary config to push
git config --global user.email firstname.lastname@example.org git config --global user.name "Russ Poldrack"
git commit -m"automated update"
git push origin master
That’s it! CircleCI should now build and deploy the book any time there is a new push to the repo. Don’t forget to add a CircleCI badge to the README to show off your work!