build #29386
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }} |