I recently ported my site from WordPress to Jekyll and couldn't be happier. I have a fantastic new portable toolset that feels more suitable and maintainable for myself as a developer. The only missing piece felt like automated deployments. I was still ssh
ing into my server, pulling down my repo, and running my build tasks. This felt wrong compared to what we do at Robin Powered with Travis Continuous Integrations and Capistrano to continuously release our applications in an automated way when we merge into master
.
First, I didn't want to use Capistrano. I use it at work, and being a JS person and not a Ruby person, I didn't feel at home with it. It was something I could tweak if I needed to change parts of my deployment process, but to start from scratch I was ready to try something new and more familiar to me. Something where my question is "what would I like to do with this?", rather than "how can I do it with this?".
This is when I discovered Shipit. For the sake of this article, I'm going to assume you already know what Shipit is. If you don't, you can read my other post about how Shipit works. As I'm using shipit for my deployments, I've incorporated the shipit-deploy module, which facilitates the release process.
The next thing I need is a continuous integration build server. I use TravisCI at work, but the free plan doesn't allow for any private configurations. As I want to continuously deploy to my personal server, I need a place to hide environment variables or private SSH keys. This is when I turned to CircleCI. They allow their public containers to store private information, which was exactly what I needed. They also have a great interface with a grouped and collapsable regions representing different steps in your build:
You can also find that exact build here.
I decided I want most of my building done on my CI server -- minimizing sources, building the static site, etc. If you think about it, there's not a compelling reason that I should duplicate this work on my server. The CI server already builds the project in production mode to verify and test everything; I'd rather take that build and push it to my server instead.
This is what CircleCI will be doing:
jekyll build
)shipit
will be taking care of the last step: deploying the static build to my server. Let's take a look at what shipitfile looks like:
var path = require('path');
module.exports = function (shipit) {
require('shipit-deploy')(shipit);
shipit.initConfig({
default: {
workspace: '/tmp/jekyll-portfolio',
repositoryUrl: 'https://github.com/ajwhite/jekyll-portfolio.git',
ignore: ['.git', 'node_modules'],
keepReleases: 5,
deleteOnRollback: false,
shallowClone: true,
},
production: {
servers: 'deploy.atticuswhite.com',
deployTo: '/var/www/atticuswhite/jekyll-portfolio'
},
develop: {
servers: 'deploy.atticuswhite.com',
deployTo: '/var/www/atticuswhite/dev.jekyll-portfolio'
}
});
shipit.on('updated', function () {
var buildDirectory = path.resolve('./public/');
shipit.remoteCopy(buildDirectory, shipit.releasePath);
});
};
shipit.initConfig
is pretty basic -- we define
workspace
: where inside the container (on the CI server) to prepare the releaserepositoryUrl
: the project to useignore
: things to ignore when releasingkeepReleases
: how many historical releases to keep before removing older onesdeleteOnRollback
: if the release should be deleted when rolled backshallowClone
: clone behavior of git, basically --depth 1
The important part of the shipitfile
is the on updated
listener:
shipit.on('updated', function () {
var buildDirectory = path.resolve('./public/');
shipit.remoteCopy(buildDirectory, shipit.releasePath);
});
As mentioned previously, I don't want to run the entire build on my server. We've already built it on the CI server, and we're deploying a static site, so there's no migrations or other moving parts I need to take care of. I can just move my built project over to the production server.
shipit.remoteCopy
will perform an rsync
from the CI container to my production server. It takes the /public
directory on the container, where the built static site is, and rsync
s it to the release path created by shipit-deploy
.
When this build completes, the generated static site has been uploaded to the release path, and shipit-deploy
finishes up by rotating the latest release to be the current
, and now my site is live.
To wrap things up, I thought it would be neat to see the build number on my site, along with the deployed commit SHA
.
It's pretty simple, just a byline with a link to the Circle build. You can also just look in the footer of this blog and you'll see it.
CircleCI, like Travis and other CI integrations, offers environment variables that describe the build and the container. We'll be using
CIRCLE_SHA1
- The SHA1
of the commit being testedCIRCLE_BUILD_NUM
- The build numberThis, as I found, had a couple of challenges in it. At first, I thought I could simply use Jekyll environment, but that's only to define a name for the environment, not accessing an object of environment variables. To do this, I had to add a plugin, jekyll-environment-variables, that would take environment variables and add them to the site:
module Jekyll
class EnvironmentVariablesGenerator < Generator
def generate(site)
site.config['circle_sha'] = ENV['CIRCLE_SHA']
site.config['circle_build_number'] = ENV['CIRCLE_BUILD_NUM']
end
end
end
After reading this, you should be able to set something similar up fairly quick. To summarize:
shipit
against a web serverrsync
of your build directory containing the final static site to a directory on your web servermaster
!