Add matrixed gobuilder image build
Replace the packer-images gobuilder with a Dockerfile matrix built on each container-base distro image, on a single repo semver. - install golang, gcc and nfpm - bake the sha256-pinned go-cache-plugin v0.1.0 binary onto PATH - run in-image assertions as a --target test build stage - add pull_request build/test/pre-commit and tag-triggered docker workflows
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# One entry per distro. Adding a distro: add distros/<name>/ and one entry here.
|
||||
- DISTRO: alma9
|
||||
BASE_IMAGE_NAME: almalinux9
|
||||
TAG_SUFFIX: alma9
|
||||
DEFAULT: "true"
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/plugin-docker-buildx:latest"
|
||||
settings:
|
||||
repo: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/gobuilder"
|
||||
target: image
|
||||
dry_run: true
|
||||
build_args:
|
||||
BASE_IMAGE_NAME: "${BASE_IMAGE_NAME}"
|
||||
DISTRO: "${DISTRO}"
|
||||
buildkit_config: |
|
||||
[registry."artifactapi.k8s.syd1.au.unkin.net"]
|
||||
ca = ["/etc/docker/certs.d/artifactapi.k8s.syd1.au.unkin.net/ca.crt"]
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
resources:
|
||||
requests:
|
||||
memory: 1Gi
|
||||
cpu: 1
|
||||
limits:
|
||||
memory: 4Gi
|
||||
cpu: 2
|
||||
@@ -0,0 +1,66 @@
|
||||
when:
|
||||
- event: tag
|
||||
ref: refs/tags/v*
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# One entry per distro. Adding a distro: add distros/<name>/ and one entry here.
|
||||
- DISTRO: alma9
|
||||
BASE_IMAGE_NAME: almalinux9
|
||||
TAG_SUFFIX: alma9
|
||||
DEFAULT: "true"
|
||||
|
||||
steps:
|
||||
- name: docker
|
||||
image: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/plugin-docker-buildx:latest"
|
||||
settings:
|
||||
registry: artifactapi.k8s.syd1.au.unkin.net
|
||||
repo: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/gobuilder"
|
||||
target: image
|
||||
build_args:
|
||||
BASE_IMAGE_NAME: "${BASE_IMAGE_NAME}"
|
||||
DISTRO: "${DISTRO}"
|
||||
buildkit_config: |
|
||||
[registry."artifactapi.k8s.syd1.au.unkin.net"]
|
||||
ca = ["/etc/docker/certs.d/artifactapi.k8s.syd1.au.unkin.net/ca.crt"]
|
||||
tags:
|
||||
- "${CI_COMMIT_TAG##v}-${TAG_SUFFIX}"
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
resources:
|
||||
requests:
|
||||
memory: 1Gi
|
||||
cpu: 1
|
||||
limits:
|
||||
memory: 4Gi
|
||||
cpu: 2
|
||||
|
||||
# The default distro also carries the unsuffixed tags.
|
||||
- name: docker-default
|
||||
image: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/plugin-docker-buildx:latest"
|
||||
when:
|
||||
- evaluate: 'DEFAULT == "true"'
|
||||
settings:
|
||||
registry: artifactapi.k8s.syd1.au.unkin.net
|
||||
repo: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/gobuilder"
|
||||
target: image
|
||||
build_args:
|
||||
BASE_IMAGE_NAME: "${BASE_IMAGE_NAME}"
|
||||
DISTRO: "${DISTRO}"
|
||||
buildkit_config: |
|
||||
[registry."artifactapi.k8s.syd1.au.unkin.net"]
|
||||
ca = ["/etc/docker/certs.d/artifactapi.k8s.syd1.au.unkin.net/ca.crt"]
|
||||
tags:
|
||||
- "${CI_COMMIT_TAG##v}"
|
||||
- latest
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
resources:
|
||||
requests:
|
||||
memory: 1Gi
|
||||
cpu: 1
|
||||
limits:
|
||||
memory: 4Gi
|
||||
cpu: 2
|
||||
@@ -0,0 +1,18 @@
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
steps:
|
||||
- name: pre-commit
|
||||
image: "docker.io/almalinux:9"
|
||||
commands:
|
||||
- ./pre-commit.sh
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
resources:
|
||||
requests:
|
||||
memory: 512Mi
|
||||
cpu: 1
|
||||
limits:
|
||||
memory: 2Gi
|
||||
cpu: 2
|
||||
@@ -0,0 +1,36 @@
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# One entry per distro. Adding a distro: add distros/<name>/ and one entry here.
|
||||
- DISTRO: alma9
|
||||
BASE_IMAGE_NAME: almalinux9
|
||||
TAG_SUFFIX: alma9
|
||||
DEFAULT: "true"
|
||||
|
||||
steps:
|
||||
# The test stage runs test.sh inside the freshly built image, so a failed assertion
|
||||
# fails the build. No docker daemon required.
|
||||
- name: test
|
||||
image: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/plugin-docker-buildx:latest"
|
||||
settings:
|
||||
repo: "artifactapi.k8s.syd1.au.unkin.net/docker-internal/gobuilder"
|
||||
target: test
|
||||
dry_run: true
|
||||
build_args:
|
||||
BASE_IMAGE_NAME: "${BASE_IMAGE_NAME}"
|
||||
DISTRO: "${DISTRO}"
|
||||
buildkit_config: |
|
||||
[registry."artifactapi.k8s.syd1.au.unkin.net"]
|
||||
ca = ["/etc/docker/certs.d/artifactapi.k8s.syd1.au.unkin.net/ca.crt"]
|
||||
backend_options:
|
||||
kubernetes:
|
||||
serviceAccountName: default
|
||||
resources:
|
||||
requests:
|
||||
memory: 1Gi
|
||||
cpu: 1
|
||||
limits:
|
||||
memory: 4Gi
|
||||
cpu: 2
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
ARG REGISTRY=artifactapi.k8s.syd1.au.unkin.net/docker-internal
|
||||
# The container-base release this image is built on — the one place to bump it.
|
||||
ARG BASE_VERSION=0.1.0
|
||||
ARG BASE_IMAGE_NAME=almalinux9
|
||||
|
||||
FROM ${REGISTRY}/${BASE_IMAGE_NAME}:${BASE_VERSION} AS image
|
||||
ARG DISTRO=alma9
|
||||
ARG GO_CACHE_PLUGIN_VERSION=v0.1.0
|
||||
ARG GO_CACHE_PLUGIN_SHA256=0eb23d06a0dcf395559cb6447864579c220e60148fdcd6a6f841c2b17169b9b8
|
||||
COPY distros/${DISTRO}/ /tmp/distro/
|
||||
RUN set -eux; \
|
||||
. /tmp/distro/distro.env; \
|
||||
dnf install -y $PACKAGES; \
|
||||
dnf clean all; \
|
||||
curl -fsSL -o /usr/local/bin/go-cache-plugin \
|
||||
"https://git.unkin.net/unkin/go-cache-plugin/releases/download/${GO_CACHE_PLUGIN_VERSION}/go-cache-plugin-linux-amd64"; \
|
||||
echo "$GO_CACHE_PLUGIN_SHA256 /usr/local/bin/go-cache-plugin" | sha256sum -c -; \
|
||||
chmod 0755 /usr/local/bin/go-cache-plugin; \
|
||||
rm -rf /var/cache/dnf /tmp/distro
|
||||
CMD ["/bin/bash"]
|
||||
|
||||
# Built only via `--target test`; assertions run as a build step so a broken image
|
||||
# fails the build, with no docker daemon needed in CI.
|
||||
FROM image AS test
|
||||
ARG DISTRO=alma9
|
||||
COPY distros/${DISTRO}/distro.env test.sh /tmp/test/
|
||||
RUN cd /tmp/test && ./test.sh
|
||||
|
||||
FROM image AS final
|
||||
@@ -0,0 +1,42 @@
|
||||
REGISTRY := artifactapi.k8s.syd1.au.unkin.net/docker-internal
|
||||
DISTROS := $(notdir $(wildcard distros/*))
|
||||
VERSION ?= $(patsubst v%,%,$(or $(shell git describe --tags --exact-match 2>/dev/null),0.1.0-dev))
|
||||
|
||||
.PHONY: build test clean patch minor major
|
||||
build: $(addprefix build-,$(DISTROS))
|
||||
test: $(addprefix test-,$(DISTROS))
|
||||
clean: $(addprefix clean-,$(DISTROS))
|
||||
|
||||
# Not .PHONY: GNU make skips implicit-rule search for phony targets.
|
||||
|
||||
build-%:
|
||||
@set -eu; . ./distros/$*/distro.env; \
|
||||
docker build --build-arg "BASE_IMAGE_NAME=$$BASE_IMAGE_NAME" --build-arg "DISTRO=$*" \
|
||||
-t "$(REGISTRY)/$$IMAGE:$(VERSION)-$$TAG_SUFFIX" .
|
||||
|
||||
test-%:
|
||||
@set -eu; . ./distros/$*/distro.env; \
|
||||
docker build --target test --progress plain \
|
||||
--build-arg "BASE_IMAGE_NAME=$$BASE_IMAGE_NAME" --build-arg "DISTRO=$*" .
|
||||
|
||||
clean-%:
|
||||
@set -eu; . ./distros/$*/distro.env; \
|
||||
docker rmi "$(REGISTRY)/$$IMAGE:$(VERSION)-$$TAG_SUFFIX" 2>/dev/null || true
|
||||
|
||||
_LATEST := $(shell git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$$' | head -1)
|
||||
_BASE := $(if $(_LATEST),$(_LATEST),v0.0.0)
|
||||
_MAJ := $(shell echo $(_BASE) | sed 's/^v//' | cut -d. -f1)
|
||||
_MIN := $(shell echo $(_BASE) | sed 's/^v//' | cut -d. -f2)
|
||||
_PAT := $(shell echo $(_BASE) | sed 's/^v//' | cut -d. -f3)
|
||||
|
||||
patch:
|
||||
@NEW=v$(_MAJ).$(_MIN).$(shell expr $(_PAT) + 1); \
|
||||
git tag $$NEW && echo "Tagged $$NEW" && git push origin $$NEW
|
||||
|
||||
minor:
|
||||
@NEW=v$(_MAJ).$(shell expr $(_MIN) + 1).0; \
|
||||
git tag $$NEW && echo "Tagged $$NEW" && git push origin $$NEW
|
||||
|
||||
major:
|
||||
@NEW=v$(shell expr $(_MAJ) + 1).0.0; \
|
||||
git tag $$NEW && echo "Tagged $$NEW" && git push origin $$NEW
|
||||
@@ -1,3 +1,29 @@
|
||||
# container-gobuilder
|
||||
|
||||
Go toolchain builder container image, built on each supported container-base distro image, used by Woodpecker pipelines to compile Go projects.
|
||||
The estate's Go toolchain image, built on each `container-base` distro image.
|
||||
Published as `artifactapi.k8s.syd1.au.unkin.net/docker-internal/gobuilder:<version>-<suffix>`;
|
||||
the default distro also gets the unsuffixed `:<version>` and `:latest`.
|
||||
|
||||
| distro | from | tag suffix | default |
|
||||
| --- | --- | --- | --- |
|
||||
| `alma9` | `docker-internal/almalinux9:<base version>` | `alma9` | yes |
|
||||
|
||||
Adds `golang`, `gcc` and `nfpm`, and bakes in the `go-cache-plugin` binary on `PATH`
|
||||
so Go pipelines get the shared build cache with no download step. No credentials and no
|
||||
`GOCACHEPROG` value are baked in — consumers set those.
|
||||
|
||||
The `container-base` release this image builds on is `BASE_VERSION` in the `Dockerfile`.
|
||||
|
||||
## Adding a distro
|
||||
|
||||
1. `distros/<name>/distro.env` — `BASE_IMAGE_NAME`, `IMAGE`, `TAG_SUFFIX`, `DEFAULT`, `PACKAGES`, `GO_MIN`.
|
||||
2. One `include` entry in each of `.woodpecker/{build,test,docker}.yaml`.
|
||||
|
||||
`pre-commit.sh` fails if step 2 is forgotten, or if more than one distro claims `DEFAULT`.
|
||||
|
||||
## Make
|
||||
|
||||
make build # build every distro
|
||||
make test # build the test stage of every distro (assertions run in-image)
|
||||
make build-alma9 # one distro
|
||||
make patch|minor|major
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# alma9 — built on the container-base almalinux9 image
|
||||
BASE_IMAGE_NAME="almalinux9"
|
||||
IMAGE="gobuilder"
|
||||
TAG_SUFFIX="alma9"
|
||||
# The default distro also gets the unsuffixed :<version> and :latest tags.
|
||||
DEFAULT="true"
|
||||
|
||||
PACKAGES="golang gcc nfpm"
|
||||
GO_MIN="1.24"
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
# Guards the one mistake this layout invites: a distro added under distros/ but left
|
||||
# out of a .woodpecker matrix, so it silently never gets built.
|
||||
set -uo pipefail
|
||||
|
||||
REQUIRED_KEYS="BASE_IMAGE_NAME IMAGE TAG_SUFFIX DEFAULT PACKAGES GO_MIN"
|
||||
WORKFLOWS=".woodpecker/build.yaml .woodpecker/test.yaml .woodpecker/docker.yaml"
|
||||
|
||||
fail=0
|
||||
for d in distros/*/; do
|
||||
n=${d#distros/}; n=${n%/}
|
||||
for k in $REQUIRED_KEYS; do
|
||||
grep -q "^${k}=" "${d}distro.env" || { echo "${d}distro.env: missing ${k}"; fail=1; }
|
||||
done
|
||||
for wf in $WORKFLOWS; do
|
||||
grep -q "DISTRO: ${n}\$" "$wf" || { echo "${wf}: distro ${n} missing from matrix"; fail=1; }
|
||||
done
|
||||
done
|
||||
|
||||
# Exactly one distro owns the unsuffixed :<version> and :latest tags.
|
||||
n=$(grep -l 'DEFAULT="true"' distros/*/distro.env | wc -l)
|
||||
[ "$n" -eq 1 ] || { echo "expected exactly one distro with DEFAULT=\"true\", found ${n}"; fail=1; }
|
||||
|
||||
for s in ./*.sh; do bash -n "$s" || fail=1; done
|
||||
|
||||
[ "$fail" -eq 0 ] || { echo "FAILED"; exit 1; }
|
||||
echo "PASS"
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# In-image assertions for the gobuilder image. Run via `docker build --target test`.
|
||||
set -uo pipefail
|
||||
|
||||
ARTIFACTAPI=https://artifactapi.k8s.syd1.au.unkin.net
|
||||
. ./distro.env
|
||||
|
||||
fail=0
|
||||
check() {
|
||||
local name=$1; shift
|
||||
if out=$("$@" 2>&1); then
|
||||
echo "ok $name"
|
||||
else
|
||||
echo "NOT OK $name"
|
||||
printf '%s\n' "$out" | sed 's/^/ /'
|
||||
fail=1
|
||||
fi
|
||||
}
|
||||
|
||||
# Inherited from container-base: no -k and no --cacert.
|
||||
check "internal CA trusted (artifactapi)" \
|
||||
curl -fsS -o /dev/null "$ARTIFACTAPI/api/v1/local/rpm-vendor-el9/repodata/repomd.xml"
|
||||
|
||||
for p in $PACKAGES; do
|
||||
check "package $p installed" rpm -q "$p"
|
||||
done
|
||||
|
||||
# GOCACHEPROG is only enabled by default from Go 1.24, so the toolchain floor matters.
|
||||
go_min_ok() {
|
||||
local have
|
||||
have=$(go env GOVERSION 2>/dev/null | awk '{sub(/^go/,"",$1); print $1}') || return 1
|
||||
[ "$(printf '%s\n%s\n' "$GO_MIN" "$have" | sort -V | head -1)" = "$GO_MIN" ]
|
||||
}
|
||||
check "go >= $GO_MIN (have $(go env GOVERSION 2>/dev/null | awk '{print $1}'))" go_min_ok
|
||||
|
||||
# gcc is what `go test -race` needs.
|
||||
check "gcc runs" gcc --version
|
||||
check "nfpm runs" nfpm --version
|
||||
check "go-cache-plugin on PATH" command -v go-cache-plugin
|
||||
check "go-cache-plugin executable" test -x /usr/local/bin/go-cache-plugin
|
||||
# `version` prints correctly but exits 2, so assert on its output.
|
||||
check "go-cache-plugin runs" bash -c 'go-cache-plugin version 2>&1 | grep -q "^go-cache-plugin version v"'
|
||||
|
||||
# cgo must actually link, which is the thing -race depends on.
|
||||
check "cgo builds" bash -c 'cd "$(mktemp -d)" && go mod init t >/dev/null &&
|
||||
printf "package main\n\nimport \"C\"\n\nfunc main() {}\n" > m.go && CGO_ENABLED=1 go build ./...'
|
||||
|
||||
[ "$fail" -eq 0 ] || { echo "FAILED"; exit 1; }
|
||||
echo "PASS"
|
||||
Reference in New Issue
Block a user