coder/scripts/build_docker.sh
Thomas Kosiewski 7171d52279 fix: replace both colons and slashes in SBOM filename for Docker image (#16915)
This PR fixes the SBOM filename generation in the Docker build script to
properly handle image tags that contain slashes. The current
implementation only replaces colons with underscores, but fails when
image tags include slashes (common in registry paths).

The fix updates the string replacement to handle both colons and slashes
in the image tag when generating the SBOM filename.

Change-Id: Ifd7bad6d165393e11202e5bf070a4cb26eaa6a6a
Signed-off-by: Thomas Kosiewski <tk@coder.com>

Signed-off-by: Thomas Kosiewski <tk@coder.com>
2025-03-13 23:01:03 +05:00

170 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# This script builds a Docker image of Coder containing the given binary, for
# the given architecture. Only linux binaries are supported at this time.
#
# Usage: ./build_docker.sh --arch amd64 [--version 1.2.3] [--target image_tag] [--build-base image_tag] [--push] path/to/coder
#
# The --arch parameter is required and accepts a Golang arch specification. It
# will be automatically mapped to a suitable architecture that Docker accepts
# before being passed to `docker buildx build`.
#
# The --build-base parameter is optional and specifies to build the base image
# in Dockerfile.base instead of pulling a copy from the registry. The string
# value is the tag to use for the built image (not pushed). This also consumes
# $CODER_IMAGE_BUILD_BASE_TAG for easily forcing a fresh build in CI.
#
# The default base image can be controlled via $CODER_BASE_IMAGE_TAG.
#
# The image will be built and tagged against the image tag returned by
# ./image_tag.sh unless a --target parameter is supplied.
#
# If no version is specified, defaults to the version from ./version.sh.
#
# If the --push parameter is supplied, the image will be pushed.
#
# Prints the image tag on success.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
DEFAULT_BASE="${CODER_BASE_IMAGE_TAG:-ghcr.io/coder/coder-base:latest}"
arch=""
image_tag=""
build_base="${CODER_IMAGE_BUILD_BASE_TAG:-}"
version=""
push=0
args="$(getopt -o "" -l arch:,target:,build-base:,version:,push -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--arch)
arch="$2"
shift 2
;;
--target)
image_tag="$2"
shift 2
;;
--version)
version="$2"
shift 2
;;
--build-base)
build_base="$2"
shift 2
;;
--push)
push=1
shift
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
if [[ "$arch" == "" ]]; then
error "The --arch parameter is required"
fi
# Check dependencies
dependencies docker
# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi
if [[ "$image_tag" == "" ]]; then
image_tag="$(execrelative ./image_tag.sh --arch "$arch" --version="$version")"
fi
if [[ "$#" != 1 ]]; then
error "Exactly one argument must be provided to this script, $# were supplied"
fi
if [[ ! -f "$1" ]]; then
error "File '$1' does not exist or is not a regular file"
fi
input_file="$(realpath "$1")"
# Remap the arch from Golang to Docker.
declare -A arch_map=(
[amd64]="linux/amd64"
[arm64]="linux/arm64"
[arm]="linux/arm/v7"
[armv7]="linux/arm/v7"
)
if [[ "${arch_map[$arch]+exists}" != "" ]]; then
arch="${arch_map[$arch]}"
fi
# Make temporary dir where all source files intended to be in the image will be
# hardlinked from.
cdroot
temp_dir="$(TMPDIR="$(dirname "$input_file")" mktemp -d)"
ln "$input_file" "$temp_dir/coder"
ln ./scripts/Dockerfile.base "$temp_dir/"
ln ./scripts/Dockerfile "$temp_dir/"
cd "$temp_dir"
export DOCKER_BUILDKIT=1
base_image="$DEFAULT_BASE"
if [[ "$build_base" != "" ]]; then
log "--- Building base Docker image for $arch ($build_base)"
docker build \
--platform "$arch" \
--tag "$build_base" \
--no-cache \
-f Dockerfile.base \
. 1>&2
base_image="$build_base"
else
docker pull --platform "$arch" "$base_image" 1>&2
fi
log "--- Building Docker image for $arch ($image_tag)"
docker build \
--platform "$arch" \
--build-arg "BASE_IMAGE=$base_image" \
--build-arg "CODER_VERSION=$version" \
--no-cache \
--tag "$image_tag" \
-f Dockerfile \
. 1>&2
cdroot
rm -rf "$temp_dir"
if [[ "$push" == 1 ]]; then
log "--- Pushing Docker image for $arch ($image_tag)"
docker push "$image_tag" 1>&2
fi
log "--- Generating SBOM for Docker image ($image_tag)"
syft "$image_tag" -o spdx-json >"${image_tag//[:\/]/_}.spdx.json"
if [[ "$push" == 1 ]]; then
log "--- Attesting SBOM to Docker image for $arch ($image_tag)"
COSIGN_EXPERIMENTAL=1 cosign clean "$image_tag"
COSIGN_EXPERIMENTAL=1 cosign attest --type spdxjson \
--predicate "${image_tag//[:\/]/_}.spdx.json" \
--yes \
"$image_tag"
fi
echo "$image_tag"