Automating Deployments to GitHub Pages with Middleman and Travis CI
I have a Middleman project and I want to push to GitHub Pages. How can I ease the process of making changes to the project's source files and publishing its generated static files?
Recently, I've been using Middleman to build static websites. I've dwelled into static site generators in the past but never really got into it until I started to build an actual website, the Unruly Tech site, which has multiple, often similar but slightly different pages. Copying and pasting became a chore and I didn't want to run a full blown server to compile templates due to the extra management. Hosting on GitHub Pages was enough.
Previously, working with GH Pages was simple enough. Just make changes to static assets and push them to gh-pages
. But now with Middleman being the middleman, I had to make changes, compile the static files locally and then push those to gh-pages
. What a chore.
Of course you could write a script to do it all in one command, but that also meant other contributors had to do the same and have a compatible environment with dependencies. That's way too much setup for a simple setup.
Now comes Travis CI. Travis is an automated build server, free for open source projects. It hooks into a repo and automatically runs scripts in a defined environment when something's pushed. With this, I now had a common environment to deploy from with minimal faff.
Note: Some dead links on this post have been removed.
Alternative Routes
I know that GH Pages has Jekyll integration to ease the process, but having tried both Jekyll and Middleman, I prefer Middleman in pretty much every aspect but that's for a different blog post
There's also some other continuous integration services available but they're all somewhat similar and I'm already familiar with Travis.
Let's start
Our goal is to be able to push changes to a Middleman project, let Travis pick it up, build and test it and deploy it to a GitHub Pages branch.
Setup the project on Travis
First, Make sure you have a Travis account and your project is visible to it by going to Travis CI's website.
Back in the root of your project, make sure you have Travis CLI installed and you're logged in:
gem install travis
travis login
Next you'll want to restrict your concurrent builds so only one is built at a time. This will prevent multiple commits being deploy out of order and causing race conditions. In the root of your project, run:
travis settings maximum_number_of_builds --set 1
Setup Middleman Deploy
First, you'll need to use the middleman-deploy extension if you haven't already and activate it with the following in your project's config.rb
:
activate :deploy do |deploy|
deploy.method = :git
deploy.branch = 'gh-pages' # or master
committer_app = "#{Middleman::Deploy::PACKAGE} v#{Middleman::Deploy::VERSION}"
commit_message = "Deployed using #{committer_app}"
if ENV["TRAVIS_BUILD_NUMBER"] then
commit_message += " (Travis Build \##{ENV["TRAVIS_BUILD_NUMBER"]})"
end
deploy.commit_message = commit_message
end
Here we're simply setting up middleman-deploy
to commit with the Travis Build Number as the message so we know where the commit came from.
If your GitHub Page is for the top-level (i.e. username.github.io) then you'll want to set deploy.branch
to master
and commit your Middleman project to a different branch like develop
.
Give Travis commit access to your project
In order for Travis to be able to commit to your GitHub Pages branch, you need to setup an SSH key for it.
Create an SSH Key for Travis
In the root of your project, create your SSH keys by running:
ssh-keygen -q -N "" -f deploy_key
travis encrypt-file deploy_key
rm -f deploy_key
You will now have these new files:
deploy_key.enc
- The encrypted private keydeploy_key.pub
- The public key
Copy and paste the contents of deploy_key.pub
as a new Deploy Key in your GitHub Repo by going to Settings > Deploy keys
in GitHub. Make sure to give it write access.
Once you've save it, you can remove the public key.
rm -f deploy_key.pub
The travis encrypt-file
command which you ran gives you a decryption command to run later:
openssl aes-256-cbc -K $encrypted... etc.
Keep note of this! We'll be using it later.
Setup the Deploy Script for Travis
Now that we have the SSH key setup, we need to use it when we deploy from Travis.
Create a new script called travis-deploy.sh
at the root of your project and give it execution rights:
touch travis-deploy.sh
chmod +x travis-deploy.sh
Set the contents of the script to be as follows:
#!/usr/bin/env bash
if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then
echo "Not deploying pull request."
exit
fi
echo -e "\nRunning Travis Deployment"
echo "Setting up Git Access"
openssl aes-256-cbc -K $encrypted... etc. # Copy and paste the command we noted before here!
chmod 600 deploy_key
# Add the SSH key so it's used on git commands
eval `ssh-agent -s`
ssh-add deploy_key
HTTPS_URL=$(git config remote.origin.url)
SSH_URL=${HTTPS_URL/https:\/\/github.com\//[email protected]:}
git remote set-url origin "${SSH_URL}"
git config --global user.name ${GH_COMMIT_AUTHOR}
git config --global user.email ${GH_COMMIT_EMAIL}
bundle exec rake deploy
This script will decrypt the encrypted private key, give it safe permissions and add it to the script's session. It then sets up the git repo to use SSH and runs the deploy
task in the same session via Rake which we'll get to next.
Make sure you've replaced the line with the comment with the output travis encrypt-file
gave you before.
Setup some Rake tasks
task default: %w[test]
task :test do
puts "\nBuilding project"
try "middleman build"
end
task :deploy do
puts "\nDeploying to GitHub"
try "middleman deploy"
end
namespace :travis do
task :script do
Rake::Task["test"].invoke
end
task :after_success do
try "./travis-deploy.sh"
end
end
## Helper so we fail as soon as a command fails.
def try(command)
system command
if $? != 0 then
raise "Command: `#{command}` exited with code #{$?.exitstatus}"
end
end
Here we're defining two tasks for Travis:
1. travis:script
Test task for the Travis build. If this fails, no deploy happens. Our test case is if the Middleman build succeeds, relying on Middleman to tell us if something's wrong.
2. travis:after_success
Deploy task after the test task succeeds. In our case, we'll run the travis-deploy.sh
script we created earlier.
Define your .travis.yml
Now that we have our Rake tasks, it's just a matter of plugging them in.
language: ruby
cache: bundler
rvm:
- 2.1.0
branches:
only:
- master # branch to build
env:
global:
- GH_COMMIT_AUTHOR="Travis CI"
- [email protected]
script:
- bundle exec rake travis:script
after_success:
- bundle exec rake travis:after_success
Pretty self-explanatory. It will run our test task first (script
), then on success it will deploy it (after_success
)!
You'll want to only run the build on the branches
holding your unbuilt Middleman project, whether it's master
, develop
or whatever.
You'll also want to setup some environment variables which travis-deploy.sh
uses:
$GH_COMMIT_AUTHOR
- The name Travis will use commit to your GitHub Pages branch.$GH_COMMIT_EMAIL
- The email Travis will use commit to your GitHub Pages branch.
Make sure everything's ready
By the end of all this, you should have at least the following files committed in the root of your project:
config.rb
- Your Middleman configuration, includingmiddleman-deploy
configuration..travis.yml
- Your Travis configuration.travis-deploy.sh
- The deploy script for Travis to run.deploy_key.enc
- The encrypted SSH key for Travis to use
Ensure no private details are committed such as unencrypted private keys and tokens.
Run it
That should be it. Commit a change to your Middleman project and watch Travis pick it up and deploy it. Watch the build and check the logs to see if anything goes wrong or any secrets are leaked.
Now you have a Middleman project setup to use GitHub Pages with automated deployment and reduced chance to publish a broken build.
To see all this in action, check out the Unruly Tech website which is open source and hosted on GitHub Pages.