Hakyll on GitHub Pages with Continuous Deployment via CircleCI
Motivation
After fighting with Jekyll for a few days, I decided to seek out alternatives. One great suggestion I received (from @davenpcm) was Hakyll (which I had not known about until that time). So naturally I searched the web for examples of using Hakyll with GitHub Pages (as I wasn’t keen to try other blog providers and very much like the simplicity of GitHub Pages’ model).
After assembling my solution from a hodge podge of other guides (& after attempting to follow one of them to the letter [with a minor exception being that Hakyll’s default mode of compilation these days seems to utilize Stack rather than, as the guide expected, Cabal (which is perfectly fine to me as it does seem like the nicer tool)]), I decided to share it as others attempting to go down this road could certainly benefit from the reduction.
Enable GitHub Pages
The straightforward guide at GitHub should be sufficient to get a GitHub Page for either your site or your organization up and running. My example below will pertain to a user/organization but it can be easily modified to serve a project site by substituting gh-pages
for any appearance of master
(in the context of a reference to a branch).
Create (or transplant) your Hakyll site generator
From the root of your repository, create a folder to host your Hakyll installation & content.
$ mkdir src/
Inside this directory, add the following Makefile
:
.PHONY: clean build watch
clean:
stack exec site clean
build:
stack build --fast
stack build --fast --pedantic --haddock --test --no-run-tests --no-haddock-deps
stack exec site rebuild
watch: build
stack exec site watch
and .gitignore
:
.stack-work/
_site/
_cache/
as well as the working Hakyll generator & content. Here’s the one being used to generate this site.
CircleCI (Continuous Deployment)
Rather than going with a (complicated & error prone) multiple branch and submodule solution as outlined in the aforementioned guide, I decided to opt for a little bash-fu in the circle.yml file that, upon deployment, removes the previously generated files and copies the new ones into their place (in the root of the repository as GitHub Pages requires).
The key to this was the combination of (appropriately switched) find
, xargs
and rm
, followed up with a (correctly modified) cp
& git add
. i.e., the following relevant lines from my circle.yml
.
- cd src/ && find .. -maxdepth 1 -type d | grep -v .git | grep -v ../src | grep -v ^\.\.$ | xargs -r rm -r
- cd src/ && find .. -maxdepth 1 -type f | grep -v .git | grep -v circle\.yml | xargs -r rm
...
- cd src/ && make build && cp -a _site/. ..
- git status && git add -A .
The trailing .
on _site/.
is required on the Linux running on CircleCI although on Mac OS X, trailing slash is sufficient/recommended by the man page. Also, the -a
on the cp
invokes the -R
mode (short for recursive presumably) which grabs the entire subtree rooted at the specified directory.
Also of note here is the -A
on git add
which does the right thing when files have been added, updated or deleted (as would be the case with generated files).
The dependencies
section of circle.yml
came from a combination of the following two examples:
Concluding Thoughts
So, there we have it: a Hakyll-generated, continuously-deployed static site elevated to [essentially] first-class status on the GitHub Pages platform (with the exception being that CircleCI does our building behind the scenes rather than GitHub itself [which in practice is inconsequential]). Along with this comes all of the niceties that people love about hosting on GitHub Pages with the primary one being: edit on any device, from any location directly in GitHub and have the site automatically updated within a handful of minutes (for my first few edits this has typically been in the range of 2-3 minutes).
A link to the full source code for this site is provided at the bottom of this (and every other) page on the site. Enjoy!