268 lines
7.2 KiB
Bash
Executable File
268 lines
7.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
TARGET_ARCH="${TARGET_ARCH:-}"
|
|
IMAGE_TAG="${IMAGE_TAG:-}"
|
|
BUILDER_IMAGE="${BUILDER_IMAGE:-rust:1-bookworm}"
|
|
RUNTIME_IMAGE="${RUNTIME_IMAGE:-debian:bookworm-slim}"
|
|
OUT_DIR="${OUT_DIR:-}"
|
|
DOCKERFILE="${DOCKERFILE:-$REPO_ROOT/docker/ours-rp-runtime.Dockerfile}"
|
|
BUILDER_NAME="${BUILDER_NAME:-default}"
|
|
INSTALL_BINFMT="${INSTALL_BINFMT:-1}"
|
|
SAVE_IMAGE="${SAVE_IMAGE:-1}"
|
|
LOAD_IMAGE="${LOAD_IMAGE:-1}"
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage:
|
|
scripts/docker/build_docker_runtime_image.sh --arch amd64|arm64 [options]
|
|
|
|
Options:
|
|
--arch <arch> Target architecture: amd64|arm64
|
|
--image <tag> Docker image tag (default: ours-rp-runtime-<arch>:<git8>)
|
|
--out-dir <path> Directory for docker save tar.gz (default: target/<arch>-docker)
|
|
--dockerfile <path> Dockerfile path
|
|
--builder <name> buildx builder name
|
|
--builder-image <tag>
|
|
Builder base image (default: rust:1-bookworm)
|
|
--runtime-image <tag>
|
|
Runtime base image (default: debian:bookworm-slim)
|
|
--no-binfmt Do not install binfmt/qemu for cross-architecture builds
|
|
--no-save Build image but do not docker save it
|
|
--no-load Use buildx output tar instead of --load
|
|
-h, --help Show this help
|
|
USAGE
|
|
}
|
|
|
|
normalize_arch() {
|
|
case "$1" in
|
|
amd64|x86_64)
|
|
printf 'amd64\n'
|
|
;;
|
|
arm64|aarch64)
|
|
printf 'arm64\n'
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
safe_tag_name() {
|
|
printf '%s' "$1" | tr '/:' '--'
|
|
}
|
|
|
|
platform_for_arch() {
|
|
printf 'linux/%s\n' "$1"
|
|
}
|
|
|
|
default_image_tag() {
|
|
local arch="$1"
|
|
local commit_short="$2"
|
|
printf 'ours-rp-runtime-%s:%s\n' "$arch" "$commit_short"
|
|
}
|
|
|
|
require_command() {
|
|
command -v "$1" >/dev/null 2>&1 || {
|
|
echo "missing required command: $1" >&2
|
|
exit 2
|
|
}
|
|
}
|
|
|
|
host_arch() {
|
|
local raw_arch
|
|
raw_arch="$(uname -m)"
|
|
normalize_arch "$raw_arch" || {
|
|
echo "unsupported host architecture: $raw_arch" >&2
|
|
exit 2
|
|
}
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--arch)
|
|
TARGET_ARCH="$(normalize_arch "$2")" || {
|
|
echo "unsupported target architecture: $2" >&2
|
|
exit 2
|
|
}
|
|
shift 2
|
|
;;
|
|
--image)
|
|
IMAGE_TAG="$2"
|
|
shift 2
|
|
;;
|
|
--out-dir)
|
|
OUT_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--dockerfile)
|
|
DOCKERFILE="$2"
|
|
shift 2
|
|
;;
|
|
--builder)
|
|
BUILDER_NAME="$2"
|
|
shift 2
|
|
;;
|
|
--builder-image)
|
|
BUILDER_IMAGE="$2"
|
|
shift 2
|
|
;;
|
|
--runtime-image)
|
|
RUNTIME_IMAGE="$2"
|
|
shift 2
|
|
;;
|
|
--no-binfmt)
|
|
INSTALL_BINFMT=0
|
|
shift
|
|
;;
|
|
--no-save)
|
|
SAVE_IMAGE=0
|
|
shift
|
|
;;
|
|
--no-load)
|
|
LOAD_IMAGE=0
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "unknown option: $1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$TARGET_ARCH" ]] || {
|
|
echo "--arch is required" >&2
|
|
usage >&2
|
|
exit 2
|
|
}
|
|
|
|
SOURCE_COMMIT_FULL="$(git -C "$REPO_ROOT" rev-parse HEAD 2>/dev/null || echo unknown)"
|
|
SOURCE_COMMIT_SHORT="$(git -C "$REPO_ROOT" rev-parse --short=8 HEAD 2>/dev/null || echo unknown)"
|
|
SOURCE_DIRTY="false"
|
|
if [[ -n "$(git -C "$REPO_ROOT" status --short 2>/dev/null)" ]]; then
|
|
SOURCE_DIRTY="true"
|
|
fi
|
|
BUILD_TIMESTAMP_UTC="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
|
|
if [[ -z "$IMAGE_TAG" ]]; then
|
|
default_revision="$SOURCE_COMMIT_SHORT"
|
|
if [[ "$SOURCE_DIRTY" == "true" ]]; then
|
|
default_revision="${default_revision}-dirty"
|
|
fi
|
|
IMAGE_TAG="$(default_image_tag "$TARGET_ARCH" "$default_revision")"
|
|
fi
|
|
if [[ -z "$OUT_DIR" ]]; then
|
|
OUT_DIR="$REPO_ROOT/target/${TARGET_ARCH}-docker"
|
|
fi
|
|
|
|
require_command docker
|
|
mkdir -p "$OUT_DIR"
|
|
|
|
target_platform="$(platform_for_arch "$TARGET_ARCH")"
|
|
local_host_arch="$(host_arch)"
|
|
|
|
if [[ "$INSTALL_BINFMT" == "1" && "$local_host_arch" != "$TARGET_ARCH" ]]; then
|
|
echo "installing binfmt/qemu for $TARGET_ARCH"
|
|
docker run --rm --privileged tonistiigi/binfmt --install "$TARGET_ARCH"
|
|
fi
|
|
|
|
if [[ "$BUILDER_NAME" != "default" ]] && ! docker buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then
|
|
docker buildx create --name "$BUILDER_NAME" --driver docker-container --use >/dev/null
|
|
else
|
|
docker buildx use "$BUILDER_NAME" >/dev/null
|
|
fi
|
|
docker buildx inspect --bootstrap >/dev/null
|
|
|
|
metadata_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build-metadata.json"
|
|
tar_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").tar.gz"
|
|
build_log="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build.log"
|
|
|
|
echo "building $target_platform image: $IMAGE_TAG"
|
|
echo "repo: $REPO_ROOT"
|
|
echo "dockerfile: $DOCKERFILE"
|
|
echo "builder_image: $BUILDER_IMAGE"
|
|
echo "runtime_image: $RUNTIME_IMAGE"
|
|
echo "source_commit: $SOURCE_COMMIT_FULL"
|
|
echo "source_dirty: $SOURCE_DIRTY"
|
|
echo "build_timestamp_utc: $BUILD_TIMESTAMP_UTC"
|
|
start_epoch="$(date +%s)"
|
|
|
|
if [[ "$LOAD_IMAGE" == "1" ]]; then
|
|
docker buildx build \
|
|
--platform "$target_platform" \
|
|
--builder "$BUILDER_NAME" \
|
|
--pull=false \
|
|
--load \
|
|
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
|
--build-arg "BUILDER_IMAGE=$BUILDER_IMAGE" \
|
|
--build-arg "RUNTIME_IMAGE=$RUNTIME_IMAGE" \
|
|
--build-arg "SOURCE_COMMIT=$SOURCE_COMMIT_FULL" \
|
|
--build-arg "SOURCE_DIRTY=$SOURCE_DIRTY" \
|
|
--build-arg "BUILD_TIMESTAMP_UTC=$BUILD_TIMESTAMP_UTC" \
|
|
--metadata-file "$metadata_path" \
|
|
-t "$IMAGE_TAG" \
|
|
-f "$DOCKERFILE" \
|
|
"$REPO_ROOT" 2>&1 | tee "$build_log"
|
|
else
|
|
raw_tar_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").tar"
|
|
docker buildx build \
|
|
--platform "$target_platform" \
|
|
--builder "$BUILDER_NAME" \
|
|
--pull=false \
|
|
--output "type=docker,dest=$raw_tar_path" \
|
|
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
|
--build-arg "BUILDER_IMAGE=$BUILDER_IMAGE" \
|
|
--build-arg "RUNTIME_IMAGE=$RUNTIME_IMAGE" \
|
|
--build-arg "SOURCE_COMMIT=$SOURCE_COMMIT_FULL" \
|
|
--build-arg "SOURCE_DIRTY=$SOURCE_DIRTY" \
|
|
--build-arg "BUILD_TIMESTAMP_UTC=$BUILD_TIMESTAMP_UTC" \
|
|
--metadata-file "$metadata_path" \
|
|
-t "$IMAGE_TAG" \
|
|
-f "$DOCKERFILE" \
|
|
"$REPO_ROOT" 2>&1 | tee "$build_log"
|
|
gzip -f "$raw_tar_path"
|
|
tar_path="${raw_tar_path}.gz"
|
|
fi
|
|
|
|
elapsed_secs=$(( $(date +%s) - start_epoch ))
|
|
|
|
if [[ "$SAVE_IMAGE" == "1" && "$LOAD_IMAGE" == "1" ]]; then
|
|
echo "saving image to $tar_path"
|
|
docker save "$IMAGE_TAG" | gzip -c > "$tar_path"
|
|
fi
|
|
|
|
tar_sha256=""
|
|
if [[ -f "$tar_path" ]]; then
|
|
tar_sha256="$(sha256sum "$tar_path" | awk '{print $1}')"
|
|
fi
|
|
|
|
{
|
|
echo "image=$IMAGE_TAG"
|
|
echo "platform=$target_platform"
|
|
echo "arch=$TARGET_ARCH"
|
|
echo "builder_image=$BUILDER_IMAGE"
|
|
echo "runtime_image=$RUNTIME_IMAGE"
|
|
echo "elapsed_secs=$elapsed_secs"
|
|
echo "metadata=$metadata_path"
|
|
echo "tar=$tar_path"
|
|
echo "tar_size_bytes=$(wc -c < "$tar_path" 2>/dev/null || echo 0)"
|
|
echo "tar_sha256=$tar_sha256"
|
|
echo "source_commit=$SOURCE_COMMIT_FULL"
|
|
echo "source_commit_short=$SOURCE_COMMIT_SHORT"
|
|
echo "source_dirty=$SOURCE_DIRTY"
|
|
echo "build_timestamp_utc=$BUILD_TIMESTAMP_UTC"
|
|
echo "git_commit=$SOURCE_COMMIT_FULL"
|
|
echo "git_status_count=$(git -C "$REPO_ROOT" status --short 2>/dev/null | wc -l | tr -d ' ')"
|
|
echo "built_at_utc=$BUILD_TIMESTAMP_UTC"
|
|
} > "$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build-summary.env"
|
|
|
|
echo "build complete: elapsed=${elapsed_secs}s tar=$tar_path"
|