#!/usr/bin/env bash ############################################################################### # infra/devcontainer/build-image.sh — build the pinned dev `toolbox` image and prove it works. # # ./build-image.sh # build + verify (tag: home-k3s-dev:local) # ./build-image.sh --no-verify # build only # IMAGE=foo:bar ./build-image.sh # build under another local name # ENGINE=docker ./build-image.sh # build with docker instead of podman # ./build-image.sh --print-build-args # print KEY=VALUE build args (what CI consumes) # # ONE BUILD DEFINITION, TWO CALLERS. `dev.sh build` calls this for local development. The CI # image pipeline (.gitea/workflows/images.yaml -> .gitea/workflows/scripts/images.sh) builds the # SAME Containerfile on rootless BuildKit (platform/buildkit/) — no container engine exists in a # CI job — taking its build args from `--print-build-args`, and PUBLISHES the result as # gitea.home.keithjackson.dev/home-network/toolbox. The Containerfile (including its `verify` # test stage) and the arg list here are the one recipe both use. # # THIS SCRIPT NEVER PUSHES. Publishing is CI's job (images.yaml, on push to main), not a # workstation's: an image that exists only because someone ran `podman push` from a laptop has # no build log, no recorded inputs and no verification anyone else can read. # # VERSIONS ARE NOT DEFINED HERE. infra/devcontainer/versions.env is the single source of truth. ############################################################################### set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENGINE="${ENGINE:-podman}" IMAGE="${IMAGE:-home-k3s-dev:local}" VERIFY=1 PRINT_ARGS=0 for arg in "$@"; do case "$arg" in --no-verify) VERIFY=0 ;; --print-build-args) PRINT_ARGS=1 ;; -h|--help) sed -n '2,/^####/p' "${BASH_SOURCE[0]}" | sed '$d' | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "unknown argument: $arg" >&2; exit 2 ;; esac done log() { printf '\n\033[1m==> %s\033[0m\n' "$*"; } die() { printf '\033[31mERROR: %s\033[0m\n' "$*" >&2; exit 1; } # shellcheck disable=SC1091 source "${HERE}/versions.env" # THE build args, in ONE place. `--print-build-args` hands exactly this list to the CI pipeline # (.gitea/workflows/scripts/images.sh), which builds on rootless BuildKit rather than calling # "$ENGINE build" — so CI and a local build cannot drift apart on a pin. BUILD_ARGS=( "FEDORA_VERSION=$FEDORA_VERSION" "OPENTOFU_VERSION=$OPENTOFU_VERSION" "AGE_VERSION=$AGE_VERSION" "SOPS_VERSION=$SOPS_VERSION" "FLUX_VERSION=$FLUX_VERSION" "KUBECTL_VERSION=$KUBECTL_VERSION" "HELM_VERSION=$HELM_VERSION" "KYVERNO_VERSION=$KYVERNO_VERSION" "KYVERNO_JSON_VERSION=$KYVERNO_JSON_VERSION" "CHAINSAW_VERSION=$CHAINSAW_VERSION" "YAMLLINT_VERSION=$YAMLLINT_VERSION" "YQ_VERSION=$YQ_VERSION" "CHECK_JSONSCHEMA_VERSION=$CHECK_JSONSCHEMA_VERSION" "PROMTOOL_VERSION=$PROMTOOL_VERSION" "VELERO_VERSION=$VELERO_VERSION" ) if [ "${PRINT_ARGS}" -eq 1 ]; then printf '%s\n' "${BUILD_ARGS[@]}"; exit 0; fi command -v "${ENGINE}" >/dev/null 2>&1 || die "'${ENGINE}' not on PATH (set ENGINE=docker to use docker)" ARG_FLAGS=() for a in "${BUILD_ARGS[@]}"; do ARG_FLAGS+=(--build-arg "$a"); done # Provenance: CI passes the commit it is building, so a pulled image names its source tree. LABEL_ARGS=() [ -z "${BUILD_REVISION:-}" ] || LABEL_ARGS+=(--label "org.opencontainers.image.revision=${BUILD_REVISION}") # --- verify -------------------------------------------------------------------- # The assertions live in the Containerfile's `verify` stage (every tool on PATH, the # version-sensitive ones report their versions.env pins — promtool above all, because # ci/versions.env asserts a promtool/Prometheus lockstep — and no network), so the SAME proof # runs here under podman and in CI under rootless BuildKit. A failing assertion fails the build. if [ "${VERIFY}" -eq 1 ]; then log "Verifying (Containerfile target: verify)" "$ENGINE" build "${ARG_FLAGS[@]}" --target verify -f "${HERE}/Containerfile" "${HERE}" log "Toolbox verified" fi log "Building ${IMAGE}" "$ENGINE" build "${LABEL_ARGS[@]}" "${ARG_FLAGS[@]}" --target toolbox \ -t "$IMAGE" -f "${HERE}/Containerfile" "${HERE}" log "Built ${IMAGE}" printf ' image id %s\n' "$("$ENGINE" image inspect --format '{{.Id}}' "$IMAGE")"