In a cost-saving exercise I wanted to try host my personal website on a free platform, since the code repository is hosted on GitHub, I decided to look into GitHub pages.
My existing workflow combines three repositories to build the website:
- Hugs (Repo) - Contains my Hugo configuration and assets
- Blog (Repo) - Contains the blog posts
- Hugo-theme (Repo) - Contains the Hugo theme
In order to use GitHub Pages I needed a fourth repository named after my GitHub account: joeheaton.github.io (Repo). This is the repository that will contain final static site’s assets, such as HTML, CSS, etc.. and be hosted on joeheaton.github.io.
GitHub Actions
GitHub Actions are defined in YAML which defines the steps to run against your repo. Github hosts a library of official & community Actions with configurable behaviour, these are useful for common operations such as checkout a git branch, etc.
I used actions/checkout
, peaceiris/actions-hugo
& peaceiris/actions-gh-pages
.
name: github pages
on:
push:
branches:
- main
Here we define what behaviour the Action triggers for, I specified Push, which means the Action is triggered for every git push origin main
that hits my repository. Larger projects will probably want to set this to pull_request
or one of the many other events.
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Prepare repos 🥣
uses: actions/[email protected]
with:
submodules: recursive # Fetch Hugo themes (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
My build process is fairly simple so it’s all grouped in one job called “deploy”. All Actions run in a container, which means we have a choice of Operating System. I don’t have strict requirements so I set this to ubuntu-latest
.
steps
is where the build process is defined; the first step is to checkout the repo this Action is attached to. In my case this is the hugs
repo, and I need to clone each submodule fully.
- name: Checkout joeheaton.github.io to ./public/
uses: actions/[email protected]
with:
repository: joeheaton/joeheaton.github.io
ref: gh-pages
path: ./public
fetch-depth: 0
I clone another repo, this time it’s the joeheaton.github.io
that will host the end result, I clone this into ./public
, this means when hugo builds my website it will overwrite the contents of joeheaton.github.io
, which I can then commit and push.
- name: Setup Hugo 🧪
uses: peaceiris/[email protected]
with:
hugo-version: '0.81.0'
# extended: true
This is all that’s needed to install any version of hugo thanks to the peaceiris/actions-hugo
Action.
- name: Prepare environment 🧪
run: |
sudo apt-get install -y sassc
- name: Build 🔨
run: |
SITE=joeheaton.github.io make css hugo-prod
Since I defined runs-on: ubuntu-latest
at the top of my YAML, I can fulfil one of my dependcies using Ubuntu’s package manager, after that I use my Makefile from hugs
to invoke sassc
& hugo
to build the static site! These commands get executed in the container using the run
attribute, we can even use sudo
to run as root if needed.
- name: Deploy 📦
uses: peaceiris/[email protected]
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
external_repository: joeheaton/joeheaton.github.io
publish_dir: ./public
Lastly we use peaceiris/actions-gh-pages
to deploy the modified joeheaton.github.io
, cloned to the ./public
directory. The Action uses a predefined GitHub Secret called ACTIONS_DEPLOY_KEY
to get write access my joeheaton.github.io
repository and push the contents of ./public
!
Done, now every commit to hugs
results in this Action’s YAML being run through from start to finish.
GitHub Secrets
I mentioned the ACTIONS_DEPLOY_KEY
which allows the Action to push changes to an external repository, this is how you set that up. I followed this documentation
This will create a new keypair in the current directory, we will add the private key as Secret in the repo with the static site generator, this is done via the web interface and the Secret should be named ACTIONS_DEPLOY_KEY
for peaceiris/actions-gh-pages
to pick it up by default.
Add the public key as a Deploy Key on joeheaton.github.io
.
Once this is done the Actions on hugs
can now push changes to joeheaton.github.io
!
Domains & CNAME
To complete replacing my old static site with GitHub Pages I need to map my personal domain to the website hosted on joeheaton.github.io
.
On GitHub navigate to your me.github.io
repository, go Settings and scroll down to GitHub Pages and set your Custom Domain.
Go to your Domain name’s DNS provider and add a CNAME record pointing to your me.github.io
address. Done, you should see your auto-generated GitHub Pages website when you visit your domain!
I haven’t covered enabling HTTPS, you may have noticed there is an option in GitHub to enforce HTTPS for your GitHub Pages custom domain which isn’t checked by default. I didn’t need to go through with this as my website is routed via Cloudflare which enforces HTTPS on my domain. Technically, that might mean Cloudflare is connecting to GitHub insecurely, not sure.
Errata
I realised after setting my blog
and hugs
repos back to private, that the hugs
repo is able to clone itself with the [email protected]
action because it inherently is allowed access to itself, despite being private, but it cannot access blog
.
June 2021 – I’ve since migrated to Hugo’s built-in Sass compiler, instead of installing sassc as part of the build process. I just deleted the sassc stage, and added extended: true
to uses: peaceiris/[email protected]
to .github/workflows/hugo.yml
.