About
Blog
Book Diary

Using GitHub Actions for Hugo CICD

2020-07-14

Background

  • This website is written using Hugo, a static site generator similar to Jekyll and others.

  • I keep the source for the website in a private repo. The source consists of markdown files and HTML templates.

  • I serve the website from a public GitHub Pages repo.

  • When Hugo builds the site, it populates a /public directory in the private repo with the final assets that are ready to be served to site visitors.

  • Up until now, I’ve performed a manual step to bridge the gap between the two. This involves copying the assets from the private repo to the public repo, committing, then pushing the change live.

  • This blog post explains how I automated this process using GitHub Actions.

Automation

First I created a GitHub Personal Access Token. This is needed later for pushing to the public GitHub Pages repo. GitHub provides some decent documentation for how to do this. It’s all done through the GitHub website. Since it’s a one off step, there isn’t much point automating it.

Then I added the newly created Personal Access Token as a secret for the private website source repo. GitHub also provides some documentation for how to do this. Adding the secret to the repo will allow us to use use the secret in its GitHub Actions workflows. Again, this is done as a manual step via the GitHub website.

Finally, we can add the GitHub Actions workflow. The workflow is defined as a YAML file, stored in .github/worflows/deploy.yaml. It has a few interesting aspects.

name: build
on:
  push:
	branches:
	  - master
jobs:
  deploy:
	runs-on: ubuntu-latest
	steps:
	  - uses: actions/checkout@v2
	  - name: deploy
		shell: bash
		env: 
		  GH_TOKEN: ${{ secrets.GH_TOKEN }}
		run: |
		  set -exo pipefail
		  git config --global user.email "ci@ci"
		  git config --global user.name "CI"
		  commit_msg="$(git log --pretty='%h %s' -n1)"
		  git clone https://$GH_TOKEN@github.com/peterstace/peterstace.github.io.git
		  cd peterstace.github.io
		  rm -r *
		  echo petsta.net > CNAME
		  cp -r ../site/public/* .
		  git add -A
		  git status
		  git commit -m "$commit_msg"
		  git push

It only executes when changes are pushed to the master branch. This is controlled by:

on:
  push:
	branches:
	  - master

When it runs, it first checks out the source repo. This uses a predefined step (provided by GitHub):

- uses: actions/checkout@v2

Then it runs a custom bash script step. This step needs to use the secret, which is imported as an environment variable named GH_TOKEN from a secret also named GH_TOKEN:

env: 
  GH_TOKEN: ${{ secrets.GH_TOKEN }}

The actual bash script clones the public GitHub Pages repo (peterstace.github.io), deletes its content, then adds the /public directory from the source repo. It makes the assumption that Hugo has already been run on the markdown and HTML templates and the result committed to the private repo. This is a reasonable workflow, since Hugo is run as part of the regular development cycle anyway (as part of creating a new blog post or tweaking a template).

set -exo pipefail
git config --global user.email "ci@ci"
git config --global user.name "CI"
commit_msg="$(git log --pretty='%h %s' -n1)"
git clone https://$GH_TOKEN@github.com/peterstace/peterstace.github.io.git
cd peterstace.github.io
rm -r *
echo petsta.net > CNAME
cp -r ../site/public/* .
git add -A
git status
git commit -m "$commit_msg"
git push

Github
LinkedIn
© Peter Stace 2015-2024