Toshimaru's Blog

CircleCI 2 speeds up your CI build time drastically

CircleCI 2 has been released, which makes your CI faster and flexible.

I’ve accomplished 3 times faster CI build time on my public project.

Here is how I configured my circle.yml.

Before: Circle 1.0 configuration

Circle 1.0 configuration is very simple.

# circle.yml
machine:
  ruby:
    version: 2.4.1
general:
  artifacts:
    - coverage
    - tmp/capybara

After: Circle 2.0 configuration

Circle 2.0 configuration becomes a bit overwhelming, but I’ll explain it one by one.

# .circleci/config.yml
jobs:
  build:
    working_directory: ~/app
    docker:
      - image: circleci/ruby:2.4-node
        environment:
          RAILS_ENV: test
          PHANTOM_JS_VERSION: 2.1.1
    steps:
      - checkout
      - run:
          name: Install PhantomJS
          command: |
            mkdir -p ~/.phantomjs/${PHANTOM_JS_VERSION}
            curl -L --output ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs.tar.bz2 https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-${PHANTOM_JS_VERSION}-linux-x86_64.tar.bz2
            tar xfvj ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs.tar.bz2 -C ~/.phantomjs/${PHANTOM_JS_VERSION}/
            chmod ugo+x ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs-${PHANTOM_JS_VERSION}-linux-x86_64/bin/phantomjs
            sudo ln -sf ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs-${PHANTOM_JS_VERSION}-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
      # Restore bundle cache
      - type: cache-restore
        key: bundle-{{ checksum "Gemfile.lock" }}
      # Bundle install dependencies
      - run: bundle install --path vendor/bundle
      # Store bundle cache
      - type: cache-save
        key: bundle-{{ checksum "Gemfile.lock" }}
        paths:
          - vendor/bundle
      # Database setup
      - run: bundle exec rails db:create db:schema:load
      # RSpec
      - run: bundle exec rspec
      - store_artifacts:
          path: coverage
      - store_artifacts:
          path: tmp/capybara

Working Directory

First of all, set your working_directory, any name is okay.

working_directory: ~/app

docker image

Then, you need to specify base docker image on CircleCI 2. In this case, I used CircleCI official docker image listed here(All available images are listed here).

docker:
  - image: circleci/ruby:2.4-node
    environment:
      RAILS_ENV: test
      PHANTOM_JS_VERSION: 2.1.1

In environment section, I configured environment variables for the base image.

Repository checkout

This line means checkout your code base from the repository.

- checkout

Bundle cache configuration

As of CircleCI 2, you need to configured cache setting by yourself. In my Rails project, bundler is used, so I configured bundle install cache as below.

# Restore bundle cache
- type: cache-restore
  key: bundle-{{ checksum "Gemfile.lock" }}

# Bundle install dependencies
- run: bundle install --path vendor/bundle

# Store bundle cache
- type: cache-save
  key: bundle-{{ checksum "Gemfile.lock" }}
  paths:
    - vendor/bundle

Rails testing

Rails database setup and runnning RSpec:

# Database setup
- run: bundle exec rails db:create db:schema:load
# RSpec
- run: bundle exec rspec

PhantomJS

I used poltergeist as a Capybara javascript driver. It requires PhantomJS as a dependency but official Ruby docker image doesn’t include PhantomJS, so I needed to install PhantomJS by myself.

- run:
    name: Install PhantomJS
    command: |
      mkdir -p ~/.phantomjs/${PHANTOM_JS_VERSION}
      curl -L --output ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs.tar.bz2 https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-${PHANTOM_JS_VERSION}-linux-x86_64.tar.bz2
      tar xfvj ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs.tar.bz2 -C ~/.phantomjs/${PHANTOM_JS_VERSION}/
      chmod ugo+x ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs-${PHANTOM_JS_VERSION}-linux-x86_64/bin/phantomjs
      sudo ln -sf ~/.phantomjs/${PHANTOM_JS_VERSION}/phantomjs-${PHANTOM_JS_VERSION}-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs

I created environment variable of PhantomJS version(PHANTOM_JS_VERSION) so that we can upgrade PhantomJS version easily.

Artifacts

You can upload artifacts by specifying store_artifacts and the path.

- store_artifacts:
    path: coverage
- store_artifacts:
    path: tmp/capybara

Comparison

CircleCI 1.0 Phase CircleCI 1.0 CircleCI 2.0 improvement
INFRASTRUCTURE 25sec 1sec 25x faster
CHECKOUT 10sec 1sec 10x faster
MACHINE(cache restore) 15sec 3sec 5x faster
DEPENDENCIES(bundle install) 6sec 1sec 6x faster
DATABASE 11sec 6sec 2x faseter
TEST(RSpec) 20sec 22sec -
TEARDOWN(artifacts) 26sec 1sec 26x fasetr
Total build time on CircleCI 02:02 00:40 3x faster

* 0sec is rounded to 1sec

Conclusion

CircleCI 2.0 is much faster than CircleCI 1.0. Although CircleCI 2 configuration looks complex, it gives you flexibility and better CI experience. It’ll be worth it.

Resources