Continuous Integration in the Cloud with Circle CI
Continuous integration and deployment are awesome. Why put off until tomorrow what you can do right now as part of your workflow, saving yourself a massive headache later?
However, there are costs. Build servers and build agents, manual or automated provisioning (perhaps using Puppet or Chef), configuration for these tools, agent configuration drift, and more. None of these compare to the risk and headache of large integrations and big releases, but what if you could avoid all that while also avoiding tedious configuration? Enter Circle CI.
A proliferation of cloud services means it is possible to outsource the complexity of continuous integration and deployment, allowing you and your team to focus on building applications. While there are several tools in this space, such as Travis CI and Semaphore, I will focus Circle CI, which we currently use on my team at Sky.
It’s past time to dive right on in. Let’s build a simple Ruby on Rails (RoR) application, add some tests, and get it running on Circle CI.
Getting on with it: Setting up Rails and Writing the Test Suite
I’ll start by making a new RoR project.
gem install rails
rails new ~/workspace/demo
Next, I’ll do the Git basics and push to my github account. If you need to know more about setting up git, checkout the Github website.
cd ~/workspace/demo/
git init
git add .
git commit -m "Initial commit"
git remote add origin git@github.com:PATH-TO-YOUR-REPOSITORY.git
git push -u origin master
Now for the test ‘suite’ (which will be very contrived) I’ll install cucumber and write a basic feature. We’ll need to add Cucumber to the Gemfile and do a bundle
. I’m also adding the database cleaner because the default cucumber-rails config requires it, and rspec-rails to add rspec matchers to my step definitions.
group :test do
gem 'cucumber-rails', require false
gem 'database_cleaner'
gem 'rspec-rails'
end
I can then bootstrap the project with the necessary cucumber files by running the following command.
rails generate cucumber:install
Then write the test feature, in a feature file called homepage.feature
Feature: The homepage displays a welcome message
Scenario: A customer visiting the home page sees a welcome message
When I visit the homepage
Then I should see 'Welcome to the homepage'
Next I’ll run the tests with bundle exec cucumber
. This will output test steps to implement. I will implement these in features/step_definitions/homepage_steps.rb
. Implement the steps like this:
When(/^I visit the homepage$/) do
visit '/'
end
Then(/^I should see '(.+)'$/) do | content |
page.should have_content(content)
end
I’ll run the tests again they fail as expected, so I’ll implement the feature.
I will use rails generate
to create a controller with an index action, a corresponding route, and view.
rails generate controller home index
Next I will delete public/index.html
, then setup the router to map the root path to the controller action home#index
.
root :to => 'home#index'
Since Rails is currently configured to use a database (even if it isn’t being used yet), I’ll need to run rake db:migrate
to set that up.
Finally, I’ll need to modify app/views/home/index.html.erb
to say ‘Welcome to the homepage’
<h1>Home#index</h1>
<p>Welcome to the homepage</p>
Run the tests again (bundle exec cucumber
) and they should pass. Then commit your changes (use git add -u
to get the deleted index.html
file added to the staging area) and push the project up to github!
Getting on Circle CI
Now we are done with the Rails boilerplate, we can get to the point of the article: Circle CI!
Note on pricing: plans start at $19 a month, but include a 14 day trial. So far, the small fee (even on larger plans) has been well worth it for the time savings. Also, though I’ve built a Ruby project here, Circle CI supports several languages: Node, Java, Python, and more.
Go to CircleCI and sign in with your github account. You will need to authorise Circle CI to access your git repositories (you will be prompted). Once that is done, you should be on the manage projects page.
Find the demo project and click follow. As soon as you do this, the magic starts. In a nice, clean interface, you will receive feedback as Circle CI starts a fresh virtual machine sandbox, checks out your project from Github, exports the necessary environment variables, does a bundle, sets up a database, and runs our test suite. As a bonus, Circle CI also caches left, right, and centre so your future builds will be even faster. For example, it will not need to fetch your gems remotely every build, but will instead store them locally for reuse. All this happened in 11 seconds for me.
That’s it.
Expecting more? For many projects, Circle CI will do everything, reading in your settings and doing configuration almost automagically. Circle CI also offers many custom options.
Getting more interesting
What if you want to deploy, then run some final tests against the deployed instance(s) of the app to make sure all is well in the production environment? What if you have quite a few of these final checks and want to run them in parallel? Well, Circle CI by default runs your test suite and then deploys your application, so what you need is some customisation.
Before doing these customisations, I will setup Heroku as my application platform. Go to Heroku and sign up for an account. You will need the Heroku toolbelt available from the Heroku Toolbelt website, or using a package manager such as brew: run brew install heroku-toolbelt
. To create a new app, run Heroku apps:create APPNAME
. You may be prompted for your login details. You will need to swap out APPNAME
for a name of your choosing, or leave it blank and Heroku will generate one for you automatically.
Heroku and Circle need to communicate securely, so we need to generate an SSH key and add it to both Heroku (https://devcenter.heroku.com/articles/keys#adding-keys-to-heroku) and Circle CI (https://circleci.com/gh/robertmackenzie/circle_demo/edit#heroku).
Heroku is a very useful application platform as a service (PaaS). To find out more about Heroku, visit heroku.com. Back to circle!
At the heart of customising Circle CI builds is the circle.yml
. Create a circle.yml
file in the root directory of your project. Add the following configuration options:
deployment:
main:
branch: master
commands:
- git push git@heroku.com:APPNAME.git master
- case $CIRCLE_NODE_INDEX in 0) REMOTE=TRUE bundle exec cucumber --tags @smoke-test-steam-1 ;; 1) REMOTE=TRUE bundle exec cucumber --tags @smoke-test-stream-2 ;; esac
- paralell:true
Note: CircleCI has ‘first class’ or built in support to deploy to Heroku, including a web interface. However, because I’m going to be doing something post-deploy, we need to take care of the deployment this way instead.
As you can see from the circle.yml
, we are going to use a shell command to deploy the application to Heroku. You will need to replace APPNAME
with the name of your app on Heroku.
The next bit is more fiddly.
I’ve told CircleCI to run smoke tests, using cucumber tags (@xyz
) to split the smoke tests into two streams. This works because Circle CI exposes an environment variable $CIRCLE_NODE_INDEX
which is set to the value (1…2…3…etc) of the virtual machine running the tests. As a result, machine 1 will run cucumber scenarios tagged with @smoke-test-stream-1
and machine 2 will run cucumber scenarios tagged with @smoke-test-stream2
. You will need to create another Cucumber feature or scenario, tag the first with @smoke-test-stream-1
and the second with @smoke-test-stream-2
to see this working.
What is with the REMOTE=TRUE
in the commands to run cucumber? Well, one last thing that needs doing is to point Capybara at the deployed application. Edit features/support/env.rb
and add the following line at the bottom, where APPNAME
is the name of your app on Heroku.
if ENV['REMOTE'] == 'TRUE' Capybara.app_host = 'http://APPNAME.herokuapp.com'
With that done, commit the changes and push them to Github. If you go back to Circle CI, your project will build, deploy, and then run some of your smoke tests against the deployed instance(s) of the app. But what’s this? No parallelism!? We’ll need to add that using the Circle CI web settings.
If you go back to the main dashboard of Circle CI, you should see a list of followed projects. Click on the cog next to the demo project and you will be taken to a settings page for that project. In the menu on the left of your screen, click on build speed and then select 2x from the grid which appears. A button will appear saying ‘View your latest build running in at 2x’. Click that and away you go with full parallelism! The beauty of this is, for the majority of your test suite, CircleCI will automatically split and run your tests in parallel. It is only because we’ve been doing something a bit more unusual that any custom configuration was needed!
You can see more about customising CircleCI in their documentation
In conclusion
Configuration sucks up time and energy, but the benefits of continuous integration, automated testing, and continuous deployment are too big a win to ignore. You can get the best of both by outsourcing all the tedious details to a cloud provider, leaving you free to focus on the important detail: your application. While you are at it, you can also reap the benefits of support and incredibly fast hardware.