Closed #33: add multi-architecture support, but the exts support amd64 only.
Fixed #34: override the build date, commit hash and vendor info.
Fixed #36: push images to multiple registries in same job.
Trigger the workflow every 10 mins.
This commit is contained in:
Razon Yang 2023-05-30 00:10:49 +08:00 committed by razonyang
parent 51ce6436f9
commit 4204ca78d6
24 changed files with 244 additions and 127 deletions

View file

@ -2,7 +2,7 @@ name: build
on:
schedule:
- cron: '*/30 * * * *'
- cron: '*/10 * * * *'
issue_comment:
types: [created]
workflow_dispatch:
@ -13,43 +13,71 @@ on:
concurrency:
group: "docker"
cancel-in-progress: false
env:
PLATFORMS: linux/amd64,linux/arm64
IMAGE_OWNER: hugomods
IMAGE_NAME: hugo
jobs:
build-and-push-image:
strategy:
fail-fast: false
matrix:
registry: ['', 'ghcr.io']
version: ['', 'reg-']
prefix: ['', 'base-', 'go-', 'node-', 'exts-']
# Tags placeholders:
# 0: the image owner.
# 1: the image name.
# 2: the Hugo version without leading v.
# 3: regular (reg-) or extended (empty string) indicator.
include:
- version: ''
gcc: true
extended: true
- registry: ''
user: hugomods
- registry: 'ghcr.io'
user: ghcr.io/hugomods
- version: ''
prefix: ''
dockerfile: docker/default/Dockerfile
tags: '{0}/hugo:latest,{0}/hugo:{1}'
dockerfile: Dockerfile
tags: |
{0}/{1}:latest
{0}/{1}:{2}
ghcr.io/{0}/{1}:latest
ghcr.io/{0}/{1}:{2}
- version: 'reg-'
prefix: ''
dockerfile: docker/default/Dockerfile
tags: '{0}/hugo:reg,{0}/hugo:reg-{1}'
dockerfile: Dockerfile
tags: |
{0}/{1}:reg
{0}/{1}:reg-{2}
ghcr.io/{0}/{1}:reg
ghcr.io/{0}/{1}:reg-{2}
- prefix: base-
dockerfile: docker/base/Dockerfile
tags: '{0}/hugo:{2}base,{0}/hugo:{3}base-{1}'
dockerfile: Dockerfile-base
tags: |
{0}/{1}:{3}base
{0}/{1}:{3}base-{2}
ghcr.io/{0}/{1}:{3}base
ghcr.io/{0}/{1}:{3}base-{2}
- prefix: go-
dockerfile: docker/go/Dockerfile
tags: '{0}/hugo:{2}go,{0}/hugo:{3}go-{1}'
dockerfile: Dockerfile-go
tags: |
{0}/{1}:{3}go
{0}/{1}:{3}go-{2}
ghcr.io/{0}/{1}:{3}go
ghcr.io/{0}/{1}:{3}go-{2}
- prefix: node-
dockerfile: docker/node/Dockerfile
tags: '{0}/hugo:{2}node,{0}/hugo:{3}node-{1}'
dockerfile: Dockerfile-node
tags: |
{0}/{1}:{3}node
{0}/{1}:{3}node-{2}
ghcr.io/{0}/{1}:{3}node
ghcr.io/{0}/{1}:{3}node-{2}
- prefix: exts-
dockerfile: docker/exts/Dockerfile
tags: '{0}/hugo:{2}exts,{0}/hugo:{3}exts-{1}'
dockerfile: Dockerfile-exts
tags: |
{0}/{1}:{3}exts
{0}/{1}:{3}exts-{2}
ghcr.io/{0}/{1}:{3}exts
ghcr.io/{0}/{1}:{3}exts-{2}
runs-on: ubuntu-latest
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.issue.number == 3 }}
steps:
@ -61,97 +89,121 @@ jobs:
if: steps.release.outputs.version != 'ull'
id: check
run: |
if [[ -z "${{ matrix.registry }}" ]];
then
curl -s \
-- url https://hub.docker.com/v2/namespaces/hugomods/repositories/hugo/tags/${{ matrix.version }}${{ matrix.prefix }}${{ steps.release.outputs.version }} \
| jq 'has("name")' \
| awk '{print "exists="$1}' \
>> $GITHUB_OUTPUT;
else
curl -s \
--url https://ghcr.io/v2/hugomods/hugo/tags/list \
--header 'authorization: Bearer ${{ secrets.GHCR_TOKEN }}' \
--header 'content-type: application/json' \
| jq '.tags | index("${{ matrix.version }}${{ matrix.prefix }}${{ steps.release.outputs.version }}") >= 0' \
| awk '{print "exists="$1}' \
>> $GITHUB_OUTPUT;
fi;
curl -s \
-- url https://hub.docker.com/v2/namespaces/${{ env.IMAGE_OWNER }}/repositories/${{ env.IMAGE_NAME }}/tags/${{ matrix.version }}${{ matrix.prefix }}${{ steps.release.outputs.version }} \
| jq 'has("name")' \
| awk '{print "exists="$1}' \
>> $GITHUB_OUTPUT;
# Checkout the main repository.
- name: Checkout repository
if: steps.check.outputs.exists == 'false'
uses: actions/checkout@v3
# Checkout the Hugo repository for building.
- name: Checkout Hugo repository
if: steps.check.outputs.exists == 'false'
uses: actions/checkout@v3
with:
repository: gohugoio/hugo
ref: v${{ steps.release.outputs.version }}
path: ./docker/hugo/src
- name: Build meta
id: build_meta
if: steps.check.outputs.exists == 'false'
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.check.outputs.exists == 'false'
run: |
echo "hash=-X github.com/gohugoio/hugo/common/hugo.commitHash=${{ steps.build_meta.outputs.hash }}" >> $GITHUB_OUTPUT
echo "builddate=-X github.com/gohugoio/hugo/common/hugo.buildDate=${{ steps.build_meta.outputs.date }}" >> $GITHUB_OUTPUT
echo "vendor=-X github.com/gohugoio/hugo/common/hugo.vendorInfo=hugomods" >> $GITHUB_OUTPUT
- name: Set up QEMU
if: steps.check.outputs.exists == 'false'
uses: docker/setup-qemu-action@v2
with:
platforms: ${{ env.PLATFORMS }}
- name: Set up Docker Buildx
if: steps.check.outputs.exists == 'false'
uses: docker/setup-buildx-action@v2
with:
driver: docker
platforms: ${{ env.PLATFORMS }}
- name: Login to Docker Hub
if: steps.check.outputs.exists == 'false'
uses: docker/login-action@v2
with:
registry: ${{ matrix.registry }}
username: ${{ matrix.registry == '' && 'hugomods' || github.actor }}
password: ${{ matrix.registry == '' && secrets.DOCKERHUB_TOKEN || secrets.GITHUB_TOKEN }}
- name: Build the Alpine image
username: ${{ env.IMAGE_OWNER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: steps.check.outputs.exists == 'false'
uses: docker/build-push-action@v4
uses: docker/login-action@v2
with:
context: ./docker/alpine
file: ./docker/alpine/Dockerfile
load: true
tags: hugo-alpine:latest
build-args: |
GCC=${{ matrix.gcc }}
- name: Build the builder image
if: steps.check.outputs.exists == 'false'
uses: docker/build-push-action@v4
with:
context: ./docker/builder
file: ./docker/builder/Dockerfile
load: true
tags: hugo-builder:latest
build-args: |
HUGO_VERSION=v${{ steps.release.outputs.version }}
HUGO_EXTENDED=${{ matrix.extended }}
registry: ghcr.io
username: ${{ env.IMAGE_OWNER }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and export to Docker
if: steps.check.outputs.exists == 'false'
uses: docker/build-push-action@v4
with:
load: true
context: .
file: ${{ matrix.dockerfile }}
tags: hugomods/hugo:${{ matrix.version }}${{ matrix.prefix }}test
context: docker/hugo
file: docker/hugo/${{ matrix.dockerfile }}
tags: ${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ matrix.version }}${{ matrix.prefix }}test
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
GCC=${{ matrix.gcc }}
build-contexts: |
alpine=docker-image://hugo-alpine:latest
builder=docker-image://hugo-builder:latest
LDFLAGS="${{ steps.ldflags.outputs.hash }} ${{ steps.ldflags.outputs.builddate }} ${{ steps.ldflags.outputs.vendor }}"
HUGO_VERSION=v${{ steps.release.outputs.version }}
HUGO_EXTENDED=${{ matrix.extended }}
- name: Test
- name: Test site build
if: steps.check.outputs.exists == 'false'
run: |
docker run --rm -v $PWD/site:/src hugomods/hugo:${{ matrix.version }}${{ matrix.prefix }}test hugo
docker run --rm -v $PWD/site:/src ${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ matrix.version }}${{ matrix.prefix }}test hugo
env:
IMAGE_PREFIX: ${{ matrix.prefix }}
- name: Check Git installation
if: |
steps.check.outputs.exists == 'false' &&
contains(fromJson('["", "exts-"]'), matrix.prefix)
run: docker run --rm ${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ matrix.version }}${{ matrix.prefix }}test git version
- name: Check Go installation
if: |
steps.check.outputs.exists == 'false' &&
contains(fromJson('["", "go-", "exts-"]'), matrix.prefix)
run: docker run --rm ${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ matrix.version }}${{ matrix.prefix }}test go version
- name: Check Node.js and NPM installation
if: |
steps.check.outputs.exists == 'false' &&
contains(fromJson('["", "node-", "exts-"]'), matrix.prefix)
run: docker run --rm ${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ matrix.version }}${{ matrix.prefix }}test node -v && npm version
- name: Build and push
if: steps.check.outputs.exists == 'false'
uses: docker/build-push-action@v4
with:
push: true
context: .
file: ${{ matrix.dockerfile }}
tags: ${{ format(matrix.tags, matrix.user, steps.release.outputs.version, matrix.version, matrix.version) }}
context: docker/hugo
# Alpine ARM64 arch does not support embedded Dart SASS.
platforms: ${{ matrix.prefix == 'exts-' && 'linux/amd64' || env.PLATFORMS }}
file: docker/hugo/${{ matrix.dockerfile }}
tags: ${{ format(matrix.tags, env.IMAGE_OWNER, env.IMAGE_NAME, steps.release.outputs.version, matrix.version) }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
GCC=${{ matrix.gcc }}
build-contexts: |
alpine=docker-image://hugo-alpine:latest
builder=docker-image://hugo-builder:latest
LDFLAGS="${{ steps.ldflags.outputs.hash }} ${{ steps.ldflags.outputs.builddate }} ${{ steps.ldflags.outputs.vendor }}"
HUGO_VERSION=v${{ steps.release.outputs.version }}
HUGO_EXTENDED=${{ matrix.extended }}

View file

@ -1,4 +0,0 @@
FROM alpine
ARG GCC
RUN apk add --no-cache ca-certificates
RUN if [[ -n "$GCC" ]]; then apk add --no-cache gcc; fi

View file

@ -1,5 +0,0 @@
# syntax=docker/dockerfile:1
FROM alpine
COPY --from=builder /go/bin/hugo /usr/bin/hugo
WORKDIR /src
CMD hugo env

View file

@ -1,5 +0,0 @@
FROM golang:alpine
ARG HUGO_VERSION=latest
ARG HUGO_EXTENDED=
RUN apk add --no-cache gcc g++ musl-dev
RUN if [[ -z "$HUGO_EXTENDED" ]]; then go install github.com/gohugoio/hugo@${HUGO_VERSION}; else go install -tags extended github.com/gohugoio/hugo@${HUGO_VERSION}; fi

View file

@ -1,8 +0,0 @@
# syntax=docker/dockerfile:1
FROM node:alpine as node
COPY --from=builder /usr/local/go /usr/local/go
RUN ln -s /usr/local/go/bin/go /usr/local/bin/go
COPY --from=builder /go/bin/hugo /usr/local/bin/hugo
RUN apk add --no-cache git
WORKDIR /src
CMD hugo env

View file

@ -1,10 +0,0 @@
# syntax=docker/dockerfile:1
FROM node:alpine as node
COPY --from=builder /usr/local/go /usr/local/go
RUN ln -s /usr/local/go/bin/go /usr/local/bin/go
COPY --from=builder /go/bin/hugo /usr/local/bin/hugo
RUN apk add dart-sass-embedded --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/
RUN apk add --no-cache dart-sass-embedded git
RUN npm i -g postcss-cli autoprefixer @fullhuman/postcss-purgecss rtlcss
WORKDIR /src
CMD hugo env

View file

@ -1,8 +0,0 @@
# syntax=docker/dockerfile:1
FROM golang:alpine
ARG GCC
RUN apk add --no-cache ca-certificates
RUN if [[ -n "$GCC" ]]; then apk add --no-cache gcc; fi
COPY --from=builder /go/bin/hugo /usr/local/bin/hugo
WORKDIR /src
CMD hugo env

10
docker/hugo/Dockerfile Normal file
View file

@ -0,0 +1,10 @@
# syntax = edrevo/dockerfile-plus
INCLUDE+ ./Dockerfile-builder
FROM node:alpine as node
INCLUDE+ ./snippets/go
INCLUDE+ ./snippets/git
INCLUDE+ ./snippets/common

View file

@ -0,0 +1,6 @@
# syntax = edrevo/dockerfile-plus
INCLUDE+ ./Dockerfile-builder
FROM alpine
INCLUDE+ ./snippets/common

View file

@ -0,0 +1,31 @@
FROM golang:alpine as builder
ARG HUGO_VERSION
ARG HUGO_EXTENDED
ARG GOPROXY
ARG TARGETOS
ARG TARGETARCH
ENV GOOS=$TARGETOS
ENV GOARCH=$TARGETARCH
# install build tools.
RUN apk add --no-cache gcc g++ musl-dev git
WORKDIR /src
# download modules.
COPY src .
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
go mod download
# build Hugo.
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
if [[ -z "$HUGO_EXTENDED" ]]; \
then \
go build -ldflags="$LDFLAGS" -o /usr/bin/hugo; \
else \
go build -ldflags="$LDFLAGS" -tags extended -o /usr/bin/hugo; \
fi

View file

@ -0,0 +1,12 @@
# syntax = edrevo/dockerfile-plus
INCLUDE+ ./Dockerfile-builder
FROM node:alpine as node
INCLUDE+ ./snippets/go
INCLUDE+ ./snippets/git
INCLUDE+ ./snippets/exts
INCLUDE+ ./snippets/common

View file

@ -0,0 +1,6 @@
# syntax = edrevo/dockerfile-plus
INCLUDE+ ./Dockerfile-builder
FROM golang:alpine
INCLUDE+ ./snippets/common

View file

@ -0,0 +1,6 @@
# syntax = edrevo/dockerfile-plus
INCLUDE+ ./Dockerfile-builder
FROM node:alpine
INCLUDE+ ./snippets/common

View file

@ -0,0 +1,15 @@
ARG HUGO_EXTENDED=
# gcc is required by extended Hugo.
RUN if [[ -n "$HUGO_EXTENDED" ]]; then apk add --no-cache gcc; fi
RUN apk add --update --no-cache ca-certificates
# copy Hugo binary from builder.
COPY --from=builder /usr/bin/hugo /usr/bin/hugo
# working directory.
WORKDIR /src
# default command.
CMD hugo env

View file

@ -0,0 +1,6 @@
# install Embedded Dart SASS.
RUN apk add dart-sass-embedded --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/
RUN apk add --no-cache dart-sass-embedded
# install packages.
RUN npm i -g postcss-cli autoprefixer @fullhuman/postcss-purgecss rtlcss

1
docker/hugo/snippets/git Normal file
View file

@ -0,0 +1 @@
RUN apk add --no-cache git

5
docker/hugo/snippets/go Normal file
View file

@ -0,0 +1,5 @@
# copy Go from builder.
COPY --from=builder /usr/local/go /usr/local/go
# create a symbolic link for Go.
RUN ln -s /usr/local/go/bin/go /usr/local/bin/go

View file

@ -1,8 +0,0 @@
# syntax=docker/dockerfile:1
FROM node:alpine
ARG GCC
RUN apk add --no-cache ca-certificates
RUN if [[ -n "$GCC" ]]; then apk add --no-cache gcc; fi
COPY --from=builder /go/bin/hugo /usr/local/bin/hugo
WORKDIR /src
CMD hugo env

3
site/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
resources/
.hugo_build.lock
public/

View file

3
site/assets/main.scss Normal file
View file

@ -0,0 +1,3 @@
h1 {
color: red;
}

View file

@ -1,3 +1,6 @@
baseURL = 'http://example.org/'
languageCode = 'en-us'
title = 'Hugo Example Site'
[security.funcs]
getenv = ['^HUGO_', '^CI$', '^IMAGE_PREFIX$']

View file

@ -1,9 +1,10 @@
<html>
<head>
<title>{{ .Page.Title }}</title>
{{ partial "get-remote" . }}
{{ partial "dart-sass" . }}
</head>
<body>
{{ partial "get-remote" . }}
{{ block "main" . }}{{ end }}
</body>
</html>
</html>

View file

@ -0,0 +1,5 @@
{{- if strings.Contains (getenv "IMAGE_PREFIX") "exts" }}
{{- $opts := dict "transpiler" "dartsass" }}
{{- $css := resources.Get "main.scss" | toCSS $opts }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">
{{- end }}