Files
agent-pet-companion/script/validate_github_release_artifacts.sh
2026-08-10 18:41:19 +08:00

160 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ARTIFACT_DIR=""
VERSION=""
BUILD=""
COMMIT=""
REQUIRE_NATIVE_ARCHITECTURE=""
usage() {
cat <<'EOF'
usage: validate_github_release_artifacts.sh \
--directory PATH \
--version X.Y.Z \
--build POSITIVE_INTEGER \
--commit FULL_LOWERCASE_GIT_COMMIT \
[--require-native-architecture arm64|x86_64]
Validates the exact three-file GitHub Release set: two ad-hoc-signed thin App
ZIPs and one checksum inventory covering exactly those ZIPs. Every ZIP receives
a bounded safety preflight before extraction.
The required version, build, and full commit are bound to Info.plist,
runtime-manifest.json, and the shared App/PetCore/CLI build identity.
EOF
}
while (($# > 0)); do
case "$1" in
--directory)
(($# >= 2)) || { usage >&2; exit 2; }
ARTIFACT_DIR="$2"
shift 2
;;
--version)
(($# >= 2)) || { usage >&2; exit 2; }
VERSION="$2"
shift 2
;;
--build)
(($# >= 2)) || { usage >&2; exit 2; }
BUILD="$2"
shift 2
;;
--commit)
(($# >= 2)) || { usage >&2; exit 2; }
COMMIT="$2"
shift 2
;;
--require-native-architecture)
(($# >= 2)) || { usage >&2; exit 2; }
REQUIRE_NATIVE_ARCHITECTURE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
printf 'unknown argument: %s\n' "$1" >&2
usage >&2
exit 2
;;
esac
done
[[ -d "$ARTIFACT_DIR" && ! -L "$ARTIFACT_DIR" ]] || {
echo 'GitHub Release validation requires a regular artifact directory' >&2
exit 2
}
[[ "$VERSION" =~ ^[0-9]+([.][0-9]+){2}$ ]] || {
echo '--version must be a three-component semantic version' >&2
exit 2
}
[[ "$BUILD" =~ ^[1-9][0-9]*$ ]] || {
echo '--build must be a positive integer' >&2
exit 2
}
[[ "$COMMIT" =~ ^[0-9a-f]{40}$ ]] || {
echo '--commit must be a full lowercase Git commit' >&2
exit 2
}
case "$REQUIRE_NATIVE_ARCHITECTURE" in
"") ;;
arm64|x86_64) ;;
*) echo '--require-native-architecture must be arm64 or x86_64' >&2; exit 2 ;;
esac
for dependency in ditto python3 shasum; do
command -v "$dependency" >/dev/null 2>&1 || {
printf 'GitHub Release artifact validation requires %s\n' "$dependency" >&2
exit 1
}
done
if [[ -n "$REQUIRE_NATIVE_ARCHITECTURE" ]]; then
HOST_ARCH="$(uname -m)"
case "$HOST_ARCH" in
aarch64) HOST_ARCH="arm64" ;;
amd64) HOST_ARCH="x86_64" ;;
esac
[[ "$HOST_ARCH" == "$REQUIRE_NATIVE_ARCHITECTURE" ]] || {
printf 'native packaged validation requires %s hardware; runner reports %s\n' \
"$REQUIRE_NATIVE_ARCHITECTURE" "$HOST_ARCH" >&2
exit 1
}
fi
"$ROOT_DIR/script/validate_release_artifact_metadata.py" \
--directory "$ARTIFACT_DIR" \
--version "$VERSION"
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/apc-github-release-validation.XXXXXX")"
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
if [[ -n "$REQUIRE_NATIVE_ARCHITECTURE" ]]; then
ARCHITECTURES=("$REQUIRE_NATIVE_ARCHITECTURE")
else
ARCHITECTURES=(arm64 x86_64)
fi
for architecture in "${ARCHITECTURES[@]}"; do
archive_name="AgentPetCompanion-$VERSION-macos-$architecture.zip"
archive="$ARTIFACT_DIR/$archive_name"
extract_dir="$TMP_DIR/$architecture"
# This must remain before every extraction of downloaded release content.
"$ROOT_DIR/script/validate_release_zip.py" --archive "$archive"
mkdir -p "$extract_dir"
ditto -x -k "$archive" "$extract_dir"
extracted_app="$extract_dir/AgentPetCompanion.app"
[[ -d "$extracted_app" && ! -L "$extracted_app" ]] || {
echo 'GitHub Release ZIP did not extract the expected regular App bundle' >&2
exit 1
}
"$ROOT_DIR/script/validate_release_identity.py" \
--app "$extracted_app" \
--architecture "$architecture" \
--version "$VERSION" \
--build "$BUILD" \
--commit "$COMMIT"
"$ROOT_DIR/script/validate_app_bundle.sh" \
--github-release \
--architecture "$architecture" \
"$extracted_app"
done
if [[ -n "$REQUIRE_NATIVE_ARCHITECTURE" ]]; then
printf 'GitHub Release exact inventory and native %s artifact validated for one exact identity\n' \
"$REQUIRE_NATIVE_ARCHITECTURE"
else
echo 'GitHub Release artifacts validated for both architectures and one exact identity'
fi