In parts 1 and 2 of this series, we created middleman-demo, a basic Middleman-based blog, imported content from WordPress, and deployed middleman-demo to Amazon S3.
Now that middleman-demo has been deployed to production, let’s design a continuous integration workflow that automates builds and deployments.
In part 3, we’ll cover the following:
If you didn’t follow parts 1 and 2, or you no longer have your original middleman-demo code, you can clone mine and check out the part3 branch:
$ git clone http://github.com/mdb/middleman-demo && cd middleman-demo && git checkout part3
In software development, the practice of continuous delivery serves to frequently deploy iterative software bug fixes and enhancements, such that users enjoy an ever-improving product. Automated processes, such as tests, assist in rapidly validating quality with each change.
middleman-demo is a relatively simple codebase, though much of its build and release workflow can still be automated via continuous delivery. Let’s write some automated tests for middleman-demo using RSpec and Capybara. These tests can assert that the site continues to work as expected with each change.
Add the gems to the middleman-demo Gemfile:
gem 'rspec'
gem 'capybara'
Install the gems:
$ bundle install
Create a spec directory to house tests:
$ mkdir spec
As is the convention in RSpec, create a spec/spec_helper.rb file to house the RSpec configuration:
$ touch spec/spec_helper.rb
Add the following configuration to spec/spec_helper.rb to run middleman-demo during test execution:
require "middleman"
require "middleman-blog"
require 'rspec'
require 'capybara/rspec'
Capybara.app = Middleman::Application.server.inst do
set :root, File.expand_path(File.join(File.dirname(__FILE__), '..'))
set :environment, :development
end
Create a spec/features directory to house the middleman-demo RSpec test files:
$ mkdir spec/features
Create an RSpec spec file for the homepage:
$ touch spec/features/index_spec.rb
Let’s create a basic test confirming that the Middleman Demo heading is present on the homepage. Add the following to spec/features/index_spec.rb:
require "spec_helper"
describe 'index', type: :feature do
before do
visit '/'
end
it 'displays the correct heading' do
expect(page).to have_selector('h1', text: 'Middleman Demo')
end
end
Run the test and confirm that it passes:
$ rspec
You should see output like the following:
Finished in 0.03857 seconds (files took 6 seconds to load)
1 example, 0 failures
Next, add a test asserting that the first blog post is listed on the homepage; confirm it passes by running the rspec command:
it 'displays the "New Blog" blog post' do
expect(page).to have_selector('ul li a[href="/blog/2014/08/20/new-blog/"]', text: 'New Blog')
end
As an example, let’s add one more basic test, this time asserting that the New Blog text properly links to the corresponding blog post. Add the following to spec/features/index_spec.rb and confirm that the test passes:
it 'properly links to the "New Blog" blog post' do
click_link 'New Blog'
expect(page).to have_selector('h2', text: 'New Blog')
end
middleman-demo can be further tested in this fashion. The extent to which the specs test every element of the site’s functionality is up to you. At what point can it be confidently asserted that the site looks good, works as expected, and can be publicly deployed to users?
Next, push your middleman-demo code to GitHub. If you forked my original github.com/mdb/middleman-demo repository, skip this section.
1. Create a GitHub repository
If you don’t already have a GitHub account, create one. Create a repository through GitHub’s web UI called middleman-demo.
2. What should you do if your version of middleman-demo is not a git repository?
If your middleman-demo is already a git repository, skip to step 3.
If you started from scratch and your code isn’t already in a git repository, let’s initialize one now. I’m assuming you have git installed and have some basic familiarity with it.
Make a middleman-demo git repository:
$ git init && git add . && git commit -m 'initial commit'
Declare your git origin, where <your_git_url_from_step_1> is your GitHub middleman-demo repository URL:
$ git remote add origin <your_git_url_from_step_1>
Push to your GitHub repository:
$ git push origin master
You’re done; skip step 3 and move on to Integrate with Travis CI.
3. If you cloned my mdb/middleman-demo repository…
If you cloned my middleman-demo git repository, you’ll need to add your newly created middleman-demo GitHub repository as an additional remote:
$ git remote add my_origin <your_git_url_from_step_1>
If you are working in a branch, merge all your changes to master.
Then push to your GitHub repository:
$ git push -u my_origin master
Travis CI is a distributed continuous integration service that integrates with GitHub. It’s free for open source projects. Let’s configure Travis CI to run the middleman-demo tests when we push to the GitHub repository.
Travis CI looks for a .travis.yml YAML file in the root of a repository. YAML is a simple, human-readable markup language; it’s a popular option in authoring configuration files. The .travis.yml file informs Travis how to execute the project’s build.
Create a .travis.yml file in the root of middleman-demo:
$ touch .travis.yml
Configure Travis CI to use Ruby 2.1 when building middleman-demo. Add the following YAML to the .travis.yml file:
language: ruby
rvm: 2.1
Next, declare how Travis CI can install the necessary gem dependencies to build middleman-demo; add the following:
install: bundle install
Let’s also add before_script, which runs the middleman-demo tests to ensure all tests pass in advance of a build:
before_script: bundle exec rspec
Finally, add a script that instructs Travis CI how to build middleman-demo:
script: bundle exec middleman build
At this point, the .travis.yml file should look like the following:
language: ruby
rvm: 2.1
install: bundle install
before_script: bundle exec rspec
script: bundle exec middleman build
Commit the .travis.yml file:
$ git add .travis.yml && git commit -m "added basic .travis.yml file"
Now, after pushing to GitHub, Travis CI will attempt to install middleman-demo dependencies using Ruby 2.1, run its tests, and build the site. Travis CI’s command build output can be seen here:
https://travis-ci.org/<your_github_username>/middleman-demo
Assuming the build passes, you should see a green build passing badge near the top right corner of the Travis CI UI on your Travis CI middleman-demo page.
Let’s add this badge to the README.md file in middleman-demo, such that a build status badge reflecting the status of the most recent Travis CI build displays on the GitHub repository’s README.
If one does not already exist, create a README.md file:
$ touch README.md
Add the following markdown, which renders the Travis CI build status badge:
[![Build Status](https://travis-ci.org/<your_github_username>/middleman-demo.svg?branch=master)](https://travis-ci.org/<your_github_username>/middleman-demo)
Through continuous deployments, code is shipped to users as soon as a quality-validated change is committed. Travis CI can be configured to deploy a middleman-demo build with each successful build.
Let’s configure Travis CI to continuously deploy middleman-demo to the S3 bucket created in part 2 of this tutorial series.
First, install the travis command-line tools:
$ gem install travis
Use the travis command-line tools to set S3 deployments. Enter the following; you’ll be prompted for your S3 details (see the example below if you’re unsure how to answer):
$ travis setup s3
An example response is:
Access key ID: <your_aws_access_key_id>
Secret access key: <your_aws_secret_access_key_id>
Bucket: <your_aws_bucket>
Local project directory to upload (Optional): build
S3 upload directory (Optional):
S3 ACL Settings (private, public_read, public_read_write, authenticated_read, bucket_owner_read, bucket_owner_full_control): |private| public_read
Encrypt secret access key? |yes| yes
Push only from <your_github_username>/middleman-demo? |yes| yes
This automatically edits the .travis.yml file to include the following deploy information:
deploy:
provider: s3
access_key_id: <your_aws_access_key_id>
secret_access_key:
secure: <your_encrypted_aws_secret_access_key_id>
bucket: <your_s3_bucket>
local-dir: build
acl: !ruby/string:HighLine::String public_read
on:
repo: <your_github_username>/middleman-demo
Add one additional option, informing Travis to preserve the build directory for use during the deploy process:
skip_cleanup: true
The final .travis.yml file should look like the following:
language: ruby
rvm: 2.1
install: bundle install
before_script: bundle exec rspec
script: bundle exec middleman build
deploy:
provider: s3
access_key_id: <your_aws_access_key>
secret_access_key:
secure: <your_encrypted_aws_secret_access_key>
bucket: <your_aws_bucket>
local-dir: build
skip_cleanup: true
acl: !ruby/string:HighLine::String public_read
on:
repo: <your_github_username>/middleman-demo
Commit your changes:
$ git add .travis.yml && git commit -m "added travis deploy configuration"
Push to GitHub and watch the build output on Travis CI:
https://travis-ci.org/<your_github_username>/middleman-demo
If all works as expected, Travis CI will run the middleman-demo tests, build the site, and deploy to the proper S3 bucket.
Throughout this series, we’ve examined the benefits of static site generators and covered some basics regarding Middleman blogging. We’ve learned how to use the wp2middleman gem to migrate content from a WordPress blog, and we’ve learned how to deploy Middleman to Amazon’s cloud-based Simple Storage Service (S3). We’ve configured Travis CI to run automated tests, produce a build, and automate deployments.
Beyond what’s been covered within this series, there’s an extensive Middleman ecosystem worth exploring, as well as numerous additional features. Middleman’s custom extensions seek to extend basic Middleman functionality through third-party gems.
Read more about Middleman at Middlemanapp.com.
Mike Ball is a Philadelphia-based software developer specializing in Ruby on Rails and JavaScript. He works for Comcast Interactive Media where he helps build web-based TV and video consumption applications.