#!/usr/bin/env bash set -euo pipefail : "${IMAGE_NAME:?}" : "${BUILD_CONTEXT:?}" : "${BASE_IMAGE:?}" : "${PRIMARY_TAG:?}" : "${TAGS:?}" : "${OCI_LABELS:?}" : "${BUILD_ARGS:?}" : "${FINGERPRINT_COMMAND:?}" candidate_tag="local/${IMAGE_NAME}:candidate" cleanup() { docker image rm -f "$candidate_tag" >/dev/null 2>&1 || true } trap cleanup EXIT mapfile -t build_args < <(printf '%s\n' "$BUILD_ARGS" | sed '/^[[:space:]]*$/d') mapfile -t oci_labels < <(printf '%s\n' "$OCI_LABELS" | sed '/^[[:space:]]*$/d') build_flags=(--pull --no-cache --load --tag "$candidate_tag") for build_arg in "${build_args[@]}"; do build_flags+=(--build-arg "$build_arg") done for oci_label in "${oci_labels[@]}"; do build_flags+=(--label "$oci_label") done context_hash="$( cd "$BUILD_CONTEXT" find . -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}' )" docker pull "$BASE_IMAGE" >/dev/null base_image_id="$(docker image inspect "$BASE_IMAGE" --format '{{.Id}}')" docker buildx build "${build_flags[@]}" "$BUILD_CONTEXT" fingerprint() { { printf 'base-image-id=%s\n' "$base_image_id" printf 'context-hash=%s\n' "$context_hash" printf 'build-args=%s\n' "$BUILD_ARGS" printf 'oci-labels=%s\n' "$OCI_LABELS" docker run --rm --entrypoint /bin/sh "$1" -c "$FINGERPRINT_COMMAND" } | sha256sum | awk '{print $1}' } candidate_fingerprint="$(fingerprint "$candidate_tag")" docker pull "$PRIMARY_TAG" >/dev/null 2>&1 || true current_fingerprint="$(docker image inspect "$PRIMARY_TAG" --format '{{ index .Config.Labels "org.ajpanton.content-fingerprint" }}' 2>/dev/null || true)" if [[ -z "$current_fingerprint" ]] && docker image inspect "$PRIMARY_TAG" >/dev/null 2>&1; then current_fingerprint="$(fingerprint "$PRIMARY_TAG")" fi if [[ -n "$current_fingerprint" && "$current_fingerprint" == "$candidate_fingerprint" ]]; then echo "${IMAGE_NAME}: unchanged; not publishing" exit 0 fi docker buildx build "${build_flags[@]}" --label "org.ajpanton.content-fingerprint=${candidate_fingerprint}" "$BUILD_CONTEXT" final_fingerprint="$(fingerprint "$candidate_tag")" if [[ "$final_fingerprint" != "$candidate_fingerprint" ]]; then echo "${IMAGE_NAME}: inputs changed while rebuilding; retry the workflow" >&2 exit 1 fi while IFS= read -r tag; do [[ -z "$tag" ]] && continue docker tag "$candidate_tag" "$tag" docker push "$tag" done <<< "$TAGS" echo "${IMAGE_NAME}: published"