Skip to content

build

build #29375

Workflow file for this run

name: build
on:
schedule:
- cron: '*/30 * * * *'
issue_comment:
types: [created]
workflow_dispatch:
push:
paths:
- '.github/workflows/build.yml'
- 'docker/hugo/**'
concurrency:
group: "docker"
cancel-in-progress: false
env:
PLATFORMS: linux/amd64,linux/arm64
IMAGE_OWNER: hugomods
IMAGE_NAME: hugo
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
jobs:
build-and-push-image:
strategy:
fail-fast: false
matrix:
version: ['', 'std-'] # empty string and `std-` represent extended and standard version of Hugo.
user: ['root', 'hugo'] # default container user.
prefix:
- ''
- 'base-'
- 'ci-'
- 'dart-sass-'
- 'dart-sass-base-'
- 'dart-sass-git-'
- 'dart-sass-go-'
- 'dart-sass-go-git-'
- 'dart-sass-node-'
- 'dart-sass-node-git-'
- 'exts-'
- 'git-'
- 'go-'
- 'go-git-'
- 'node-'
- 'node-git-'
- 'node-lts-'
# Tags placeholders:
# 0: the image owner.
# 1: the image name.
# 2: the Hugo version without leading v.
# 3: standard (std-) or extended (empty string) indicator.
# 4: root user (empty string) or non-root user indicator.
include:
- version: ''
prefix: ''
user: 'root'
dockerfile: Dockerfile
tags: |
{0}/{1}:latest
{0}/{1}:{2}
ghcr.io/{0}/{1}:latest
ghcr.io/{0}/{1}:{2}
- version: ''
prefix: ''
user: 'hugo'
dockerfile: Dockerfile
tags: |
{0}/{1}:non-root
{0}/{1}:non-root-{2}
ghcr.io/{0}/{1}:non-root
ghcr.io/{0}/{1}:non-root-{2}
- version: 'std-'
prefix: ''
dockerfile: Dockerfile
tags: |
{0}/{1}:std{4}
{0}/{1}:std{4}-{2}
ghcr.io/{0}/{1}:std{4}
ghcr.io/{0}/{1}:std{4}-{2}
{0}/{1}:reg{4}
{0}/{1}:reg{4}-{2}
ghcr.io/{0}/{1}:reg{4}
ghcr.io/{0}/{1}:reg{4}-{2}
- prefix: base-
dockerfile: Dockerfile-base
tags: |
{0}/{1}:{3}base{4}
{0}/{1}:{3}base{4}-{2}
ghcr.io/{0}/{1}:{3}base{4}
ghcr.io/{0}/{1}:{3}base{4}-{2}
- prefix: ci-
dockerfile: Dockerfile-ci
tags: |
{0}/{1}:{3}ci{4}
{0}/{1}:{3}ci{4}-{2}
ghcr.io/{0}/{1}:{3}ci{4}
ghcr.io/{0}/{1}:{3}ci{4}-{2}
- prefix: dart-sass-
dockerfile: Dockerfile-dart-sass
tags: |
{0}/{1}:{3}dart-sass{4}
{0}/{1}:{3}dart-sass{4}-{2}
ghcr.io/{0}/{1}:{3}dart-sass{4}
ghcr.io/{0}/{1}:{3}dart-sass{4}-{2}
- prefix: dart-sass-base-
dockerfile: Dockerfile-dart-sass-base
tags: |
{0}/{1}:{3}dart-sass-base{4}
{0}/{1}:{3}dart-sass-base{4}-{2}
ghcr.io/{0}/{1}:{3}dart-sass-base{4}
ghcr.io/{0}/{1}:{3}dart-sass-base{4}-{2}
- prefix: dart-sass-git-
dockerfile: Dockerfile-dart-sass-git
tags: |
{0}/{1}:{3}dart-sass-git{4}
{0}/{1}:{3}dart-sass-git{4}-{2}
ghcr.io/{0}/{1}:{3}dart-sass-git{4}
ghcr.io/{0}/{1}:{3}dart-sass-git{4}-{2}
- prefix: dart-sass-go-
dockerfile: Dockerfile-dart-sass-go
tags: |
{0}/{1}:{3}dart-sass-go{4}
{0}/{1}:{3}dart-sass-go{4}-{2}
ghcr.io/{0}/{1}:{3}dart-sass-go{4}
ghcr.io/{0}/{1}:{3}dart-sass-go{4}-{2}
- prefix: dart-sass-go-git-
dockerfile: Dockerfile-dart-sass-go-git
tags: |
{0}/{1}:{3}dart-sass-go-git{4}
{0}/{1}:{3}dart-sass-go-git{4}-{2}
ghcr.io/{0}/{1}:{3}dart-sass-go-git{4}
ghcr.io/{0}/{1}:{3}dart-sass-go-git{4}-{2}
- prefix: dart-sass-node-
dockerfile: Dockerfile-dart-sass-node
tags: |
{0}/{1}:{3}dart-sass-node{4}
{0}/{1}:{3}dart-sass-node{4}-{2}
ghcr.io/{0}/{1}:{3}dart-sass-node{4}
ghcr.io/{0}/{1}:{3}dart-sass-node{4}-{2}
- prefix: dart-sass-node-git-
dockerfile: Dockerfile-dart-sass-node-git
tags: |
{0}/{1}:{3}dart-sass-node-git{4}
{0}/{1}:{3}dart-sass-node-git{4}-{2}
ghcr.io/{0}/{1}:{3}dart-sass-node-git{4}
ghcr.io/{0}/{1}:{3}dart-sass-node-git{4}-{2}
- prefix: git-
dockerfile: Dockerfile-git
tags: |
{0}/{1}:{3}git{4}
{0}/{1}:{3}git{4}-{2}
ghcr.io/{0}/{1}:{3}git{4}
ghcr.io/{0}/{1}:{3}git{4}-{2}
- prefix: go-
dockerfile: Dockerfile-go
tags: |
{0}/{1}:{3}go{4}
{0}/{1}:{3}go{4}-{2}
ghcr.io/{0}/{1}:{3}go{4}
ghcr.io/{0}/{1}:{3}go{4}-{2}
- prefix: go-git-
dockerfile: Dockerfile-go-git
tags: |
{0}/{1}:{3}go-git{4}
{0}/{1}:{3}go-git{4}-{2}
ghcr.io/{0}/{1}:{3}go-git{4}
ghcr.io/{0}/{1}:{3}go-git{4}-{2}
- prefix: node-
dockerfile: Dockerfile-node
tags: |
{0}/{1}:{3}node{4}
{0}/{1}:{3}node{4}-{2}
ghcr.io/{0}/{1}:{3}node{4}
ghcr.io/{0}/{1}:{3}node{4}-{2}
- prefix: node-git-
dockerfile: Dockerfile-node-git
tags: |
{0}/{1}:{3}node-git{4}
{0}/{1}:{3}node-git{4}-{2}
ghcr.io/{0}/{1}:{3}node-git{4}
ghcr.io/{0}/{1}:{3}node-git{4}-{2}
- prefix: node-lts-
dockerfile: Dockerfile-node-lts
tags: |
{0}/{1}:{3}node-lts{4}
{0}/{1}:{3}node-lts{4}-{2}
ghcr.io/{0}/{1}:{3}node-lts{4}
ghcr.io/{0}/{1}:{3}node-lts{4}-{2}
- prefix: exts-
dockerfile: Dockerfile-exts
tags: |
{0}/{1}:{3}exts{4}
{0}/{1}:{3}exts{4}-{2}
ghcr.io/{0}/{1}:{3}exts{4}
ghcr.io/{0}/{1}:{3}exts{4}-{2}
# build tags
- version: ''
build_tags: 'extended'
- version: ''
prefix: 'ci-'
build_tags: 'extended,withdeploy'
- version: 'std-'
prefix: 'ci-'
build_tags: 'withdeploy'
runs-on: ubuntu-latest
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.issue.number == 3 }}
steps:
- name: Fetch latest Hugo release
id: release
uses: razonyang/github-action-github-latest-release@v1
with:
owner: gohugoio
name: hugo
prefix: v
- name: Check if the image tag exists
if: steps.release.outputs.version
id: check-image-tag
uses: razonyang/github-action-docker-image-tag-exists@v1
with:
owner: ${{ env.IMAGE_OWNER }}
name: ${{ env.IMAGE_NAME }}
tag: ${{ matrix.version }}${{ matrix.prefix }}${{ steps.release.outputs.version }}
# The `build` is `true` on when the image tag doesn't exist or the commit message contains the word `[build]`.
- id: needs-build
if: steps.release.outputs.version
name: Check if there is a need to build images.
run: |
if [[ ${{ steps.check-image-tag.outputs.exists }} == 0 || "${{ github.event.head_commit.message }}" == *"[build]"* ]]; \
then echo "build=1"; \
else echo "build=0"; \
fi \
>> $GITHUB_OUTPUT;
# Checkout the main repository.
- name: Checkout repository
if: steps.needs-build.outputs.build == true
uses: actions/checkout@v4
# Checkout the Hugo repository for building.
- name: Checkout Hugo repository
if: steps.needs-build.outputs.build == true
uses: actions/checkout@v4
with:
repository: gohugoio/hugo
ref: v${{ steps.release.outputs.version }}
path: ./docker/hugo/src
- name: Build meta
id: build_meta
if: steps.needs-build.outputs.build == true
run: |
echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
cd ./docker/hugo/src && echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
- name: LDFLAGS
id: ldflags
if: steps.needs-build.outputs.build == true
run: |
HASH='github.com/gohugoio/hugo/common/hugo.commitHash=${{ steps.build_meta.outputs.hash }}'
BUILD_DATE='github.com/gohugoio/hugo/common/hugo.buildDate=${{ steps.build_meta.outputs.date }}'
VENDOR='github.com/gohugoio/hugo/common/hugo.vendorInfo=hugomods'
echo "-X '$HASH' -X '$BUILD_DATE' -X '$VENDOR'"
echo "flags=-X '$HASH' -X '$BUILD_DATE' -X '$VENDOR'">> $GITHUB_OUTPUT
- name: Set up QEMU
if: steps.needs-build.outputs.build == true
uses: docker/setup-qemu-action@v3
with:
platforms: ${{ env.PLATFORMS }}
- name: Set up Docker Buildx
if: steps.needs-build.outputs.build == true
uses: docker/setup-buildx-action@v3
with:
platforms: ${{ env.PLATFORMS }}
- name: Build and export to Docker
if: steps.needs-build.outputs.build == true
uses: docker/build-push-action@v5
with:
load: true
context: docker/hugo
file: docker/hugo/${{ matrix.dockerfile }}
tags: hugomods/hugo:test
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
LDFLAGS=${{ steps.ldflags.outputs.flags }}
HUGO_VERSION=v${{ steps.release.outputs.version }}
BUILD_TAGS=${{ matrix.build_tags }}
USER=${{ matrix.user }}
- id: hugo-version
name: Save Hugo version for subsequent tests.
if: steps.needs-build.outputs.build == true
run: echo "version=$(docker run --rm hugomods/hugo:test hugo version)" >> $GITHUB_OUTPUT
- name: Check Hugo version
if: steps.needs-build.outputs.build == true
run: |
if [[ "${{ steps.hugo-version.outputs.version }}" != *"v${{ steps.release.outputs.version }}"* ]];
then
echo "Expected Hugo version "v${{ steps.release.outputs.version }}", but got "${{ steps.hugo-version.outputs.version }}"." && exit 1;
fi
- name: Check Hugo edition
if: steps.needs-build.outputs.build == true
run: |
if [[ "${{ matrix.version }}" == "std-" && "${{ steps.hugo-version.outputs.version }}" == *"extended"* ]];
then
echo "Expected standard edition of Hugo, got "${{ steps.hugo-version.outputs.version }}"." && exit 1;
elif [[ "${{ matrix.version }}" == "" && "${{ steps.hugo-version.outputs.version }}" != *"extended"* ]]
then
echo "Expected extended edition of Hugo, got "${{ steps.hugo-version.outputs.version }}"." && exit 1;
fi
- name: Check Vendor Info
if: steps.needs-build.outputs.build == true
run: |
if [[ "${{ steps.hugo-version.outputs.version }}" != *"VendorInfo=hugomods"* ]];
then
echo "Expected vendor "hugomods", got "${{ steps.hugo-version.outputs.version }}"." && exit 1;
fi
- name: Check working directory
if: steps.needs-build.outputs.build == true
run: |
TEST_PWD=$(docker run --rm -v $PWD/site:/src hugomods/hugo:test pwd)
if [[ "$TEST_PWD" != "/src" ]];
then
echo "Expected working directory \"/src\", got \"$TEST_PWD\"." && exit 1;
fi
- name: Check /src ownership
if: steps.needs-build.outputs.build == true
run: |
SRC_OWNER=$(docker run --rm hugomods/hugo:test stat -c "%U:%G" /src)
if [[ "$SRC_OWNER" != "hugo:hugo" ]];
then
echo "Expected the owner of \"/src\" to be "hugo:hugo", got \"$SRC_OWNER\"." && exit 1;
fi
- name: Check default user
if: steps.needs-build.outputs.build == true
run: |
TEST_WHOAMI=$(docker run --rm -v $PWD/site:/src hugomods/hugo:test whoami)
if [[ "$TEST_WHOAMI" != "${{ matrix.user }}" ]];
then
echo "Expected default user \"${{ matrix.user }}\", got \"$TEST_WHOAMI\"." && exit 1;
fi
- name: Check the hugo user ID
if: steps.needs-build.outputs.build == true
run: |
TEST_UID=$(docker run --rm -v $PWD/site:/src hugomods/hugo:test id -u hugo)
if [[ "$TEST_UID" != "1000" ]];
then
echo "Expected UID \"1000\", got \"$TEST_UID\"." && exit 1;
fi
- name: Check the hugo user group ID
if: steps.needs-build.outputs.build == true
run: |
TEST_GID=$(docker run --rm -v $PWD/site:/src hugomods/hugo:test id -g hugo)
if [[ "$TEST_GID" != "1000" ]];
then
echo "Expected GID \"1000\", got \"$TEST_GID\"." && exit 1;
fi
- name: Change permission of site to build with non-root image
if: steps.needs-build.outputs.build == true && matrix.user == 'hugo'
run: chmod a+w $PWD/site
- name: Test site build
if: steps.needs-build.outputs.build == true
run: |
docker run --rm -v $PWD/site:/src hugomods/hugo:test hugo
env:
IMAGE_PREFIX: ${{ matrix.prefix }}
- name: Test Extra Markdown Handlers
if: steps.needs-build.outputs.build == true &&
contains(fromJson('["ci-", "exts-"]'), matrix.prefix)
run: |
docker run --rm -v $PWD/site:/src hugomods/hugo:test hugo --config config.yaml,config-markdown-handlers.yaml
env:
IMAGE_PREFIX: ${{ matrix.prefix }}
- name: Check Git installation
if: |
steps.needs-build.outputs.build == true &&
contains(fromJson('["", "ci-", "dart-sass-", "dart-sass-git-", "dart-sass-go-git-", "dart-sass-node-git-", "git-", "node-git-", "go-git-", "exts-"]'), matrix.prefix)
run: docker run --rm hugomods/hugo:test git version
- name: Test site build with --enableGitInfo
if: |
steps.needs-build.outputs.build == true &&
contains(fromJson('["", "ci-", "dart-sass-", "dart-sass-git-", "dart-sass-go-git-", "dart-sass-node-git-", "git-", "node-git-", "go-git-", "exts-"]'), matrix.prefix)
run: >-
docker run --rm
-v $PWD/site:/src
hugomods/hugo:test
/bin/sh -c "
if [[ "${{ matrix.user }}" == "hugo" ]]; then git config --global --add safe.directory /src; fi &&
git init &&
git add -A &&
git config --global user.email "support@hugomods.com" &&
git config --global user.name "HugoMods" &&
git commit -m 'Test commit' &&
hugo --enableGitInfo"
- name: Check Hugo server binding
if: steps.needs-build.outputs.build == true
run: >-
HUGO_SERVER_OUTPUT=$(docker run --rm -v $PWD/site:/src hugomods/hugo:test sh -c "server > server.out & sleep 5 && cat server.out") &&
echo $HUGO_SERVER_OUTPUT &&
if [[ $HUGO_SERVER_OUTPUT != *"bind address 0.0.0.0"* ]]; then echo "Expected bind "0.0.0.0" by default." && exit 1; fi
env:
IMAGE_PREFIX: ${{ matrix.prefix }}
- name: Check Go installation
if: |
steps.needs-build.outputs.build == true &&
contains(fromJson('["", "ci-", "dart-sass-", "dart-sass-go-", "dart-sass-go-git-", "go-", "go-git-", "exts-"]'), matrix.prefix)
run: docker run --rm hugomods/hugo:test go version
- name: Check Node.js, NPM and Yarn installation
if: |
steps.needs-build.outputs.build == true &&
contains(fromJson('["", "ci-", "dart-sass-", "dart-sass-node-", "dart-sass-node-git-", "node-", "node-git-", "node-lts-", "exts-"]'), matrix.prefix)
run: docker run --rm hugomods/hugo:test node -v && npm version && yarn -v
- name: Check PostCSS, PurgeCSS, Autoprefixer and RTLCSS
if: |
steps.needs-build.outputs.build == true &&
contains(fromJson('["ci-", "exts-"]'), matrix.prefix)
run: >-
docker run --rm
-v $PWD/tests/postcss:/src
hugomods/hugo:test
postcss ./main.css
- name: Check CI tools
if: |
steps.needs-build.outputs.build == true &&
contains(fromJson('["ci-"]'), matrix.prefix)
run: >-
docker run --rm
hugomods/hugo:test
sh -c "hugo deploy -h &&
curl --version &&
jq --version &&
htmlproofer -v"
- name: Login to Docker Hub
if: ${{ steps.needs-build.outputs.build == true && env.BRANCH_NAME == 'main' }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: ${{ steps.needs-build.outputs.build == true && env.BRANCH_NAME == 'main' }}
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ env.IMAGE_OWNER }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: User tag suffix
id: user-tag
if: ${{ steps.needs-build.outputs.build == true }}
run: |
if [[ ${{ matrix.user }} == "hugo" ]]; \
then echo "suffix=-non-root"; \
else echo "suffix="; \
fi \
>> $GITHUB_OUTPUT;
- name: Build and push
if: steps.needs-build.outputs.build == true
uses: docker/build-push-action@v5
with:
push: ${{ env.BRANCH_NAME == 'main' }}
context: docker/hugo
platforms: ${{ env.PLATFORMS }}
file: docker/hugo/${{ matrix.dockerfile }}
# Reserve reg* variants for backwards compatibility.
tags: >
${{
(matrix.version == 'std-' && matrix.prefix != '') &&
format(
'{0}{1}',
format(matrix.tags, env.IMAGE_OWNER, env.IMAGE_NAME, steps.release.outputs.version, matrix.version, steps.user-tag.outputs.suffix),
format(matrix.tags, env.IMAGE_OWNER, env.IMAGE_NAME, steps.release.outputs.version, 'reg-', steps.user-tag.outputs.suffix)
) ||
format(matrix.tags, env.IMAGE_OWNER, env.IMAGE_NAME, steps.release.outputs.version, matrix.version, steps.user-tag.outputs.suffix)
}}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
LDFLAGS=${{ steps.ldflags.outputs.flags }}
HUGO_VERSION=v${{ steps.release.outputs.version }}
BUILD_TAGS=${{ matrix.build_tags }}
USER=${{ matrix.user }}