Circle CI is quickly becoming my favorite CI tool. Their infrastructure is pretty robust - I have yet to see a test flake out. Plus, their UI is elegant, their config files are more expressive than Travis', and their free tier generously includes support for private builds for teams. I still have a soft spot in my heart for Semaphore, but I find it hard to use them because they charge extra for organizations and they require you to configure your environment in a web UI rather than in a YAML file. Both are leaps and bounds ahead of Travis and Shippable: their infrastructures are flakier than a Head & Shoulders commercial.

Here's a summary of the benefits of Circle CI over Semaphore, Travis, and Shippable:

  • Robust architecture (as opposed to Shippable and Travis)
  • Configure with a .yml file in your repo (as opposed to Semaphore)
  • Free for private repos and organizations - only pay when you want parallel builds (as opposed to Travis and Semaphore)

Now that you see why Circle CI is so useful, let's take a look at how to set up Circle CI for a Node.js project.

Adding a Project to Circle

Once you register for an account and log in, you should see an 'Add Projects' button on the left hand side of your Circle dashboard.

Next, choose the GitHub organization that your project is in:

Then choose the project you want to build and hit "Build Project" to trigger your first build for this project. In this example, I'll be adding my MongoDB test fixture preprocessor called dookie to Circle CI. This project's test setup is not unusual for a node project - npm install to install all the dependencies, start MongoDB, and run npm test.

Once you hit "Build Project", Circle will kick off the first build of your project. Circle prides themselves on being able to infer the right configuration for your project, but in my experience they always get it wrong. The initial build of dookie crashes horribly because Circle uses node 0.10 by default and is not smart enough to see "node": ">= 4.0.0" in the package.json. If your initial build fails, don't worry, you can configure Circle from a .yml file.

Note: By default, Circle creates a GitHub deploy key for your repo. You can think of a deploy key as an SSH key that only works for that repo. This means that, by default, Circle only gets access to one of your repos, not all of them.

The circle.yml File

TLDR; see a diff on GitHub

Putting a circle.yml file in your repo's root directory tells Circle how to set up and test your project. Circle has a detailed guide to all the options available for circle.yml, but in this article I'll highlight a few basic ones that are important for Node.js projects.

The first option is the Node.js version. Circle uses Node.js 0.10 by default, which may not be the right choice for your project. You can always tell Circle to download node directly from nodejs.org, but Circle also can pull the Node.js version you specify in the machine.node.version option

machine:
  node:
    # Circle will use node v4.0.0
    version: 4.0.0

The circle.yml file is broken up into 6 "sections" or "phases". The "machine" phase is the first phase: it's responsible for setting up the core VM. There's also a "database" phase. Much like Travis, Circle uses 3-year-old MongoDB 2.4 by default, which is not ideal. However, it's easy to install whichever version of MongoDB you want in the "database" phase. Let's run MongoDB 3.2.3:

database:
  # Circle will execute the below commands
  pre:
    # Stop MongoDB
    - sudo service mongodb stop
    # Download MongoDB 3.2.3
    - curl -Ol https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1204-3.2.3.tgz
    # Untar it
    - tar -zxvf mongodb-linux-x86_64-ubuntu1204-3.2.3.tgz
    # Create data directory
    - mkdir -p ./data/db
    # Fork MongoDB and log to './mongod.log'. Print the log file if it failed.
    - ./mongodb-linux-x86_64-ubuntu1204-3.2.3/bin/mongod --dbpath ./data/db --logpath ./mongod.log --fork || cat ./mongod.log

You can also install MongoDB in the "machine" phase. I'm not sure which approach is more correct, but as far as I can tell these approaches are interchangeable.

The most important phase is the "test" phase, which is where you define how to run your repo's tests. Circle is smart enough to know to run npm test for Node.js projects, but if you want to override this (or you just want to be explicit) you can set the test.override option.

test:
  override:
    - npm test

You can check out the whole circle.yml for dookie on GitHub.

Moving On

Circle is an excellent CI for teams. It's robust, extensible, and fits great with any GitHub-centric Node.js workflow. Circle is free for public as well as private projects, so now your team has no excuse to not run tests on every commit.

Found a typo or error? Open up a pull request! This post is available as markdown on Github
comments powered by Disqus