Up one levelforkThis

Git post-receive Hooks, Pt. 1

I recently started using Git and have been super impressed, so I thought I’d write up a few thoughts on using it for website versioning and deployment. I’ll begin at the end of the process (where better?) – setting up post-receive hooks – and cover the more basic setting up some other time. Mainly because while the whole process of using Git feels a little like magic, this bit is by far the most magical.

My Set-Up

A few brief words about where I’m at and what set-up I use. Other than this site, which is at GitHub Pages, I use nearlyfreespeech.net for my hosting. I really can’t recommend them enough – I hesitate to use the word cheap, because I think it sends the wrong message, so instead I’ll say that they are one of the very few hosts that I consider reasonably priced for small-scale sites. Everybody else screws you.

NFSN are pay-as-you-go, whereas most hosts charge a monthly rental fee. It’s true that if you used every last drop of storage and bandwidth of your monthly allowance, your costs would probably be lower per unit elsewhere – but there is essentially a 0% chance of that happening for anybody not running a high-traffic blog or similar. To put things in perspective: the cheapest alternative host I have found costs as much for one site, for one month, as NFSN costs me for all my sites, for an entire year. They also offer SSH as standard, no questions asked.

One last comment in this extended NFSN advert (I swear they don’t run an affiliate programme): you might be put off by their lack of 24/7 support. In my time online I’ve run into a few problems with webhosts. These things happen. With other hosts – who allegedly do offer support – it often took days to get a response, and weeks to get anything done about the issue. Quite literally weeks, during which time the site in question was totally offline.

In my entire time with NFSN, I have had one problem. I put up a message on their support forum, and had a response within an hour. The next morning the issue was fixed. The only reason the fix came the next morning was the time-zone difference: if I lived in the States I’d have seen it by the end of the day. That’s how you do support.

I discovered Jekyll just after I had finished doing a big overhaul of the back- and front-end of the View from Nobody, including moving the whole site to WordPress and building a custom theme from scratch. Talk about great timing. The short version of the story is that Jekyll is the bomb, and as soon as I have time I’ll be porting the site over to it. In the meantime, Git is still pretty helpful – particularly during updates, given that I customise some of the core. I was going to set up automatic syncing of the database as well, but then I just got on with my life.

For now, I have most of my sites running on Jekyll, and one running on WordPress. I use different post-receive hooks in each case, so I’m splitting this into two parts. The WordPress version is more straightforward (they’re both pretty straightforward) so I’ll cover it first.

Last note: I’m on Windows. I expected this to be a massive headache, but thanks to the shell included with GitHub for Windows, it’s actually not been a big deal.

NFSN Directory Structure & Other Quirks

Directory Structure

There are three basic directories on NFSN:

Just to point out, all three of these reside in a root folder called home, so be aware that /home and $HOME are not the same – the latter is actually /home/private. Clear?

I’ve seen a couple of people who, when using Git for deployment, simply make the public folder a repo. No doubt that makes deployment easier, but it seems a little untidy to me – and is potentially a security risk if people start sniffing around for .git. So I set up the remote in my private folder, and take it from there.

Gzip Support, or Lack Thereof

I do have one minor gripe about NFSN: they don’t offer on-the-fly server-side compression. Whether it’s because they don’t charge for server cycles or do charge for bandwidth, I don’t know. That means that if you want to serve up your site Gzipped (and you definitely do, right?) then you need to come up with some other kind of solution.

Since WordPress generates its PHP pages on the fly, there’s not an easy answer. Maybe there’s a plug-in that generates compressed pages in parallel? Probably. For the sake of a few bytes (and given how little traffic I get) I have better things to do with my life. I include a pre-zipped version of the CSS and JavaScript files. That could probably be automated but I haven’t looked into it.

With Jekyll, since the static HTML is generated at compile-time rather than run-time, you can do the Gzipping as part of the post-receive process. More on which in part two.

A WordPress post-receive Hook

Finally, the main event. Here’s the whole thing in its ten-line entirety:

#!/bin/sh

rm -Rf /home/public/*
rm /home/public/.htaccess

git clone $HOME/.git /home/public

rm -Rf /home/public/.git/objects
rm -Rf /home/public/.git

rm /home/public/.gitattributes
rm /home/public/.gitignore
rm /home/public/README.md

exit

Nothing too complicated. At this point I’m assuming there is already a remote for your local development repo in $HOME. The words version of what this does is as follows:

  1. Delete everything that’s currently in the public folder;

  2. Clone the repo from $HOME into the public folder;

  3. There is no step three. Actually, there is: remove all of the Git-specific files.

Line by Line

#!/bin/sh

Hello this is a shell script so please, like, use Bash.

rm -Rf /home/public/*

Delete everything in the public folder.

rm /home/public/.htaccess

The previous command ignores dot-files and -folders, so we need to specifically instruct it to remove the .htaccess file. I have multiple such files throughout the directory structure, but the others are all removed when their non-hidden directories get the axe. If you have other dot-files in the root of public then you’ll also need to specify them.

git clone $HOME/.git /home/public

Clone the repo, so that public now includes an up-to-date version.

rm -Rf /home/public/.git/objects
rm -Rf /home/public/.git

Remove the .git directory. I’m not sure that this actually needs to be split into the two steps above rather than just:

rm -Rf /home/public/.git

But hey. Other people seem to do it this way, and since I don’t know what I’m up to really I’m just going to go along with that. If you were just using public as the remote, this wouldn’t be possible without breaking things, hence the point in keeping the repo in $HOME.

rm /home/public/.gitattributes
rm /home/public/.gitignore
rm /home/public/README.md

Remove the other Git-specific files that don’t need to be there. Again, if you don’t include a README, or do include other files, you’ll need to tweak this step to fit your needs.

exit

Presumably I don’t need to explain this step.


So there you have it. A nice and easy post-receive hook that is perfect (according to my low standards) for deploying WordPress but could easily be applied to other CMSs or the like. Next, we do the same for a Jekyll-based deployment (which is much more fun).