#!/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:-}" IMAGE_TAR="${IMAGE_TAR:-}" METRICS_IMAGE="${METRICS_IMAGE:-}" METRICS_IMAGE_TAR="${METRICS_IMAGE_TAR:-}" PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-prom/prometheus:v2.55.1}" PROMETHEUS_IMAGE_TAR="${PROMETHEUS_IMAGE_TAR:-}" GRAFANA_IMAGE="${GRAFANA_IMAGE:-grafana/grafana:11.3.1}" GRAFANA_IMAGE_TAR="${GRAFANA_IMAGE_TAR:-}" OUT_DIR="${OUT_DIR:-}" PACKAGE_PREFIX="${PACKAGE_PREFIX:-}" TEMPLATE_DIR="${TEMPLATE_DIR:-$REPO_ROOT/deploy/arm64-installer}" usage() { cat <<'USAGE' Usage: scripts/docker/build_docker_installer_package.sh --arch amd64|arm64 [options] Options: --arch Target architecture: amd64|arm64 --image Runtime image tag recorded in package manifest. --image-tar Existing docker save tar/tar.gz to include. --metrics-image Metrics image tag recorded in package manifest. --metrics-image-tar Existing metrics docker save tar/tar.gz to include. --prometheus-image Prometheus image tag to record and package. --prometheus-image-tar Existing Prometheus docker save tar/tar.gz to include. --grafana-image Grafana image tag to record and package. --grafana-image-tar Existing Grafana docker save tar/tar.gz to include. --out-dir Output directory. --prefix Package directory/tar prefix. --template-dir Package template directory. -h, --help Show help. If --image-tar or --metrics-image-tar is omitted, the script uses the matching docker-save archive under target/-docker/. USAGE } normalize_arch() { case "$1" in amd64|x86_64) printf 'amd64\n' ;; arm64|aarch64) printf 'arm64\n' ;; *) return 1 ;; esac } platform_for_arch() { printf 'linux/%s\n' "$1" } safe_tag_name() { printf '%s' "$1" | tr '/:' '--' } default_runtime_tag() { printf 'ours-rp-runtime-%s:dev\n' "$1" } default_metrics_tag() { printf 'ours-rp-metrics-%s:dev\n' "$1" } default_package_prefix() { printf 'ours-rp-docker-installer-%s\n' "$1" } default_host_data_dir() { printf '/var/lib/ours-rp-%s-installer\n' "$1" } default_compose_project() { printf 'ours-rp-%s-installer\n' "$1" } default_metrics_instance() { printf '%s-installer\n' "$1" } replace_or_append_env() { local env_path="$1" local key="$2" local value="$3" local tmp_env tmp_env="${env_path}.tmp" awk -v key="$key" -v value="$value" ' BEGIN { done=0 } $0 ~ "^" key "=" { print key "=" value; done=1; next } { print } END { if (!done) print key "=" value } ' "$env_path" > "$tmp_env" mv "$tmp_env" "$env_path" } replace_text_in_file() { local file_path="$1" local old_text="$2" local new_text="$3" OLD_TEXT="$old_text" NEW_TEXT="$new_text" perl -0pi -e 's/\Q$ENV{OLD_TEXT}\E/$ENV{NEW_TEXT}/g' "$file_path" } ensure_image_for_platform() { local image="$1" local expected_platform="$2" local role="$3" local actual_platform if ! docker image inspect "$image" >/dev/null 2>&1; then echo "pulling $role image for $expected_platform: $image" >&2 docker pull --platform "$expected_platform" "$image" >&2 fi actual_platform="$(docker image inspect --format '{{.Os}}/{{.Architecture}}' "$image" 2>/dev/null || echo unknown)" if [[ "$actual_platform" != "$expected_platform" ]]; then echo "re-pulling $role image for $expected_platform: $image (current=$actual_platform)" >&2 docker pull --platform "$expected_platform" "$image" >&2 actual_platform="$(docker image inspect --format '{{.Os}}/{{.Architecture}}' "$image" 2>/dev/null || echo unknown)" fi [[ "$actual_platform" == "$expected_platform" ]] || { cat >&2 <&2 exit 2 } printf '%s\n' "$existing_tar" return 0 fi ensure_image_for_platform "$image" "$expected_platform" "$role" local tar_path="$out_dir/$(safe_tag_name "$image")-${TARGET_ARCH}.tar.gz" echo "saving $role image to $tar_path" >&2 docker save "$image" | gzip -c > "$tar_path" printf '%s\n' "$tar_path" } default_saved_image_tar() { local image="$1" local out_dir="$REPO_ROOT/target/${TARGET_ARCH}-docker" local safe_name safe_name="$(safe_tag_name "$image")" if [[ -f "$out_dir/${safe_name}.tar.gz" ]]; then printf '%s\n' "$out_dir/${safe_name}.tar.gz" return 0 fi if [[ -f "$out_dir/${safe_name}.tar" ]]; then printf '%s\n' "$out_dir/${safe_name}.tar" return 0 fi printf '\n' } 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 ;; --image-tar) IMAGE_TAR="$2" shift 2 ;; --metrics-image) METRICS_IMAGE="$2" shift 2 ;; --metrics-image-tar) METRICS_IMAGE_TAR="$2" shift 2 ;; --prometheus-image) PROMETHEUS_IMAGE="$2" shift 2 ;; --prometheus-image-tar) PROMETHEUS_IMAGE_TAR="$2" shift 2 ;; --grafana-image) GRAFANA_IMAGE="$2" shift 2 ;; --grafana-image-tar) GRAFANA_IMAGE_TAR="$2" shift 2 ;; --out-dir) OUT_DIR="$2" shift 2 ;; --prefix) PACKAGE_PREFIX="$2" shift 2 ;; --template-dir) TEMPLATE_DIR="$2" shift 2 ;; -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 } [[ -d "$TEMPLATE_DIR" ]] || { echo "missing template dir: $TEMPLATE_DIR" >&2 exit 2 } if [[ -z "$IMAGE_TAG" ]]; then IMAGE_TAG="$(default_runtime_tag "$TARGET_ARCH")" fi if [[ -z "$METRICS_IMAGE" ]]; then METRICS_IMAGE="$(default_metrics_tag "$TARGET_ARCH")" fi if [[ -z "$OUT_DIR" ]]; then OUT_DIR="$REPO_ROOT/target/${TARGET_ARCH}-installer" fi if [[ -z "$PACKAGE_PREFIX" ]]; then PACKAGE_PREFIX="$(default_package_prefix "$TARGET_ARCH")" fi TARGET_PLATFORM="$(platform_for_arch "$TARGET_ARCH")" HOST_DATA_DIR_DEFAULT="$(default_host_data_dir "$TARGET_ARCH")" COMPOSE_PROJECT_DEFAULT="$(default_compose_project "$TARGET_ARCH")" METRICS_INSTANCE_DEFAULT="$(default_metrics_instance "$TARGET_ARCH")" if [[ -z "$IMAGE_TAR" ]]; then IMAGE_TAR="$(default_saved_image_tar "$IMAGE_TAG")" fi if [[ -z "$METRICS_IMAGE_TAR" ]]; then METRICS_IMAGE_TAR="$(default_saved_image_tar "$METRICS_IMAGE")" fi [[ -n "$IMAGE_TAR" && -f "$IMAGE_TAR" ]] || { cat >&2 <&2 </dev/null || echo unknown)" timestamp="$(date -u +%Y%m%dT%H%M%SZ)" package_name="${PACKAGE_PREFIX}-${timestamp}-${commit}" stage="$OUT_DIR/$package_name" tar_path="$OUT_DIR/$package_name.tar.gz" rm -rf "$stage" rsync -a --delete "$TEMPLATE_DIR"/ "$stage"/ mkdir -p "$stage/images" cp "$IMAGE_TAR" "$stage/images/" cp "$METRICS_IMAGE_TAR" "$stage/images/" monitor_image_stage="$OUT_DIR/.monitor-images-$TARGET_ARCH-$timestamp" rm -rf "$monitor_image_stage" mkdir -p "$monitor_image_stage" prometheus_tar="$(save_image_if_needed "$PROMETHEUS_IMAGE" "$PROMETHEUS_IMAGE_TAR" "$monitor_image_stage" "prometheus" "$TARGET_PLATFORM")" grafana_tar="$(save_image_if_needed "$GRAFANA_IMAGE" "$GRAFANA_IMAGE_TAR" "$monitor_image_stage" "grafana" "$TARGET_PLATFORM")" cp "$prometheus_tar" "$stage/images/" cp "$grafana_tar" "$stage/images/" if [[ -f "$stage/.env.example" ]]; then replace_or_append_env "$stage/.env.example" "PACKAGE_ARCH" "$TARGET_ARCH" replace_or_append_env "$stage/.env.example" "PACKAGE_PLATFORM" "$TARGET_PLATFORM" replace_or_append_env "$stage/.env.example" "COMPOSE_PROJECT_NAME" "$COMPOSE_PROJECT_DEFAULT" replace_or_append_env "$stage/.env.example" "RPKI_IMAGE" "$IMAGE_TAG" replace_or_append_env "$stage/.env.example" "RPKI_PLATFORM" "$TARGET_PLATFORM" replace_or_append_env "$stage/.env.example" "METRICS_IMAGE" "$METRICS_IMAGE" replace_or_append_env "$stage/.env.example" "METRICS_PLATFORM" "$TARGET_PLATFORM" replace_or_append_env "$stage/.env.example" "HOST_DATA_DIR" "$HOST_DATA_DIR_DEFAULT" replace_or_append_env "$stage/.env.example" "ALLOW_CROSS_ARCH" "0" replace_or_append_env "$stage/.env.example" "METRICS_INSTANCE" "$METRICS_INSTANCE_DEFAULT" replace_or_append_env "$stage/.env.example" "MONITOR_PLATFORM" "$TARGET_PLATFORM" replace_or_append_env "$stage/.env.example" "PROMETHEUS_IMAGE" "$PROMETHEUS_IMAGE" replace_or_append_env "$stage/.env.example" "GRAFANA_IMAGE" "$GRAFANA_IMAGE" fi replace_text_in_file "$stage/docs/README.zh-CN.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" replace_text_in_file "$stage/docs/README.en.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" replace_text_in_file "$stage/docs/operations.zh-CN.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" replace_text_in_file "$stage/docs/operations.en.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" replace_text_in_file "$stage/docs/troubleshooting.zh-CN.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" replace_text_in_file "$stage/docs/troubleshooting.en.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" replace_text_in_file "$stage/docs/README.zh-CN.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" replace_text_in_file "$stage/docs/README.en.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" replace_text_in_file "$stage/docs/troubleshooting.zh-CN.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" replace_text_in_file "$stage/docs/troubleshooting.en.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" replace_text_in_file "$stage/docs/README.zh-CN.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" replace_text_in_file "$stage/docs/README.en.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" replace_text_in_file "$stage/docs/operations.zh-CN.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" replace_text_in_file "$stage/docs/operations.en.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" replace_text_in_file "$stage/docs/troubleshooting.zh-CN.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" replace_text_in_file "$stage/docs/troubleshooting.en.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" runtime_tar_sha256="$(sha256sum "$IMAGE_TAR" | awk '{print $1}')" metrics_tar_sha256="$(sha256sum "$METRICS_IMAGE_TAR" | awk '{print $1}')" prometheus_tar_sha256="$(sha256sum "$prometheus_tar" | awk '{print $1}')" grafana_tar_sha256="$(sha256sum "$grafana_tar" | awk '{print $1}')" cat > "$stage/PACKAGE-MANIFEST.env" </dev/null | wc -l | tr -d ' ') PACKAGE_ARCH=$TARGET_ARCH PACKAGE_PLATFORM=$TARGET_PLATFORM package_arch=$TARGET_ARCH package_platform=$TARGET_PLATFORM cross_arch_default=deny cross_arch_enable_var=ALLOW_CROSS_ARCH RPKI_IMAGE=$IMAGE_TAG RPKI_PLATFORM=$TARGET_PLATFORM image_tag=$IMAGE_TAG image_tar=$(basename "$IMAGE_TAR") image_tar_size_bytes=$(wc -c < "$IMAGE_TAR") image_tar_sha256=$runtime_tar_sha256 rpki_image=$IMAGE_TAG rpki_platform=$TARGET_PLATFORM METRICS_IMAGE=$METRICS_IMAGE METRICS_PLATFORM=$TARGET_PLATFORM metrics_image=$METRICS_IMAGE metrics_image_tar=$(basename "$METRICS_IMAGE_TAR") metrics_image_tar_size_bytes=$(wc -c < "$METRICS_IMAGE_TAR") metrics_image_tar_sha256=$metrics_tar_sha256 PROMETHEUS_IMAGE=$PROMETHEUS_IMAGE prometheus_image=$PROMETHEUS_IMAGE prometheus_image_tar=$(basename "$prometheus_tar") prometheus_image_tar_size_bytes=$(wc -c < "$prometheus_tar") prometheus_image_tar_sha256=$prometheus_tar_sha256 GRAFANA_IMAGE=$GRAFANA_IMAGE grafana_image=$GRAFANA_IMAGE grafana_image_tar=$(basename "$grafana_tar") grafana_image_tar_size_bytes=$(wc -c < "$grafana_tar") grafana_image_tar_sha256=$grafana_tar_sha256 target_platform=$TARGET_PLATFORM MONITOR_PLATFORM=$TARGET_PLATFORM rpki_image_tar=$(basename "$IMAGE_TAR") monitor_platform=$TARGET_PLATFORM metrics_platform=$TARGET_PLATFORM default_host_data_dir=$HOST_DATA_DIR_DEFAULT default_compose_project_name=$COMPOSE_PROJECT_DEFAULT default_metrics_instance=$METRICS_INSTANCE_DEFAULT EOF cat > "$stage/PACKAGE-SUMMARY.txt" < "$OUT_DIR/$package_name.summary.env" echo "package built: $tar_path"