From 36204e82da5693a3572fab53ea7116a389f7fe02 Mon Sep 17 00:00:00 2001 From: Barry Morrison <689591+esacteksab@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:32:22 +0000 Subject: [PATCH] chore: local build support --- Makefile | 18 ++++++ container-structure-test.yaml | 52 +++++++++++++++ scripts/build-container.sh | 84 +++++++++++++++++++++++++ scripts/run-container-structure-test.sh | 30 +++++++++ 4 files changed, 184 insertions(+) create mode 100644 Makefile create mode 100644 container-structure-test.yaml create mode 100755 scripts/build-container.sh create mode 100755 scripts/run-container-structure-test.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..345cc6c --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +MAKEFLAGS += --warn-undefined-variables +SHELL := bash +.SHELLFLAGS := -eu -o pipefail -c +.DEFAULT_GOAL := all +.DELETE_ON_ERROR: +.SUFFIXES: + +.PHONY: build +build: + ./scripts/build-container.sh + +.PHONY: test +test: + ./scripts/run-container-structure-test.sh + +.PHONY: lint +lint: + docker run --rm -i ghcr.io/hadolint/hadolint hadolint - < Dockerfile diff --git a/container-structure-test.yaml b/container-structure-test.yaml new file mode 100644 index 0000000..3f85822 --- /dev/null +++ b/container-structure-test.yaml @@ -0,0 +1,52 @@ +schemaVersion: 2.0.0 + +metadataTest: + envVars: + - key: GOPATH + value: /go + - key: PATH + value: /go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + - key: CGO_ENABLED + value: "0" + - key: GO_VERSION + value: "1.25.8" + workdir: /go + +commandTests: + - name: go binary is available + command: go + args: ["version"] + expectedOutput: + - "go version go[0-9]+\\.[0-9]+(\\.[0-9]+)? linux/amd64" + + - name: ca-certificates package is installed + command: dpkg + args: ["-s", "ca-certificates"] + expectedOutput: + - "Version: 20240203" + +fileExistenceTests: + - name: Go toolchain directory exists + path: /usr/local/go + shouldExist: true + isExecutableBy: any + + - name: go command binary exists + path: /usr/local/go/bin/go + shouldExist: true + isExecutableBy: any + + - name: GOPATH root exists with expected permissions + path: /go + shouldExist: true + permissions: "drwxr-x---" + + - name: GOPATH src exists with expected permissions + path: /go/src + shouldExist: true + permissions: "drwxr-x---" + + - name: GOPATH bin exists with expected permissions + path: /go/bin + shouldExist: true + permissions: "drwxr-x---" diff --git a/scripts/build-container.sh b/scripts/build-container.sh new file mode 100755 index 0000000..6c3a774 --- /dev/null +++ b/scripts/build-container.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +if ! command -v docker > /dev/null 2>&1; then + echo "error: docker is required" >&2 + exit 1 +fi + +if ! command -v curl > /dev/null 2>&1; then + echo "error: curl is required" >&2 + exit 1 +fi + +if ! command -v jq > /dev/null 2>&1; then + echo "error: jq is required" >&2 + exit 1 +fi + +IMAGE_REPO="${IMAGE_REPO:-esacteksab/go}" +GO_VERSION="${GO_VERSION:-}" +PUSH="${PUSH:-false}" +DATE="$(date +'%Y-%m-%d')" + +declare -a VERSIONS=() + +if [[ -n "$GO_VERSION" ]]; then + VERSIONS=("$GO_VERSION") +else + mapfile -t VERSIONS < <( + curl -fsSL 'https://go.dev/dl/?mode=json' \ + | jq -r ' + [.[] | select(.stable) | .version] + | group_by(split(".")[0:2] | join(".")) + | map(max_by(ltrimstr("go") | split(".") | map(tonumber)))[] + | ltrimstr("go") + ' + ) +fi + +if [[ "${#VERSIONS[@]}" -eq 0 ]]; then + echo "error: no Go versions resolved" >&2 + exit 1 +fi + +LATEST_VERSION="${VERSIONS[${#VERSIONS[@]}-1]}" + +for version in "${VERSIONS[@]}"; do + stable_line="$(echo "$version" | cut -d. -f1,2)" + go_image_digest="$(docker buildx imagetools inspect "golang:${version}-bookworm" | awk '/^Digest:[[:space:]]/ {print $2; exit}')" + if [[ -z "$go_image_digest" ]]; then + echo "error: failed to resolve digest for golang:${version}-bookworm" >&2 + exit 1 + fi + + tags=( + "${IMAGE_REPO}:${version}" + "${IMAGE_REPO}:${stable_line}" + "${IMAGE_REPO}:${version}-${DATE}" + ) + + if [[ "$version" == "$LATEST_VERSION" ]]; then + tags+=("${IMAGE_REPO}:latest") + fi + + echo "Building image with GO_VERSION=${version} GO_IMAGE_DIGEST=${go_image_digest}" + docker build \ + --build-arg "GO_VERSION=${version}" \ + --build-arg "GO_IMAGE_DIGEST=${go_image_digest}" \ + -t "${tags[0]}" \ + . + + for tag in "${tags[@]:1}"; do + docker tag "${tags[0]}" "$tag" + done + + echo "Tagged images for GO_VERSION=${version}:" + printf ' - %s\n' "${tags[@]}" + + if [[ "$PUSH" == "true" ]]; then + for tag in "${tags[@]}"; do + docker push "$tag" + done + fi +done diff --git a/scripts/run-container-structure-test.sh b/scripts/run-container-structure-test.sh new file mode 100755 index 0000000..ff4cc8e --- /dev/null +++ b/scripts/run-container-structure-test.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +if ! command -v docker > /dev/null 2>&1; then + echo "error: docker is required to run container-structure-test locally" >&2 + exit 1 +fi + +CST_BIN="" +TMP_DIR="" +cleanup() { + if [[ -n "$TMP_DIR" && -d "$TMP_DIR" ]]; then + rm -rf "$TMP_DIR" + fi +} +trap cleanup EXIT + +if command -v container-structure-test > /dev/null 2>&1; then + CST_BIN="$(command -v container-structure-test)" +else + TMP_DIR="$(mktemp -d)" + CST_BIN="$TMP_DIR/container-structure-test" + curl -fsSL -o "$CST_BIN" \ + https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-amd64 + chmod +x "$CST_BIN" +fi + +IMAGE_TAG="dev-container:local-ci" +docker build -t "$IMAGE_TAG" . +"$CST_BIN" test --image "$IMAGE_TAG" --config container-structure-test.yaml