#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" PROFILE="${PROFILE:-release}" OUT_DIR="${OUT_DIR:-$REPO_ROOT/target/portable-soak}" PACKAGE_PREFIX="${PACKAGE_PREFIX:-portable-soak}" PACKAGE_DIR_NAME="${PACKAGE_DIR_NAME:-portable-soak}" usage() { cat <<'USAGE' Usage: scripts/soak/build_portable_soak_package.sh [--out-dir ] [--profile ] Requires release binaries to already exist. Build them first, for example: cargo build --release --bin rpki --bin rpki_daemon --bin db_stats USAGE } die() { echo "error: $*" >&2 exit 2 } while [[ $# -gt 0 ]]; do case "$1" in --out-dir) shift OUT_DIR="${1:?--out-dir requires a value}" ;; --profile) shift PROFILE="${1:?--profile requires a value}" ;; --help|-h) usage exit 0 ;; *) die "unknown argument: $1" ;; esac shift done command -v python3 >/dev/null 2>&1 || die "python3 is required" command -v tar >/dev/null 2>&1 || die "tar is required" if [[ "$PROFILE" == "release" ]]; then TARGET_BIN_DIR="$REPO_ROOT/target/release" else TARGET_BIN_DIR="$REPO_ROOT/target/$PROFILE" fi REQUIRED_BINS=(rpki rpki_daemon db_stats) OPTIONAL_BINS=( ccr_dump ccr_state_compare ccr_to_compare_views ccr_to_routinator_csv ccr_verify cir_drop_report cir_dump_reject_list cir_extract_inputs cir_materialize cir_probe_rpki_client_cache cir_state_compare rrdp_state_dump ) for binary_name in "${REQUIRED_BINS[@]}"; do [[ -x "$TARGET_BIN_DIR/$binary_name" ]] || die "missing required binary: $TARGET_BIN_DIR/$binary_name" done mkdir -p "$OUT_DIR" GIT_SHA="$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || printf 'unknown')" TIMESTAMP="$(date -u +%Y%m%dT%H%M%SZ)" PACKAGE_NAME="${PACKAGE_PREFIX}-${TIMESTAMP}-${GIT_SHA}" BUILD_ROOT="$REPO_ROOT/target/portable-soak-build" STAGE_DIR="$BUILD_ROOT/$PACKAGE_DIR_NAME" ARCHIVE_PATH="$OUT_DIR/$PACKAGE_NAME.tar.gz" rm -rf "$STAGE_DIR" "$ARCHIVE_PATH" mkdir -p "$STAGE_DIR/bin" "$STAGE_DIR/fixtures" "$STAGE_DIR/scripts" \ "$STAGE_DIR/runs" "$STAGE_DIR/state" "$STAGE_DIR/logs" "$STAGE_DIR/tmp" install -m 0755 "$SCRIPT_DIR/run_soak.sh" "$STAGE_DIR/run_soak.sh" install -m 0644 "$SCRIPT_DIR/portable-soak.env.example" "$STAGE_DIR/.env" install -m 0644 "$SCRIPT_DIR/portable-soak.env.example" "$STAGE_DIR/portable-soak.env.example" COPIED_BIN_LIST="$STAGE_DIR/copied-binaries.txt" MISSING_OPTIONAL_BIN_LIST="$STAGE_DIR/missing-optional-binaries.txt" : > "$COPIED_BIN_LIST" : > "$MISSING_OPTIONAL_BIN_LIST" for binary_name in "${REQUIRED_BINS[@]}"; do install -m 0755 "$TARGET_BIN_DIR/$binary_name" "$STAGE_DIR/bin/$binary_name" printf '%s\n' "$binary_name" >> "$COPIED_BIN_LIST" done for binary_name in "${OPTIONAL_BINS[@]}"; do if [[ -x "$TARGET_BIN_DIR/$binary_name" ]]; then install -m 0755 "$TARGET_BIN_DIR/$binary_name" "$STAGE_DIR/bin/$binary_name" printf '%s\n' "$binary_name" >> "$COPIED_BIN_LIST" else printf '%s\n' "$binary_name" >> "$MISSING_OPTIONAL_BIN_LIST" fi done cp -a "$REPO_ROOT/tests/fixtures/tal" "$STAGE_DIR/fixtures/" cp -a "$REPO_ROOT/tests/fixtures/ta" "$STAGE_DIR/fixtures/" cp -a "$REPO_ROOT/scripts/periodic" "$STAGE_DIR/scripts/" cp -a "$REPO_ROOT/scripts/cir" "$STAGE_DIR/scripts/" find "$STAGE_DIR/scripts" -type d -name __pycache__ -prune -exec rm -rf {} + (cd "$STAGE_DIR" && find fixtures -type f | sort > fixtures.txt) (cd "$STAGE_DIR" && find scripts -type f | sort > scripts.txt) GIT_DIRTY="false" if [[ -n "$(git -C "$REPO_ROOT" status --short 2>/dev/null || true)" ]]; then GIT_DIRTY="true" fi GIT_STATUS="$(git -C "$REPO_ROOT" status --short 2>/dev/null || true)" python3 - "$STAGE_DIR/manifest.json" "$PACKAGE_NAME" "$TIMESTAMP" "$REPO_ROOT" "$GIT_SHA" \ "$GIT_DIRTY" "$PROFILE" "$TARGET_BIN_DIR" "$GIT_STATUS" <<'PY' import json import pathlib import sys ( manifest_path, package_name, created_at, repo_root, git_sha, git_dirty, profile, target_bin_dir, git_status, ) = sys.argv[1:] stage_dir = pathlib.Path(manifest_path).parent def read_lines(name): path = stage_dir / name if not path.exists(): return [] return [line for line in path.read_text(encoding="utf-8").splitlines() if line] manifest = { "packageName": package_name, "createdAtUtc": created_at, "sourceRepo": repo_root, "gitCommit": git_sha, "gitDirty": git_dirty == "true", "gitStatusShort": git_status.splitlines(), "rustProfile": profile, "targetBinDir": target_bin_dir, "copiedBinaries": read_lines("copied-binaries.txt"), "missingOptionalBinaries": read_lines("missing-optional-binaries.txt"), "fixtures": read_lines("fixtures.txt"), "scripts": read_lines("scripts.txt"), } pathlib.Path(manifest_path).write_text( json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8", ) PY tar -C "$BUILD_ROOT" -czf "$ARCHIVE_PATH" "$PACKAGE_DIR_NAME" printf '%s\n' "$ARCHIVE_PATH"