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.