Use a CentOS Docker Image to Build and Deploy a Hugo (Modules) Site to GitLab Pages

This was originally a blog post, but cross posting here in case useful for others.

Am wrapping up a project for a client that needed a static site. They requested that it be built with Hugo and deployed to GitLab pages. The theme they wanted to base it off uses Hugo Modules, which in turn requires a Golang installation.

I know there are existing docker images that have Hugo and Golang installed, but this particular project’s build is a bit complex (I’ve dumbed down the build script for this post), so I needed more flexibility.

I set things up so that when a commit is pushed to the master branch, a CI job is triggered. This job does the following:

  • Pulls a CentOS docker image
  • Installs curl, git, go, hugo
  • Builds the site
  • Deploys the site to GitLab Pages

Set GO_VERSION and HUGO_VERSION to your desired versions in .gitlab-ci.yml, then it’s ready to go.

Sample files are below.

.gitlab-ci.yml

image: centos:8

variables:
  GIT_SUBMODULE_STRATEGY: recursive
  GO_VERSION: "1.15.6"
  HUGO_VERSION: "0.78.2"

before_script:
  - yum install -y curl git
  - chmod +x task_ci_config.sh
  - chmod +x task_ci_install_go.sh
  - chmod +x task_ci_install_hugo.sh
  - chmod +x task_ci_build.sh
  - ./task_ci_install_go.sh $GO_VERSION
  - ./task_ci_install_hugo.sh $HUGO_VERSION

pages:
  stage: deploy
  script:
    - ./task_ci_build.sh
  artifacts:
    paths:
      - public
  only:
    - master

task_ci_config.sh

#!/usr/bin/env bash

export PATH=${PATH}:/usr/local/go/bin
export PATH=${PATH}:/usr/local

task_ci_install_go.sh

#!/usr/bin/env bash

USAGE="Usage:\n\n${0} VERSION\n"

if [[ -z ${1} ]]; then
  echo -e "${USAGE}"; exit 1
fi

source task_ci_config.sh

VERSION=${1}
TAR=go${VERSION}.linux-amd64.tar.gz
URL=https://golang.org/dl/${TAR}

curl -L -O ${URL}
tar -C /usr/local -xzf ${TAR}
go version

task_ci_install_hugo.sh

#!/usr/bin/env bash

USAGE="Usage:\n\n${0} VERSION\n"

if [[ -z ${1} ]]; then
  echo -e "${USAGE}"; exit 1
fi

source task_ci_config.sh

VERSION=${1}
TAR=hugo_extended_${VERSION}_Linux-64bit.tar.gz
URL=https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${TAR}

curl -L -O ${URL}
tar -C /usr/local -xzf ${TAR}
hugo version

task_ci_build.sh

#!/usr/bin/env bash

source task_ci_config.sh

hugo
3 Likes

Looking at the code (and not pretending to know how Gitlab and their CI works) I have the feeling, that this script installs all the tools every single time it runs, can that be? If they count the build minutes that might eat up the free tier quite fast. Is there a quick way to cache stuff?

Or is the image once it’s all installed saved and reused until you clear some cache on Gitlab?

As far as I know, one cannot cache custom installations in these CI tools.

A while back, I built a custom pipe to deploy a project on Netlify. Every single time the script was re-installing a specified Python tool. And yes, it did count these build minutes. In the end I moved away from that approach because I ended up in a situation with builds hanging or timing out.

For a setup like the one @zwbetz had to provide for, I would be looking into using a VPS rather than something like GitLab or Netlify, so that the Go and other tools installation is persistent.

1 Like

Netlify has build plugins now and :tada: there is a Hugo resources cache plugin:

This probably can be abused to save some stuff (like API responses). But I wouldn’t want to save programs in there. So a local or VPS docker instance is probably best.

1 Like

@davidsneighbour @alexandros - Fair points.

Looks like GitLab does offer caching for things like dependencies. I’ll have to look into that.

2 Likes

I tried that GitHub caching for projects with PHP and composer and they work great, as long as you load the cache and update at the proper times. It just puts away folders that you define and retrieves them. You could even share these cached tools between what Github calls “matrixes”, versions of things… like PHP versions and operating system versions. But I think that makes no sense (yet, maybe some day something like configuration comes up).

I love how much is possible in the modern web, if you have enough time to read all the documentations :slight_smile: