Files
coder/scripts/build_docker.sh
T
Thomas Kosiewski 389af22dac chore: replace colons in SBOM filename for Docker image attestation (#16914)
This PR fixes an issue in the Docker build script where the SBOM file path used the image tag directly, which could contain colons. Since colons are not valid characters in filenames on many filesystems, this replaces colons with underscores in the output filename.

Change-Id: I887f4fc255d9bfa19b6c5d23ad0a5db7352aa2af
Signed-off-by: Thomas Kosiewski <tk@coder.com>
2025-03-13 18:20:43 +01: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"