#!/usr/bin/env bash set -euo pipefail repo_root="$( git rev-parse --show-toplevel 2>/dev/null || { cd "$(dirname "${BASH_SOURCE[0]}")/../.." pwd } )" input_dir="${BURROW_PACKAGE_INPUT_DIR:-${repo_root}/dist/packages}" output_dir="${BURROW_PACKAGE_REPOSITORY_DIR:-${repo_root}/publish/repositories}" channel="${BURROW_PACKAGE_CHANNEL:-stable}" suite="${BURROW_APT_SUITE:-stable}" component="${BURROW_APT_COMPONENT:-main}" apt_arch="${BURROW_APT_ARCH:-${BURROW_PACKAGE_ARCH:-amd64}}" rpm_arch="${BURROW_RPM_ARCH:-x86_64}" pacman_arch="${BURROW_PACMAN_ARCH:-x86_64}" repo_name="${BURROW_ARCH_REPO_NAME:-burrow}" generic_kms_public_key_pem="${BURROW_PACKAGE_REPO_KMS_PUBLIC_KEY_PEM:-}" generic_kms_key="${BURROW_PACKAGE_REPO_KMS_KEY:-}" apt_kms_public_key_pem="${BURROW_APT_REPO_KMS_PUBLIC_KEY_PEM:-$generic_kms_public_key_pem}" apt_kms_key="${BURROW_APT_REPO_KMS_KEY:-${generic_kms_key:-package-apt-repository}}" rpm_kms_public_key_pem="${BURROW_RPM_REPO_KMS_PUBLIC_KEY_PEM:-$generic_kms_public_key_pem}" rpm_kms_key="${BURROW_RPM_REPO_KMS_KEY:-${generic_kms_key:-package-rpm-repository}}" pacman_kms_public_key_pem="${BURROW_PACMAN_REPO_KMS_PUBLIC_KEY_PEM:-$generic_kms_public_key_pem}" pacman_kms_key="${BURROW_PACMAN_REPO_KMS_KEY:-${generic_kms_key:-package-pacman-repository}}" usage() { cat <<'EOF' Usage: Scripts/package/build-repositories.sh [apt|rpm|arch|keys|all] Builds native Linux package repositories from existing package artifacts. Inputs: BURROW_PACKAGE_INPUT_DIR directory containing .deb, .rpm, and .pkg.tar.* files BURROW_PACKAGE_REPOSITORY_DIR output directory, default publish/repositories BURROW_APT_REPO_KMS_PUBLIC_KEY_PEM Google KMS public key PEM for APT OpenPGP signatures BURROW_APT_REPO_KMS_KEY Google KMS key name for APT signatures BURROW_RPM_REPO_KMS_PUBLIC_KEY_PEM Google KMS public key PEM for RPM repository signatures BURROW_RPM_REPO_KMS_KEY Google KMS key name for RPM repository signatures BURROW_PACMAN_REPO_KMS_PUBLIC_KEY_PEM Google KMS public key PEM for pacman signatures BURROW_PACMAN_REPO_KMS_KEY Google KMS key name for pacman signatures BURROW_PACKAGE_REPO_KMS_PUBLIC_KEY_PEM fallback public key PEM for all formats BURROW_PACKAGE_REPO_KMS_KEY fallback KMS key name for all formats BURROW_KMS_KEYRING/BURROW_KMS_LOCATION/BURROW_KMS_VERSION/GOOGLE_CLOUD_PROJECT The repository metadata is signed with Google KMS-backed OpenPGP detached signatures when each format's KMS public key and key name are configured. EOF } if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then usage exit 0 fi mode="${1:-all}" sha256_file() { local file="$1" if command -v shasum >/dev/null 2>&1; then shasum -a 256 "$file" > "${file}.sha256" else sha256sum "$file" > "${file}.sha256" fi } sign_file() { local file="$1" local output="${2:-${file}.sig}" local armor="${3:-0}" local kms_key="$4" local kms_public_key_pem="$5" if [[ -z "$kms_public_key_pem" || -z "$kms_key" ]]; then echo "warning: KMS OpenPGP signing disabled for ${file}; set the ${file} repository KMS public key and key name" >&2 return 0 fi args=( "${repo_root}/Scripts/package/google-kms-openpgp.py" detach-sign --public-key-pem "$kms_public_key_pem" --kms-key "$kms_key" --input "$file" --output "$output" ) if [[ "$armor" == "1" ]]; then args+=(--armor) fi "${args[@]}" } write_public_key() { local name="$1" local kms_key="$2" local kms_public_key_pem="$3" local user_id="$4" local output="${output_dir}/keys/${name}.asc" if [[ -z "$kms_public_key_pem" || -z "$kms_key" ]]; then echo "warning: KMS OpenPGP public key export disabled for ${name}; set the repository KMS public key and key name" >&2 return 0 fi mkdir -p "$(dirname "$output")" "${repo_root}/Scripts/package/google-kms-openpgp.py" \ public-key \ --public-key-pem "$kms_public_key_pem" \ --kms-key "$kms_key" \ --user-id "$user_id" \ --output "$output" \ --armor } write_release_file() { local release_path="$1" local dist_dir="$2" local now now="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')" { printf 'Origin: Burrow\n' printf 'Label: Burrow\n' printf 'Suite: %s\n' "$suite" printf 'Codename: %s\n' "$suite" printf 'Date: %s\n' "$now" printf 'Architectures: %s\n' "$apt_arch" printf 'Components: %s\n' "$component" printf 'Description: Burrow native package repository\n' printf 'SHA256:\n' ( cd "$dist_dir" find . -type f ! -name Release -print | LC_ALL=C sort | while read -r file; do clean="${file#./}" sum="$(sha256sum "$clean" | awk '{print $1}')" size="$(wc -c < "$clean" | tr -d ' ')" printf ' %s %s %s\n' "$sum" "$size" "$clean" done ) } > "$release_path" } build_apt() { local root binary_dir pool_dir release_dir release_file root="${output_dir}/apt" pool_dir="${root}/pool/${component}/b/burrow" binary_dir="${root}/dists/${suite}/${component}/binary-${apt_arch}" release_dir="${root}/dists/${suite}" mkdir -p "$pool_dir" "$binary_dir" "$release_dir" find "$input_dir" -type f -name '*.deb' -exec cp {} "$pool_dir/" \; 2>/dev/null || true if ! find "$pool_dir" -type f -name '*.deb' -print -quit | grep -q .; then echo "warning: no .deb packages found under ${input_dir}; apt repository contains README only" >&2 printf 'Place Burrow .deb artifacts in this repository pool before publishing.\n' > "${root}/README.txt" return 0 fi if ! command -v dpkg-scanpackages >/dev/null 2>&1; then echo "error: dpkg-scanpackages is required to build the apt repository" >&2 exit 127 fi ( cd "$root" dpkg-scanpackages --arch "$apt_arch" "pool/${component}" /dev/null > "dists/${suite}/${component}/binary-${apt_arch}/Packages" gzip -9cn "dists/${suite}/${component}/binary-${apt_arch}/Packages" > "dists/${suite}/${component}/binary-${apt_arch}/Packages.gz" ) release_file="${release_dir}/Release" write_release_file "$release_file" "$release_dir" sign_file "$release_file" "${release_file}.gpg" 1 "$apt_kms_key" "$apt_kms_public_key_pem" sha256_file "$release_file" } build_rpm() { local root repomd root="${output_dir}/rpm/${channel}/${rpm_arch}" mkdir -p "$root" find "$input_dir" -type f -name '*.rpm' -exec cp {} "$root/" \; 2>/dev/null || true if ! find "$root" -maxdepth 1 -type f -name '*.rpm' -print -quit | grep -q .; then echo "warning: no .rpm packages found under ${input_dir}; rpm repository contains README only" >&2 printf 'Place Burrow .rpm artifacts in this directory before publishing.\n' > "${root}/README.txt" return 0 fi if ! command -v createrepo_c >/dev/null 2>&1; then echo "error: createrepo_c is required to build the rpm repository" >&2 exit 127 fi createrepo_c "$root" repomd="${root}/repodata/repomd.xml" sign_file "$repomd" "${repomd}.asc" 1 "$rpm_kms_key" "$rpm_kms_public_key_pem" sha256_file "$repomd" } build_arch() { local root db root="${output_dir}/arch/${repo_name}/${pacman_arch}" mkdir -p "$root" find "$input_dir" -type f \( -name '*.pkg.tar.zst' -o -name '*.pkg.tar.xz' -o -name '*.pkg.tar.gz' \) -exec cp {} "$root/" \; 2>/dev/null || true if ! find "$root" -maxdepth 1 -type f -name '*.pkg.tar.*' -print -quit | grep -q .; then echo "warning: no Arch packages found under ${input_dir}; pacman repository contains README only" >&2 printf 'Place Burrow .pkg.tar.* artifacts in this directory before publishing.\n' > "${root}/README.txt" return 0 fi if ! command -v repo-add >/dev/null 2>&1; then echo "error: repo-add is required to build the Arch repository" >&2 exit 127 fi ( cd "$root" repo-add "${repo_name}.db.tar.gz" ./*.pkg.tar.* ) db="${root}/${repo_name}.db.tar.gz" sign_file "$db" "${db}.sig" 0 "$pacman_kms_key" "$pacman_kms_public_key_pem" find "$root" -maxdepth 1 -type f -name '*.pkg.tar.*' ! -name '*.sig' -print | while read -r package; do sign_file "$package" "${package}.sig" 0 "$pacman_kms_key" "$pacman_kms_public_key_pem" done sha256_file "$db" } build_keys() { write_public_key "burrow-package-apt-repository" "$apt_kms_key" "$apt_kms_public_key_pem" "Burrow APT Package Repository " write_public_key "burrow-package-rpm-repository" "$rpm_kms_key" "$rpm_kms_public_key_pem" "Burrow RPM Package Repository " write_public_key "burrow-package-pacman-repository" "$pacman_kms_key" "$pacman_kms_public_key_pem" "Burrow pacman Package Repository " } mkdir -p "$output_dir" case "$mode" in apt) build_apt ;; rpm) build_rpm ;; arch) build_arch ;; keys) build_keys ;; all) build_keys build_apt build_rpm build_arch ;; *) echo "unknown repository mode: ${mode}" >&2 exit 64 ;; esac