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.

Undergraduate Talks, Slides, and Writeups Retrieving Your Top Genres on Spotify