Automating Jekyll Site Deployment with Git Webhooks
After years of hosting my static websites solely through Github pages, I switched to a linode server for this blog in November. The major impetus for the change was that I wanted my blog to have an SSL certificate, and Let’s Encrypt offered an easy way to do that. In addition, I anticipated working on a few projects in the future that would require a database and dynamically-generated pages - namely, a website idea I have had for a while to create a private recommendation website for my friends to review media. When I ran into a couple days of uninterrupted free time, it seemed as good a time as any to do the infrastructure overhaul.
My website is hosted on a linode machine, which was straightforward enough to set up. I used ArchLinux for the OS, and nginx for the webserver. All of these - including Arch - were new to me before beginning the project. The setup took a while, but wasn’t too difficult after I learned the basics.
After setting up my webserver, I realized that updating code would be a pain - I would have to work on my local machine to make any code changes, push to a remote branch, ssh into my server, pull from the remote branch, run jekyll build
, and move the contents of the generated directory into the folder that I configured nginx to serve from. To save myself some time, I started writing a script to do all these steps for me, with the intention of saving it to my local machine and running it anytime I made any code changes. However, mostly in the interest of learning new things, and partly in the interest of adding some more artisanally crafted tech flair to my deployment process, I looked into automating this process with git webhooks.
What are git webhooks?
Git webhooks allow a server to receive notifications about actions performed to a git repository, and allow a user to specify what the server should do upon receiving the notification. They are triggered when a user performs a variety of actions, such as pushing to a repository or opening a pull request. Some software deployment pipelines may use git webhooks to run tests every time a PR is merged into master, for example. Generally, if I want to do anything remotely that is triggered by a change to git, webhooks are a good bet.
In our case, we just want to use a post-receive hook to manage our web deployment process. When we push a commit to our repository, a post-receive event is triggered, and that post-receive event handles building and serving our site for us.
For far more information than I’ve given here, check out the GitHub explanation.
Setting up git webhooks
1. Create a bare git repository
First, create a bare git repository on your server:
1
2
> mkdir /home/sofiya/YOURUSERNAME.github.io.git
> git init --bare
A bare git repository is like the regular git repositories you are used to, but doesn’t contain any code that can be directly edited and is mainly used as a centralized location for people to push their code changes to. See: What is a bare git repository? and Practical differences between a bare and non-bare git repository.
2. Create the webhook
Navigate to your bare repository’s /hooks
directory. In this folder, you will see that the bare git repo comes with a few sample hooks, all named something like pre-commit.sample
or pre-push.sample
. You want to create your own post-receive
file.
1
> vim post-receive
Copy and paste the following into your file, substituting YOURUSERNAME
with your github username, and /PATH/TO/YOUR/PUBLICLY/SERVED/FILES
with, you guessed it, the path to your publicly served files. Mine is data/www
and I believe this is the nginx default, but yours might be different.
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
GIT_REPO=$HOME/repos/YOURUSERNAME.github.io.git
TMP_GIT_CLONE=$HOME/tmp/YOURUSERNAME.github.io
PUBLIC_WWW=/PATH/TO/YOUR/PUBLICLY/SERVED/FILES
git clone $GIT_REPO $TMP_GIT_CLONE
jekyll build -s $TMP_GIT_CLONE -d $PUBLIC_WWW
rm -Rf $TMP_GIT_CLONE
exit
This script will run after you push to our bare git repository on your server, and performs all the basic functions for deploying a Jekyll site: build the Jekyll _site
directory, move the site into your publicly served directory, and remove the temporary files created.
Now, set your hook to be executable:
1
> chmod +X post-receive
3. Set up a remote branch on your local machine
On your local machine, in the directory that houses the repository for your site, set up a new remote branch. You’re already familiar with remote branches, because you probably push to a remote branch all the time - the GitHub.com remote branch of your local branch, that stores all your repositories and commits for open source friends to see. You’re going to create a new remote branch that points to the bare repository on your server, that you can push to in the exact same way that you push to your remote GitHub.com repository.
1
> git remote add deploy YOURUSERNAME@YOURSERVER:repos/YOURUSERNAME.github.io.git
This repository is named deploy
. You can see all your remote repositories by running:
1
> git remote -v
4. Set up “git push” to push to both remotes at once (optional)
If you want to be able to type in git push
and push to both your remote repository on GitHub and your remote repository on your server, enter both these commands on your local machine:
1
2
> git remote set-url --add --push origin https://github.com/YOURGITHUBUSERNAME/YOURUSERNAME.github.io.git
> git remote set-url --add --push origin YOURUSERNAME@YOURSERVER:repos/YOURUSERNAME.github.io.git
5. Usage
And you’re done! Make some changes on your local branch and commit them. You should be able to run git push origin master
to push to github, and then git push deploy master
to push to your server. If you followed step 4, you should additionally be able to run git push
and push to both GitHub and your server.