#!/usr/bin/env bash set -euo pipefail usage() { sed -n '2,22p' "$0" } # Build and publish a Docker image with buildx. # # Usage: # publish.sh --registry HOST --repository PATH --tag TAG --platform PLATFORMS [options] # # Options: # --registry HOST Registry host, without a URL scheme # --repository PATH Repository path, such as team/service # --tag TAG Image tag # --platform LIST Comma-separated platforms # --file PATH Dockerfile path (default: Dockerfile) # --context PATH Build context (default: .) # --builder NAME Existing buildx builder # --load Load a single-platform image instead of pushing # --dry-run Print the resolved build without executing it # --help Show this help registry= repository= tag= platform= dockerfile=Dockerfile build_context=. builder= mode=push dry_run=false while (($#)); do case "$1" in --registry) registry=${2-}; shift 2 ;; --repository) repository=${2-}; shift 2 ;; --tag) tag=${2-}; shift 2 ;; --platform) platform=${2-}; shift 2 ;; --file) dockerfile=${2-}; shift 2 ;; --context) build_context=${2-}; shift 2 ;; --builder) builder=${2-}; shift 2 ;; --load) mode=load; shift ;; --dry-run) dry_run=true; shift ;; --help|-h) usage; exit 0 ;; *) printf 'Unknown argument: %s\n' "$1" >&2; usage >&2; exit 2 ;; esac done for required_name in registry repository tag platform; do if [[ -z ${!required_name} ]]; then printf 'Missing required option: --%s\n' "$required_name" >&2 exit 2 fi done if [[ $registry == *://* || $registry == */* ]]; then printf '%s\n' 'Registry must be a host without a URL scheme or path.' >&2 exit 2 fi if [[ $repository == /* || $repository == */ || $repository != */* ]]; then printf '%s\n' 'Repository must look like namespace/image.' >&2 exit 2 fi if [[ $tag == *:* || $tag == */* ]]; then printf '%s\n' 'Tag must not contain ":" or "/".' >&2 exit 2 fi if [[ $mode == load && $platform == *,* ]]; then printf '%s\n' '--load supports only one platform.' >&2 exit 2 fi if [[ ! -f $dockerfile ]]; then printf 'Dockerfile not found: %s\n' "$dockerfile" >&2 exit 2 fi if [[ ! -d $build_context ]]; then printf 'Build context not found: %s\n' "$build_context" >&2 exit 2 fi if [[ $dry_run == false ]] && ! command -v docker >/dev/null 2>&1; then printf '%s\n' 'docker is not installed or not available in PATH.' >&2 exit 127 fi image_ref="${registry}/${repository}:${tag}" build_cmd=(docker buildx build --file "$dockerfile" --platform "$platform" --tag "$image_ref") if [[ -n $builder ]]; then build_cmd+=(--builder "$builder") fi if [[ $mode == push ]]; then build_cmd+=(--push) else build_cmd+=(--load) fi build_cmd+=("$build_context") printf 'Image: %s\n' "$image_ref" printf 'Platform: %s\n' "$platform" printf 'Dockerfile: %s\n' "$dockerfile" printf 'Context: %s\n' "$build_context" printf 'Mode: %s\n' "$mode" if [[ $dry_run == true ]]; then printf 'Command:' printf ' %q' "${build_cmd[@]}" printf '\n' exit 0 fi "${build_cmd[@]}" if [[ $mode == push ]]; then docker buildx imagetools inspect "$image_ref" fi