Blog System
How My Site Uses Github Pages
To quickly setup my personal site a few years ago, I pulled an often used academic website template built on Jekyll and configured to deploy on Github Pages. The system worked smoothly, but it’s been a while since I’ve used it and in the meantime some of the pieces have stopped working. In this post, I describe the system and the modifications I’ve made to get everything working together again.
Tools
The system uses Github Pages to host the site and Github Actions to deploy it.
Github Pages
The main product page states that you can host websites directly from your github repository: “Just edit, push, and your changes are live”. You can create two kinds of sites: account/organization sites (limited to just one), and unlimited project sites. For account/organization sites, the basic product is straightforward. You create a repository with a specific name (this is important so that Github knows that the repository is intended to be an account site), and then you use the repository as the “root” of your site by commiting an index.html
file to it. Push the change you commit, and the site will be available.
Pages serves static sites. This means, roughly, that the web server will map paths in the HTTP request to a stored object (could be a file in a file system, a chunk of bytes stored in a database, whatever) and returns the content as-is to the client. Unconfigured, Pages will serve the files saved in a Git repository on a given branch (master
or mainline
by default).
In my site, I have two branches: master and gh-pages. The master
branch contains all the source files that Jekyll uses to generate the site pages, and the gh-pages
branch contains the Jekyll output that Pages actually serves. These two branches, in general, are called the “source” and the “deploy” branches. To change the deploy branch, go to your repository’s Settings > Pages
and change the branch (there is also additional flexibility to deploy from a specific directory within a given branch). You can directly develop in the deploy branch (in which case there is no source branch), or you can use a build process to translate source artifacts into the final site. My site uses the “source and deploy branches” approach, and uses Github Actions for the build.
Github Actions
Github Actions is a CI/CD platform. The key concept is a workflow which describes a collection of jobs that run in response to triggers. In many ways, Actions works like the front end to a cluster workload manager such as Slurm or Kubernetes. A workflow is defined using a YAML file and stored in the .github/workflows
directory of your repo.
A trigger is an event that kicks off the jobs in a workflow. This is usually an event in the repo (e.g. you push a change or merge a pull request), but can also be a scheduled trigger (like a chron job) or a manual trigger.
A job is a sequence of steps that are configured to run on a given machine. Each step can either be a script or an action, which is a reusable, configurable step. To specify where a job runs, you set the runs-on
key in the configuration file. Github provides a small number of VM images for public repositories that are free to use. The options are listed here. Actions supports running workflows on larger hosted machines (e.g. one with a GPU) or on self-hosted machines, but I won’t cover that here.
For the site setup I’ve forked, Actions push content from master
to gh-pages
. The workflows are defined here. Let’s break down how they work. We’ve got three workflows:
- deploy-docker-tag.yml: Triggers when we push a tag that matches
v*
. There’s only one job. It checks out the repo, sets up an environment to build docker images, then builds the image using the./Dockerfile
in the repo root. It then pushes to[amirpourmand/al-folio](https://hub.docker.com/r/amirpourmand/al-folio)
on Dockerhub. I don’t ever use this. - deploy-image.yml: Similar to the
deploy-docker-tag.yml
workflow, but builds whenever we push tomaster
and we’re the original author (which we’re not). The difference between this workflow anddeploy-docker-tag.yml
seems to be that the latter adds some metadata (my guess is the tag). - deploy.yml: This is the core workflow that builds my site. It has a single
deploy
job comprised of 5 steps (two actions and three scripts). The two actions (1) checkout the repo and (2) setup ruby. The scripts run afterwards and (1) installs themermaid
CLI tool, (2) configures git and sets some state based on the triggering event, and (3) runs the./bin/deploy
script in the repo. The./bin/deploy
script is where the bulk of the work happens. In a nutshell, the./bin/deploy
script will checkout the source branch, build the site, move the./_site
contents to the repo root (so that Pages will serve the content), create a new local deploy branch (deleting it if it exists), add the built site, then force push to theorigin
remote.
Once the deploy branch is pushed to Github (remote
), Pages will serve the new content!
Developing Locally
gem install bundler jekyll
bundle install
bundle exec jekyll serve