Automate Hugo builds with GitHub Actions
The sources of this blog are tracked in a git repository hosted on GitHub. The
editable content is kept in the default branch main
, while the static pages
built by Hugo are stored in the gh-pages
branch. The GitHub Pages hosting
service is configured to use the content of gh-pages
as a source.
The process to build and publish a website with Hugo, explained in a previous blog post, requires several manual steps. In a world of automation, I strongly believe that if you have to do it twice, you should automate it.
In this blog post, I will leverage the power of GitHub Actions to create a workflow that will automate the build and publication of this website.
At the time of this writing, the complete workflow to publish new content to the website is:
- Checkout the content locally
- Add or edit some content
- Commit and push the changes to the
main
branch - Download the theme
- Add a worktree to the
gh-pages
branch - Build the website
- Commit the changes to the
gh-pages
branch - Push to GitHub
I want to automate steps 4 to 8 each time a change is pushed to the main
branch (step 3).
The actions defined in the automated workflow will run on GitHub hosted runners. Any time an action starts, a new runner will be allocated to run the workflow. Each runner provides a clean instance, so the workflow must setup the environment for each build.
The automated workflow steps will be:
- Checkout the
main
branch - Install Hugo
- Download the theme
- Checkout the
gh-pages
branch in the public directory - Build the website
- Commit and push the changes to the
gh-pages
branch
The GitHub Actions workflows are YAML files stored in the .github/workflows
directory of the repository. A basic workflow template must include a name, a
list of events that trigger the workflow and a list of jobs.
The automation must trigger the GitHub Actions workflow for any push to the
main
branch. I define a single job named build that will run in an Ubuntu
20.04 VM.
name: Build the Hugo website
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-20.04
steps:
[...list of steps...]
The first step of the build job is to checkout the content of the main
branch in the working directory.
GitHub provides an action to checkout the repository, which greatly simplify the process.
Add the step below to the workflow file:
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
This will checkout the branch that triggered the workflow.
Only a single commit is fetched by default. Because I want to update the last
modification date for each page using the last git commit date for that content
file (enableGitInfo = true
in config.toml), this action must fetch all
history for the branch with fetch-depth: 0
.
The brew command is installed in the Ubuntu runners, thus it is possible to install Hugo with Homebrew.
steps:
[...]
- name: Install Hugo
run: brew install hugo
This blog uses the geekblog theme. To avoid any surprise with the static
pages generated by Hugo, I prefer to stick with a known release of the theme
for automation, defined with the environment variable GEEKBLOG_RELEASE
. Any
new release of the theme must be tested locally first, before updating the
value of GEEKBLOG_RELEASE
.
Edit the workflow to set the release of the theme then add a new step to download the release from the project page:
env:
GEEKBLOG_RELEASE: 'v0.7.0'
steps:
[...]
- name: Download the geekblog theme
run: |
curl -L -O https://github.com/thegeeklab/hugo-geekblog/releases/download/${GEEKBLOG_RELEASE}/hugo-geekblog.tar.gz
mkdir -p themes/geekblog
tar -xzf hugo-geekblog.tar.gz -C themes/geekblog
rm -f hugo-geekblog.tar.gz
The source branch of my GitHub Pages is gh-pages
. Since Hugo generates the
static website in the public directory, add a step to checkout the branch
gh-pages
to the public directory. Let’s use the checkout action with
additional parameters:
steps:
[...]
- name: Checkout the gh-pages branch in the public directory
uses: actions/checkout@v2
with:
ref: gh-pages
path: public
Cleanup the content of the public directory before the build:
steps:
[...]
- name: Cleanup the public directory
run: rm -rf public/*
The build with Hugo is straightforward. Add a step to run the hugo command in the workflow:
steps:
[...]
- name: Build the website
run: hugo
Once the website is generated, the last steps are to commit and push the
changes to the gh-pages
branch for GitHub Pages to update the website.
There is an action available in the GitHub Marketplace that can handle both steps at once: git-auto-commit.
Add the step below to the workflow file:
steps:
[...]
- name: Commit and push to the gh-pages branch
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "actions: publish to gh-pages"
branch: gh-pages
repository: public/
The workflow file is now ready. The aggregated workflow file is available
here for review. Commit it to the main
branch and push it to the GitHub
repository.
$ git checkout main
$ git add .github/workflows/build-the-hugo-website.yml
$ git commit -m "Add GH Actions workflow to automate Hugo builds"
$ git push
The push will immediately trigger an action with this workflow because the
command pushes to the main
branch. Next updates to this branch, be it a new
blog post, a new page or even a configuration change will trigger this workflow
and update the website automatically.
From a user perspective, the new workflow to publish content to the website is:
- Checkout the
main
branch locally - Add or edit content
- Commit and push
This is definitely simpler. 😃