Deploying Hugo to GitHub Pages with GitHub Actions

#GitHub#GitHub Actions#Hugo#GitHub Pages

Tim Kye

There are a lot of strategies to hosting in-repo docs on GitHub Pages (as opposed to a dedicated repo for docs). I'm going to cover how to publish docs that are stored on the master branch to the gh-pages branch.

This approach has several benefits

  • Only the Hugo "source" files live in master, while gh-pages contains the built HTML, etc. This keeps master clean and lean, and makes PR diffs much easier to review.
  • Code and docs both live on master, making it simple to ensure changes go out at the same time. Pull Requests that change code can also update docs, instead of having separate PRs for code and docs.
  • No need to explain to external contributors where doc PRs go: everything is on master.
  • GitHub Actions workflow files all live in master, instead of living on a docs branch (or on gh-pages, where your built artifacts live).

I am going to be using Hugo along with the excellent Learn theme, but for the most part any system that generates static HTML sites can be used.

The Plan

  • Create a /docsdirectory in the repo root to hold Hugo content
  • Setup a Hugo theme using Git submodules
  • Setup a Makefile to run Hugo locally
  • Add a GitHub Actions workflow that will run on changes to /docs that builds Hugo and pushes the output to the gh-pages

Setup Hugo

First install Hugo. If you are on a mac just use Homebrew. Otherwise checkout their installation docs.

brew install hugo

To create the /docs folder, initialize a Hugo site, and install the theme run:

hugo new site docs
cd docs/themes
git submodule add https://github.com/matcornic/hugo-theme-learn learn

To finish the Hugo setup edit the docs/config.toml to include the following

baseURL = "https://{YOUR_GITHUB_USERNAME}.github.io/{YOUR_GITHUB_REPO}"
languageCode = "en-us"
title = "{YOUR_GITHUB_REPO}"
theme = "learn"
RelativeURLs=true
CanonifyURLs=true

[params]
  pygmentsCodeFences = true
  pygmentsStyle="monokai"
    editURL = ""
  author = "{YOUR_GITHUB_USERNAME}"
  description = "Documentation for {YOUR_GITHUB_REPO}"
  showVisitedLinks = false
  disableSearch = false
  disableAssetsBusting = false
  disableInlineCopyToClipBoard = false
  disableShortcutsTitle = false
  disableLanguageSwitchingButton = false
  disableBreadcrumb = true
  disableNextPrev = false
  ordersectionsby = "weight"
  themeVariant = "green"

[[menu.shortcuts]] 
  name = "<i class='fab fa-github'></i> Github repo"
  identifier = "ds"
  url = "https://github.com/{YOUR_GITHUB_USERNAME}/{YOUR_GITHUB_REPO}"
  weight = 10

[outputs]
  home = [ "HTML", "RSS", "JSON"]

For more information on the above settings see the learn configuration docs.

Create Documentation Content

I'm not going to cover this here, because the learn docs are good enough. I will note though that currently markdown code fences (the triple-backticks) don't seem to work. The highlight shortcodes do, though.

Setup Makefile

To make building and running locally easier add a Makefile to your project root.

run-docs: ## Run in development mode
  cd docs && hugo serve -D

docs: ## Build the site
  cd docs && hugo -t learn -d public --gc --minify --cleanDestinationDir

You can run the above commands with make run-docs and make docs.

If you aren't familiar with make its basically npm run <script>. Except its decades older, faster, composes easily, and works on every system with a shell (so... not Windows) without installing anything.

This is probably a good time to stop and check that your site builds correctly and looks the way you want it to.

Create the GitHub Action

We want changes to the /docs directory to result in Hugo building and pushing the resulting /public directory to the root of the gh-pages branch. There are a few ways to do this, but GitHub user peaceiris has wrapped up the nicest one into two ready-to-use GitHub actions: actions-hugo (to setup the Hugo cli) actions-gh-pages (to handle pushing to the *gh-pages *branch).

Create a .github/workflows/docs.yaml with the following.

name: Publish Docs

on:
  push:
    branches:
      - master
    paths:
      - 'docs/**'

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v1
      - name: Checkout
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2.2.1
        with:
          hugo-version: '0.58.3'
      - name: Prepare Hugo
        run: |
          git submodule sync && git submodule update --init
      - name: Build
        run: make docs
      - name: add nojekyll
        run: touch ./docs/public/.nojekyll
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v2.4.0
        with:
          emptyCommits: false
        env:
          ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}
          PUBLISH_BRANCH: gh-pages
          PUBLISH_DIR: ./docs/public

This almost works out of the box. Unfortunately we can't use GitHub Actions's default GITHUB_TOKEN here, so we need to setup a repository deploy key to authorize pushing to the gh-pages branch from inside the workflow.

To do this go to Settings > Deploy Keys > Add Deploy Key

Copy the key and then add it to Settings > Secrets.

With all of this in place you are ready to commit to master. This should kick off the publish workflow, which you can view under Actions > Publish Docs.

Turn on GitHub Pages

Finally, enable GitHub pages from your repository **settings **page by selecting gh-pages branch on the Source dropdown.

Once you've done that you should get a link to your new GitHub Pages deployment!

You can see the GitHub Pages I built for raviger here.