Hugo is one of the most popular static site generators. It is open source, written in Go, and blazingly fast.
Hugo enables us to write our content in markdown in the editor of our choice, create themes using Go's html/template and text/template.
Hugo is often used for blogs, but also works extremely well for everything from a simple landing page, resume, or product site, to complex documentation learning resources or presentations, and has well maintained themes for all of these and more.
GitHub Pages provides free static site hosting for you, your organization, and your projects, hosted directly from your GitHub repository. Just edit, push, and your changes are live.
By default you will receive a URL in the format username.github.io
or organization.github.io
, where you can host a site directly at the root, or host your project at username.github.io/repository. We will be using the project option by default as we experiment. GitHub Pages supports the Jekyll static site generator natively, but since it deploys static HTML pages and assets, we can build Hugo static sites with GitHub Actions.
It is worth mentioning that Azure Static Web Apps (Preview) is another feature-rich option that uses GitHub Actions to build and deploy your static web app, and has native support for Hugo, amongst other popular static site and front-end frameworks, and has support for APIs, routing, authentication, authorization, pre-production environments for Pull Requests, and more. However, our focus today is to explore GitHub Actions via Hugo, as a Go CLI tool, and how we can automate and create a GitHub Actions workflow from scratch, without any local or external dependencies, or needing to leave our GitHub repository.
If you are new to Hugo, you may want to follow these steps locally. You can follow Hugo's Quick Start to install Hugo, choose a Hugo Theme, and test locally. This includes the hugo server command which lets you preview locally, including draft, future, or expired content, and a LiveReload feature that enable you to see your edits in realtime.
For the purposes of this lab, and for use inside GitHub Actions, we have tweaked and wrapped the Quick Start and Host on GitHub steps into bash functions in hugo.sh. We will step through hugo.sh, but you can use it locally, or go through the same steps manually, if you prefer.
We also include our first (optional) GitHub Action (1-hugo-setup
) that can bootstrap our first site, the gh-pages
and gh-pages-content
branches, along with a clean easy to modify theme, hugo xmin, without any local tools or need to leave GitHub.
- Open the the-gophers/go-actions and click the "Use this template" button.
Template repositories are a useful way to make any project project or sample simple to share and be used as a starting point by others. GitHub Actions workflows are included whenever a repo is used as a template repo, or forked.
- Select your own account and create a new repository. Leaving the name
go-actions
is a good option. - Check the
Include all branches
box before clicking theCreate repository from template
button. - Click the
Actions
tab on your newly created repo. - Look for the
1-hugo-setup
action on the left hand side. You will see it says "This workflow has a workflow_dispatch event trigger." and you can clickRun workflow
, leave theBranch: main
by default, and clickRun workflow
. Here you can also supply additional inputs to aworkflow_dispatch
action. We've left an example input calleddebug
with a default value oftrue
.Note: the
Branch
option will decide both which version of an Actions workflow, and the branch that is checked out when the action runs. In order for a workflow to appear here it must also be present in the default branch of the repo. - Click on the
Actions
tab again, and you will see the action is now running. Click on the action, and then click the job name,deploy
, where you will see live logs of the build. - In less than a minute, the
1-hugo-setup
workflow will use thehugo.sh
script to bootstrap a static site in a newgh-pages-content
branch inside thecurrent
repo, including new site, the hugo xmin theme which we have chosen for you, configuration via config.toml, and a new post undercontent/posts
withdraft: true
set in its front matter. - Your
1-hugo/
folder in thegh-pages-content
branch of your repository should now look like this:. |-- README.md |-- archetypes | `-- default.md |-- config.toml |-- content | `-- posts | `-- my-first-post.md |-- hugo.sh |-- themes `-- hugo-xmin
So how did all this happen? In this repository we have 2 workflows and 1 shell script for this lab:
.
├── .github
│ └── workflows
│ ├── 1-hugo-setup.yml
│ └── 1-hugo-deploy.yml
└── 1-hugo
├── README.md
└── hugo.sh
Let's begin with 1-hugo.sh
, which contains a handful of bash functions:
hugo-install-linux
installs Hugo for linux by downloading and extracting the Go binary directly from gohugoio/hugo's Releases page.hugo-new-site
runshugo new site
and creates a new site, which is checked into the git repository on thegh-pages-content
branch. We are creating this by convention so that your site content is separate from the rest of the sample repo. Note, that because this folder already existed, and hadhugo.sh
in it, we had to use the--force
flag forhugo new site
. You can delete thegh-pages-content
branch at any time and re-run the1-hugo-setup
action to experiment further.hugo-github-init
creates thegh-pages
branch required to deploy to GitHub actions. Your remote must be calledorigin
, as this is hard-coded inhugo.sh
.hugo-github-deploy
builds your site, and uses the git worktree command to ensure thepublic/
folder our content is generated to is part of thegh-pages
branch. We are taking this extra step to keep your site and its markdown-formatted content separate from the HTML files and other static assets that will be generated on every change.hugo-reset
is not used during setup or deployment, but will remove the git-worktree, that points to thegh-pages
branch, if needed.
Now let's publish your first piece of content.
- Find the
gh-pages-content
branch and navigate to the same1-hugo/
folder where your site is now located. Findcontent/posts/
and openmy-first-post.md
. Remove the line that saysdraft: true
and commit that change. - Your second GitHub Actions workflow,
1-hugo-deploy
, will kick off immediately. Click theActions
tab to have a look at the output. - Click on the
Settings
tab of your GitHub repository, and scroll down toGitHub Pages
. You should see a message that says: "Your site is published at https://the-gophers.github.io/go-actions/". Here you will see other options such as custom domains and to use a branch other thangh-pages
. GitHub Pages are always public, but they can be generated from a repository that remains private. If for any reason your site hasn't already been generated, select thegh-pages
branch here, or make any commit to the pre-existinggh-pages
branch (e.g. add a README.md).
Congratulations! You've used your first GitHub Actions workflow stored in a YAML file under .github/workflows/1-hugo-setup.yml manually triggered by a workflow_dispatch
event. Your second GitHub Actions workflow stored in a YAML file under .github/workflows/1-hugo-deploy.yml, was triggered by the push event which is one of 27 webhook events, the cron-like schedule event, and 2 manual (workflow_dispatch, repository_dispatch) events that can trigger Actions workflows.
Both 1-hugo-setup
and 1-hugo-deploy
use a single Action called Checkout V2, which lives inside the public github.com/actions/checkout repository. The actions/checkout@v2
Action is included by default with most workflows (if you click Actions > New workflow > Simple workflow > Setup this workflow
, for example), and checks-out your repository under $GITHUB_WORKSPACE
, one of a number of default environment variables that GitHub Actions sets.
The V2 of this action enables your scripts to run authenticated git commands by persisting an automatic GITHUB_TOKEN GitHub Actions Secret that GitHub creates as as part of every workflow to your local git config.
GitHub Actions workflows are defined in YAML. Let's look at the syntax for the simplest workflow we have created.
Note: Editing YAML by hand can be a hassle for many developers, but the GitHub web interface has its own YAML workflow editor with auto-completion, linting and even help with cron expressions.
Both workflows begin with the name, and the on section where we define the events that trigger them. In 1-hugo-setup.yml
, workflow_dispatch takes inputs that are passed via the github.event.inputs
payload on the github context that contains other useful values such as github.sha
and github.event.respository.name
which we use to set a SITE_BASEURL
environment variable under env
and later use to set baseURL in our config.toml using context expression syntax.
We define a job named deploy
with a step named hugo.sh
that runs-on ubuntu-latest
. The linux ubuntu-latest
runner defaults to ubuntu-18.04
, but ubuntu-16.04
and ubuntu-20.04
are also options alongside Windows Server 2019 (windows-latest
/windows-2019
), macOS Big Sur (macos-11.0
), macOS Catalina (macos-latest
/macos-10.15
), or free self-hosted (self-hosted
) runners.
Finally, we use our step's run command (which defaults to bash) to run a multi-line script. In order to commit to our repo, we need to set our user.name
and user.email
via git config
, and we then source
our hugo.sh
, before running our bash functions. source
lets us use the environment variables we've set without exporting them.
name: 1-hugo-setup
on:
workflow_dispatch:
inputs:
site_title:
description: 'Site Title'
default: 'Hello, GopherCon!'
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: hugo.sh
env:
GITHUB_SHA: ${{ github.sha }}
SITE_TITLE: ${{ github.event.inputs.site_title }}
SITE_BASEURL: "/${{ github.event.repository.name }}/"
SITE_NAME: 1-hugo
run: |
git config --global user.name github-actions
git config --global user.email [email protected]
cd /home/
source $GITHUB_WORKSPACE/1-hugo/hugo.sh
hugo-install-linux
cd $GITHUB_WORKSPACE/
hugo-new-site
hugo-github-init
hugo-github-deploy
Our 1-hugo-deploy
action is very similar to the above, with the exception of the push trigger which we use to filter on a branch:
on:
push:
branches:
- gh-pages-content
In this brief workflow we've explored GitHub Actions workflows through a quite straightforward workflow that installs and runs a Go CLI tool and using bash scripts as part of our workflow.
Many of the steps we have may have their own, dedicated, Actions in the GitHub Marketplace.
For example, there are official actions for Go which provide more advanced functionality than running go build
using the version that is pre-installed on every runner. The community has created many Hugo actions you may find useful.
It can often be helpful to strike a balance between your existing tools and workflows, particularly those you use locally, and what gets run in the cloud. This is especially true when debugging. The debug loop is much tighter when you can hack on a bash script locally, and then push it to GitHub Actions knowing it will run the same as it does locally.
Finally, Go CLI tools are ideal for GitHub Actions workflows. Go binaries are very quick to install (Hugo takes single-digit seconds), and while Actions workflows also support both docker
and docker-compose
that can be great for complex build environments, Go binaries are often a solid alternative to packaging an environment as a container.
We will continue to explore similar workflows in our next labs covering Go in Serverless Functions and Containers in the Cloud.
- Hack on your site, configure, and modify your existing theme, or choose a new one.
- Is it a landing page? A resume? A blog? Documentation for your GitHub project?
- Could you integrate wjdp/htmltest into your workflow to check for broken links? You could do this via shell scripting, or perhaps an existing Action in the Marketplace.
- Consider how you might integrate more dynamic content into your site, or implement more advanced functionality such as publishing posts on a schedule.