Automating Jekyll deployment to S3 using CircleCI in code
I've been working a great deal with Jekyll lately. I've used it for this blog for a while, but I hadn't spent much time automating the whole process. Unfortunately, when I set out to hook everything up, there didn't seem to be anybody out there doing exactly what I wanted, so it took me a little more time than I expected.
I'll guide you through the configuration steps I've taken to get my site automatically deploying to S3 (hosted by CloudFront) using CircleCI whenever you push to your master
branch.
Overview of the Set Up
When this is all configured, we will be automatically deploying your Jekyll website to an S3 bucket every time you push code to your repository's master branch.
It assumes you already have your site hosted using S3 (and, optionally, Cloudfront).
We will be using the s3_website
gem to push code to S3.
I find this a great set up because it will probably set our caching headers on our S3 objects and Gzips them, but also perform CloudFront invalidations on each build. Not to mention it takes out the grunt work of manually deploying my website.
Set Up CircleCI
First, a few steps that I shouldn't need to go into much detail:
- Create an account on CircleCI
- Add your project Point the new project to your website/blog git repository. This will kick off an initial build that will fail.
- Add AWS permissions
- From the dashboard, select the cog next to your project.
- Go to "AWS Permissions" in the sidebar.
- Enter AWS Access Keys that have adequate permissions.
Configure The Build
First, you'll want to add gem s3_website
to your Gemfile
and $ bundle install
. This ruby gem will provide us with an executable that knows how to deploy our Jekyll build to S3 and can even configure our S3 bucket and CloudFront for proper hosting (mostly).
Circle.yml
CircleCI uses a circle.yml
file, placed in the root of your project, for configuration. Here is mine:
bundle exec jekyll b -d $CIRCLE_ARTIFACTS
- This will make our jekyll output, usually saved to ./_site
, save to a special folder on the CircleCI container that will be saved later and allows you to see the output of each build through their web interface. This is mostly a luxury, but can make debugging builds later easier.
test:
override:
- echo "Oh yeah!" # No way to test Jekyll. Just do something to satisfy CircleCI.
Remember that initial build that failed? This will fix it. CircleCI is built on the premise that builds are tested. The initial build failed because there were no tests executed. A build without tests is a failure (generally that's a great policy). As far as I know, Jekyll has no mechanism to test the site that's generated, so this configuration option will simply execute a bash command that outputs some text. Since the command executes successfully, CircleCI sees it as a success and will move on to deploy after build.
deployment:
production:
branch: master
commands:
- s3_website push
This block instructs CircleCI to invoke s3_website push
for any changes to the master branch.
s3_website.yml
This configuration file will give s3_website
the information it needs to deploy to s3 and configure/manipulate your cloudfront distribution.
cloudfront_distribution_id
- Be sure to set this value, if you're using CloudFront or comment it out, otherwise.
site: <%= ENV['CIRCLE_ARTIFACTS'] %>
- This tells s3_website
to look for the site in the CircleCI artifacts folder and not the default _site
folder.
gzip:
- I've included a few very common extensions for s3_website
to gzip before uploading. Depending on your website content, you should customize this list.
exclude_from_upload:
- In the next section, I'll instruct Jekyll not to include our config files in the site build, so this block should be redundant, but I left it in place to make sure nothing gets uploaded.
_config.yml
One last step is to make sure that our brand new config files don't get deployed to our public hosting. While there's nothing special in these configs, there's no reason to publish them and generally giving any information to would-be bad guys should be avoided.
IMPORTANT: If you decide to skip this step, you will still need to exclude the vendor
folder. The build will fail with it because of a pre-build step CircleCI performs.
Just add/merge this block into your Jekyll config:
exclude:
- vendor
- Gemfile
- Gemfile.lock
- s3_website.yml
- circle.yml