Local Continuous Integration of Jekyll using Git hooks
Once of the fantastic features of git is that it’s decentralized. This means I can keep my CI (Continuous Integration) workflow without requiring internet nor extra services - just git and Jekyll.
I would say this is the closest thing you can get to the KISS (Keep It Simple, Stupid) principle. Optimally I would be building the site in a Docker container as well, but as I am the only developer on the site, I won’t bother with that type of work.
As I mentioned in my previous post, I would be using the hooks functionality of git; more specifically, the pre-commit
and post-commit
hooks.
Tackling the issues
Certainly the largest hurdle to getting this on the road was deciding how to make sure nothing gets deleted, but at the same time it is not massively inconvenient.
Sometimes, the best solution is the simplest one. I hardcoded 2 lists of files: ones that would always be updated (like /feed.xml), and ones that should never be touched (like /code).
Any file not matching either of these would use rm -i
, where -i
stands for interactive - meaning that it would ask me for permission for every file imaginable.
Unfortunately the potential interactivity makes it unsuitable for use in scripting (unless you like to live dangerously), but it was not something I was worried about.
The hooks
For a short period of time, I was hoping to use a branch to push content rather than ‘hacking’ around a post-commit
solution.
To my surprise, using git branches to deploy without pushing does not seem like a possible solution - or at least not how I see it.
The big showstopper for the branches was covering fast-forwarded branches. This is normally a desirable solution, because it keeps the history clean and devoid of merge commits.
It turns out that the hook associated with fast-forwarding is a checkout.
Since so many other things are also checkouts (basically anything that writes a file to disk), this was no longer a viable idea.
So, scrapping that idea, I ended up using an environment variable with the default being to not push. Worst case, I forget to set the variable and I have to do a git reset --soft HEAD^
and then recommit to deploy.
.git/hooks/post-commit
Nothing out of the ordinary here. I chose to use --strict_front_matter
because I prefer having warnings as errors during deployments.
You could potentially drop the build files directly into the webroot from Jekyll, but I decided not to in case the build process throws an error.
.git/hooks/pre-commit
Again, nothing too exciting here.
I used a temporary build dir since I wasn’t too sure what would happen in case jekyll serve
was already running.
Conclusion
I feel like not going for high-end solutions such as buildbot and Jenkins was worth it in my case. I ended up with no resident services and essentially the smallest possible footprint using BASH.
I hope that satiated your thirst for some code and problem solving. This is the first of many styles of posts where I will be covering a technical problem with some real-world code examples.