641 lines
21 KiB
Bash
Executable File
641 lines
21 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
APP_NAME="AgentPetCompanion"
|
|
BUNDLE_ID="dev.agentpet.companion"
|
|
APP_ICON_NAME="AgentPetCompanion.icns"
|
|
MIN_SYSTEM_VERSION="14.0"
|
|
CONFIGURATION="debug"
|
|
UNIVERSAL=0
|
|
TARGET_ARCH=""
|
|
OUTPUT_PATH=""
|
|
INTERACTION_ATTESTATION_SOURCE=""
|
|
VALIDATION_PROFILE="full"
|
|
SOURCE_VERSION="$(
|
|
awk -F'"' '/^version = / {print $2; exit}' "$ROOT_DIR/crates/petcore/Cargo.toml"
|
|
)"
|
|
RELEASE_VERSION="${APC_RELEASE_VERSION:-$SOURCE_VERSION}"
|
|
RELEASE_BUILD="${APC_RELEASE_BUILD:-1}"
|
|
BUILD_ID="${APC_BUILD_ID:-}"
|
|
RELEASE_CHANNEL="${APC_RELEASE_CHANNEL:-develop}"
|
|
SIGN_DEVELOPMENT=1
|
|
CREATE_DEVELOP_ARCHIVE=0
|
|
CARGO_COMMAND=(cargo)
|
|
CARGO_RUSTC=""
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
usage: build_app_bundle.sh [--configuration debug|release] [--arch arm64|x86_64] [--universal] [--archive] [--unsigned] [--output PATH] [--interaction-attestation ABSOLUTE_PATH] [--validation static|full]
|
|
|
|
Builds an ad-hoc signed development app bundle by default. --archive also
|
|
creates and verifies a `-develop.zip` for informal handoff. --unsigned disables
|
|
development signing and cannot be combined with --archive. --arch cross-builds
|
|
one thin architecture-specific bundle. --universal remains available for local
|
|
inspection, requires release mode, and cannot be combined with --arch. This
|
|
script never launches the app.
|
|
EOF
|
|
}
|
|
|
|
while (($# > 0)); do
|
|
case "$1" in
|
|
--configuration)
|
|
(($# >= 2)) || { usage >&2; exit 2; }
|
|
CONFIGURATION="$2"
|
|
shift 2
|
|
;;
|
|
--universal)
|
|
UNIVERSAL=1
|
|
shift
|
|
;;
|
|
--arch)
|
|
(($# >= 2)) || { usage >&2; exit 2; }
|
|
TARGET_ARCH="$2"
|
|
shift 2
|
|
;;
|
|
--arch=*)
|
|
TARGET_ARCH="${1#--arch=}"
|
|
shift
|
|
;;
|
|
--archive)
|
|
CREATE_DEVELOP_ARCHIVE=1
|
|
shift
|
|
;;
|
|
--unsigned)
|
|
SIGN_DEVELOPMENT=0
|
|
shift
|
|
;;
|
|
--output)
|
|
(($# >= 2)) || { usage >&2; exit 2; }
|
|
OUTPUT_PATH="$2"
|
|
shift 2
|
|
;;
|
|
--interaction-attestation)
|
|
(($# >= 2)) || { usage >&2; exit 2; }
|
|
INTERACTION_ATTESTATION_SOURCE="$2"
|
|
shift 2
|
|
;;
|
|
--validation)
|
|
(($# >= 2)) || { usage >&2; exit 2; }
|
|
VALIDATION_PROFILE="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
printf 'unknown argument: %s\n' "$1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ "$CREATE_DEVELOP_ARCHIVE" == "1" && "$SIGN_DEVELOPMENT" != "1" ]]; then
|
|
echo '--archive cannot be combined with --unsigned' >&2
|
|
exit 2
|
|
fi
|
|
if [[ -n "$INTERACTION_ATTESTATION_SOURCE" && "$INTERACTION_ATTESTATION_SOURCE" != /* ]]; then
|
|
echo 'interaction attestation input must be an absolute path' >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$UNIVERSAL" == "1" && -n "$TARGET_ARCH" ]]; then
|
|
echo '--universal cannot be combined with --arch' >&2
|
|
exit 2
|
|
fi
|
|
|
|
case "$CONFIGURATION" in
|
|
debug|release) ;;
|
|
*) printf 'invalid configuration: %s\n' "$CONFIGURATION" >&2; exit 2 ;;
|
|
esac
|
|
case "$TARGET_ARCH" in
|
|
"")
|
|
;;
|
|
arm64|aarch64)
|
|
TARGET_ARCH="arm64"
|
|
;;
|
|
x86_64|x64|amd64|intel)
|
|
TARGET_ARCH="x86_64"
|
|
;;
|
|
*)
|
|
printf 'unsupported architecture: %s\n' "$TARGET_ARCH" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
case "$VALIDATION_PROFILE" in
|
|
static|full) ;;
|
|
*) echo '--validation must be static or full' >&2; exit 2 ;;
|
|
esac
|
|
if [[ "$UNIVERSAL" == "1" && "$CONFIGURATION" != "release" ]]; then
|
|
echo '--universal requires --configuration release' >&2
|
|
exit 2
|
|
fi
|
|
if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+([.][0-9]+){0,2}$ ]]; then
|
|
echo 'APC_RELEASE_VERSION must contain one to three dot-separated numeric components' >&2
|
|
exit 2
|
|
fi
|
|
if [[ ! "$RELEASE_BUILD" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo 'APC_RELEASE_BUILD must be a positive integer' >&2
|
|
exit 2
|
|
fi
|
|
case "$RELEASE_CHANNEL" in
|
|
develop|release) ;;
|
|
*) echo 'APC_RELEASE_CHANNEL must be develop or release' >&2; exit 2 ;;
|
|
esac
|
|
if [[ -z "$BUILD_ID" ]]; then
|
|
if [[ "$RELEASE_CHANNEL" == "release" ]]; then
|
|
echo 'release bundle builds require an explicit APC_BUILD_ID' >&2
|
|
exit 2
|
|
fi
|
|
DEVELOPMENT_BUILD_VARIANT="$CONFIGURATION|${TARGET_ARCH:-$(uname -m)}|universal=$UNIVERSAL|signed=$SIGN_DEVELOPMENT"
|
|
DEVELOPMENT_SOURCE_FINGERPRINT="$(
|
|
"$ROOT_DIR/script/validation_fingerprint.py" \
|
|
--root "$ROOT_DIR" \
|
|
--scope runtime \
|
|
--extra "$DEVELOPMENT_BUILD_VARIANT"
|
|
)"
|
|
BUILD_ID="${RELEASE_VERSION}.${RELEASE_BUILD}.develop.${DEVELOPMENT_SOURCE_FINGERPRINT:0:16}"
|
|
fi
|
|
if [[ ! "$BUILD_ID" =~ ^[A-Za-z0-9._+-]{1,128}$ ]]; then
|
|
echo 'APC_BUILD_ID must be 1-128 ASCII letters, digits, dots, underscores, pluses, or hyphens' >&2
|
|
exit 2
|
|
fi
|
|
|
|
# The macOS 26 UI contract is compile-time as well as runtime. An older Swift
|
|
# compiler removes the native Liquid Glass branches, while an older SDK links
|
|
# the entire App into the legacy control appearance. Fail before doing any
|
|
# expensive Rust or Swift work so development and Release cannot diverge.
|
|
"$ROOT_DIR/script/validate_macos_build_contract.py" toolchain
|
|
|
|
SWIFT_DIR="$ROOT_DIR/apps/macos"
|
|
APP_ICON_SOURCE="$ROOT_DIR/logo/macos/AgentPetCompanionTransparent.icns"
|
|
DIST_DIR="$ROOT_DIR/dist"
|
|
APP_BUNDLE="${OUTPUT_PATH:-$DIST_DIR/$APP_NAME.app}"
|
|
case "$APP_BUNDLE" in
|
|
*.app) ;;
|
|
*) echo 'bundle output must end in .app' >&2; exit 2 ;;
|
|
esac
|
|
|
|
APP_PARENT="$(dirname "$APP_BUNDLE")"
|
|
mkdir -p "$APP_PARENT"
|
|
# Always assemble and sign outside the repository so the bundle copied into
|
|
# dist is complete before local validation. An optional handoff archive is also
|
|
# produced from this clean staging area.
|
|
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/apc-app-bundle.XXXXXX")"
|
|
STAGED_APP="$TMP_DIR/$APP_NAME.app"
|
|
APP_CONTENTS="$STAGED_APP/Contents"
|
|
APP_MACOS="$APP_CONTENTS/MacOS"
|
|
APP_RESOURCES="$APP_CONTENTS/Resources"
|
|
APP_BINARY="$APP_MACOS/$APP_NAME"
|
|
INFO_PLIST="$APP_CONTENTS/Info.plist"
|
|
RUNTIME_MANIFEST="$APP_RESOURCES/runtime-manifest.json"
|
|
INTERACTION_ATTESTATION="$APP_RESOURCES/interaction-attestation.json"
|
|
DEVELOP_ARCHIVE="${APP_BUNDLE%.app}-develop.zip"
|
|
STAGED_DEVELOP_ARCHIVE="$TMP_DIR/$APP_NAME-develop.zip"
|
|
ARCHIVE_VERIFY_DIR=""
|
|
|
|
verify_destination_development_bundle() {
|
|
local attempt
|
|
local forbidden_xattrs
|
|
local diff_log="$TMP_DIR/destination-diff.log"
|
|
local verify_log="$TMP_DIR/destination-codesign.log"
|
|
|
|
# Require a clean, valid destination and give filesystem metadata a bounded
|
|
# settling window before accepting the local App bundle.
|
|
for attempt in 1 2 3; do
|
|
xattr -cr "$APP_BUNDLE"
|
|
if ! codesign --verify --deep --strict "$APP_BUNDLE" >"$verify_log" 2>&1; then
|
|
forbidden_xattrs="$(xattr -lr "$APP_BUNDLE" 2>/dev/null \
|
|
| grep -E 'com[.]apple[.](FinderInfo|ResourceFork):' || true)"
|
|
if [[ -z "$forbidden_xattrs" ]]; then
|
|
cat "$verify_log" >&2
|
|
echo 'destination app signature is invalid after clearing extended attributes' >&2
|
|
return 1
|
|
fi
|
|
continue
|
|
fi
|
|
sleep 0.25
|
|
if codesign --verify --deep --strict "$APP_BUNDLE" >"$verify_log" 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
forbidden_xattrs="$(xattr -lr "$APP_BUNDLE" 2>/dev/null \
|
|
| grep -E 'com[.]apple[.](FinderInfo|ResourceFork):' || true)"
|
|
if [[ -z "$forbidden_xattrs" ]]; then
|
|
cat "$verify_log" >&2
|
|
echo 'destination app signature changed after a clean copy for an unknown reason' >&2
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
# A File Provider may repeatedly re-attach package metadata. Only an explicit
|
|
# handoff archive gives us an independently verified fallback in that case.
|
|
xattr -cr "$APP_BUNDLE"
|
|
if ! codesign --verify --deep --strict "$APP_BUNDLE" >"$verify_log" 2>&1; then
|
|
forbidden_xattrs="$(xattr -lr "$APP_BUNDLE" 2>/dev/null \
|
|
| grep -E 'com[.]apple[.](FinderInfo|ResourceFork):' || true)"
|
|
if [[ -z "$forbidden_xattrs" ]]; then
|
|
cat "$verify_log" >&2
|
|
echo 'destination app signature is invalid after final extended-attribute cleanup' >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
if [[ "$CREATE_DEVELOP_ARCHIVE" != "1" ]]; then
|
|
echo 'destination app metadata did not remain stable; move the workspace out of File Provider storage or rerun with --archive' >&2
|
|
return 1
|
|
fi
|
|
if ! diff -qr "$ARCHIVE_VERIFY_DIR/$APP_NAME.app" "$APP_BUNDLE" >"$diff_log"; then
|
|
cat "$diff_log" >&2
|
|
echo 'destination app payload differs from the strictly verified development archive' >&2
|
|
return 1
|
|
fi
|
|
printf '%s\n' \
|
|
'warning: File Provider repeatedly attached FinderInfo to the destination .app; use the strictly verified -develop.zip as the supported handoff' >&2
|
|
}
|
|
|
|
cleanup() {
|
|
rm -rf "$TMP_DIR"
|
|
if [[ -n "$ARCHIVE_VERIFY_DIR" ]]; then
|
|
rm -rf "$ARCHIVE_VERIFY_DIR"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
copy_swift_resource_bundles() {
|
|
local swift_bin_path="$1"
|
|
while IFS= read -r -d '' bundle; do
|
|
cp -R "$bundle" "$APP_RESOURCES/"
|
|
done < <(find "$swift_bin_path" -maxdepth 1 -type d -name "${APP_NAME}_*.bundle" -print0)
|
|
}
|
|
|
|
select_cargo_for_target() {
|
|
local rust_target="$1"
|
|
local target_libdir
|
|
local rustup_toolchain
|
|
|
|
# Architecture-specific repository builds must use the toolchain pinned by
|
|
# rust-toolchain.toml for every target, even when a Homebrew compiler appears
|
|
# first on PATH. This keeps arm64 and x86_64 release artifacts reproducible.
|
|
if [[ -f "$ROOT_DIR/rust-toolchain.toml" ]] \
|
|
&& command -v rustup >/dev/null 2>&1; then
|
|
rustup_toolchain="$(
|
|
cd "$ROOT_DIR"
|
|
rustup show active-toolchain 2>/dev/null | awk 'NR == 1 {print $1}'
|
|
)"
|
|
if [[ -n "$rustup_toolchain" ]]; then
|
|
target_libdir="$(
|
|
rustup run "$rustup_toolchain" \
|
|
rustc --print target-libdir --target "$rust_target" 2>/dev/null || true
|
|
)"
|
|
if [[ -d "$target_libdir" ]]; then
|
|
CARGO_COMMAND=(rustup run "$rustup_toolchain" cargo)
|
|
CARGO_RUSTC="$(rustup which --toolchain "$rustup_toolchain" rustc)"
|
|
return
|
|
fi
|
|
printf 'missing Rust target %s for %s; run: rustup target add --toolchain %s %s\n' \
|
|
"$rust_target" "$rustup_toolchain" "$rustup_toolchain" "$rust_target" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
target_libdir="$(rustc --print target-libdir --target "$rust_target" 2>/dev/null || true)"
|
|
if [[ -d "$target_libdir" ]]; then
|
|
CARGO_COMMAND=(cargo)
|
|
CARGO_RUSTC=""
|
|
return
|
|
fi
|
|
|
|
# A workspace without rust-toolchain.toml may still have a usable active
|
|
# rustup toolchain even when another compiler appears first on PATH.
|
|
if command -v rustup >/dev/null 2>&1; then
|
|
rustup_toolchain="$(rustup show active-toolchain 2>/dev/null | awk 'NR == 1 {print $1}')"
|
|
if [[ -n "$rustup_toolchain" ]]; then
|
|
target_libdir="$(
|
|
rustup run "$rustup_toolchain" \
|
|
rustc --print target-libdir --target "$rust_target" 2>/dev/null || true
|
|
)"
|
|
if [[ -d "$target_libdir" ]]; then
|
|
CARGO_COMMAND=(rustup run "$rustup_toolchain" cargo)
|
|
CARGO_RUSTC="$(rustup which --toolchain "$rustup_toolchain" rustc)"
|
|
return
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
printf 'missing Rust target %s; run: rustup target add %s\n' \
|
|
"$rust_target" "$rust_target" >&2
|
|
exit 1
|
|
}
|
|
|
|
run_cargo() {
|
|
if [[ -n "$CARGO_RUSTC" ]]; then
|
|
RUSTC="$CARGO_RUSTC" "${CARGO_COMMAND[@]}" "$@"
|
|
else
|
|
"${CARGO_COMMAND[@]}" "$@"
|
|
fi
|
|
}
|
|
|
|
build_native() {
|
|
local cargo_args=(build --workspace --locked)
|
|
local swift_args=(build --product "$APP_NAME")
|
|
local rust_target=""
|
|
local swift_triple=""
|
|
local rust_binary_root="$ROOT_DIR/target/$CONFIGURATION"
|
|
|
|
case "$TARGET_ARCH" in
|
|
arm64)
|
|
rust_target="aarch64-apple-darwin"
|
|
swift_triple="arm64-apple-macosx$MIN_SYSTEM_VERSION"
|
|
;;
|
|
x86_64)
|
|
rust_target="x86_64-apple-darwin"
|
|
swift_triple="x86_64-apple-macosx$MIN_SYSTEM_VERSION"
|
|
;;
|
|
esac
|
|
|
|
if [[ "$CONFIGURATION" == "release" ]]; then
|
|
cargo_args+=(--release)
|
|
swift_args=(build -c release --product "$APP_NAME")
|
|
fi
|
|
if [[ -n "$rust_target" ]]; then
|
|
select_cargo_for_target "$rust_target"
|
|
cargo_args+=(--target "$rust_target")
|
|
swift_args+=(--triple "$swift_triple")
|
|
rust_binary_root="$ROOT_DIR/target/$rust_target/$CONFIGURATION"
|
|
fi
|
|
(cd "$ROOT_DIR" && \
|
|
APC_BUILD_ID="$BUILD_ID" \
|
|
APC_APP_VERSION="$RELEASE_VERSION" \
|
|
APC_APP_BUILD="$RELEASE_BUILD" \
|
|
APC_RELEASE_CHANNEL="$RELEASE_CHANNEL" \
|
|
run_cargo "${cargo_args[@]}")
|
|
(cd "$SWIFT_DIR" && swift "${swift_args[@]}")
|
|
|
|
local swift_bin_path
|
|
swift_bin_path="$(cd "$SWIFT_DIR" && swift "${swift_args[@]}" --show-bin-path)"
|
|
cp "$swift_bin_path/$APP_NAME" "$APP_BINARY"
|
|
copy_swift_resource_bundles "$swift_bin_path"
|
|
cp "$rust_binary_root/petcore" "$APP_RESOURCES/bin/petcore"
|
|
cp "$rust_binary_root/petcore-cli" "$APP_RESOURCES/bin/petcore-cli"
|
|
}
|
|
|
|
write_runtime_manifest() {
|
|
local host_arch
|
|
host_arch="$(uname -m)"
|
|
case "$host_arch" in
|
|
aarch64) host_arch="arm64" ;;
|
|
amd64) host_arch="x86_64" ;;
|
|
esac
|
|
|
|
if [[ -z "$TARGET_ARCH" || "$TARGET_ARCH" == "$host_arch" || "$UNIVERSAL" == "1" ]]; then
|
|
"$APP_RESOURCES/bin/petcore" runtime-manifest >"$RUNTIME_MANIFEST"
|
|
return
|
|
fi
|
|
|
|
# A thin cross-built PetCore may not be executable on the build host. Build
|
|
# the small host-native manifest authority with the same release identity so
|
|
# architecture-specific archives do not depend on Rosetta merely to assemble.
|
|
local cargo_args=(build --locked -p petcore)
|
|
local host_petcore="$ROOT_DIR/target/debug/petcore"
|
|
if [[ "$CONFIGURATION" == "release" ]]; then
|
|
cargo_args+=(--release)
|
|
host_petcore="$ROOT_DIR/target/release/petcore"
|
|
fi
|
|
(cd "$ROOT_DIR" && \
|
|
APC_BUILD_ID="$BUILD_ID" \
|
|
APC_APP_VERSION="$RELEASE_VERSION" \
|
|
APC_APP_BUILD="$RELEASE_BUILD" \
|
|
APC_RELEASE_CHANNEL="$RELEASE_CHANNEL" \
|
|
run_cargo "${cargo_args[@]}")
|
|
"$host_petcore" runtime-manifest >"$RUNTIME_MANIFEST"
|
|
}
|
|
|
|
build_universal() {
|
|
[[ "$(uname -s)" == "Darwin" ]] || {
|
|
echo 'universal macOS builds require Darwin' >&2
|
|
exit 1
|
|
}
|
|
command -v lipo >/dev/null 2>&1 || {
|
|
echo 'universal macOS builds require lipo from Xcode command-line tools' >&2
|
|
exit 1
|
|
}
|
|
|
|
local targets=(aarch64-apple-darwin x86_64-apple-darwin)
|
|
local target
|
|
for target in "${targets[@]}"; do
|
|
select_cargo_for_target "$target"
|
|
if ! (cd "$ROOT_DIR" && \
|
|
APC_BUILD_ID="$BUILD_ID" \
|
|
APC_APP_VERSION="$RELEASE_VERSION" \
|
|
APC_APP_BUILD="$RELEASE_BUILD" \
|
|
APC_RELEASE_CHANNEL="$RELEASE_CHANNEL" \
|
|
run_cargo build --workspace --locked --release --target "$target"); then
|
|
printf 'Rust target %s is unavailable; install both Apple targets before release\n' "$target" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
(cd "$SWIFT_DIR" && swift build -c release --arch arm64 --arch x86_64 --product "$APP_NAME")
|
|
local swift_bin_path
|
|
swift_bin_path="$(cd "$SWIFT_DIR" && swift build -c release --arch arm64 --arch x86_64 --show-bin-path)"
|
|
cp "$swift_bin_path/$APP_NAME" "$APP_BINARY"
|
|
copy_swift_resource_bundles "$swift_bin_path"
|
|
|
|
lipo -create \
|
|
"$ROOT_DIR/target/aarch64-apple-darwin/release/petcore" \
|
|
"$ROOT_DIR/target/x86_64-apple-darwin/release/petcore" \
|
|
-output "$APP_RESOURCES/bin/petcore"
|
|
lipo -create \
|
|
"$ROOT_DIR/target/aarch64-apple-darwin/release/petcore-cli" \
|
|
"$ROOT_DIR/target/x86_64-apple-darwin/release/petcore-cli" \
|
|
-output "$APP_RESOURCES/bin/petcore-cli"
|
|
|
|
for binary in "$APP_BINARY" "$APP_RESOURCES/bin/petcore" "$APP_RESOURCES/bin/petcore-cli"; do
|
|
architectures="$(lipo -archs "$binary")"
|
|
[[ " $architectures " == *' arm64 '* && " $architectures " == *' x86_64 '* ]] || {
|
|
printf 'universal build is missing a required slice: %s (%s)\n' "$binary" "$architectures" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
}
|
|
|
|
mkdir -p "$APP_MACOS" "$APP_RESOURCES/bin" "$APP_RESOURCES/skills"
|
|
[[ -f "$APP_ICON_SOURCE" ]] || {
|
|
echo "missing app icon source: $APP_ICON_SOURCE" >&2
|
|
exit 1
|
|
}
|
|
if [[ -n "$INTERACTION_ATTESTATION_SOURCE" ]]; then
|
|
"$ROOT_DIR/script/validate_interaction_attestation.py" \
|
|
"$INTERACTION_ATTESTATION_SOURCE" \
|
|
--expected-build-id "$BUILD_ID"
|
|
cp "$INTERACTION_ATTESTATION_SOURCE" "$INTERACTION_ATTESTATION"
|
|
"$ROOT_DIR/script/validate_interaction_attestation.py" \
|
|
"$INTERACTION_ATTESTATION" \
|
|
--expected-build-id "$BUILD_ID"
|
|
else
|
|
INTERACTION_PREPARE_ARGS=(--output "$INTERACTION_ATTESTATION")
|
|
if [[ "$RELEASE_CHANNEL" == "develop" ]]; then
|
|
INTERACTION_PREPARE_ARGS+=(--resume)
|
|
fi
|
|
APC_BUILD_ID="$BUILD_ID" \
|
|
APC_APP_VERSION="$RELEASE_VERSION" \
|
|
APC_APP_BUILD="$RELEASE_BUILD" \
|
|
APC_RELEASE_CHANNEL="$RELEASE_CHANNEL" \
|
|
"$ROOT_DIR/script/prepare_interaction_attestation.sh" \
|
|
"${INTERACTION_PREPARE_ARGS[@]}"
|
|
fi
|
|
|
|
if [[ "$UNIVERSAL" == "1" ]]; then
|
|
build_universal
|
|
else
|
|
build_native
|
|
fi
|
|
|
|
cp -R "$ROOT_DIR/skills/agent-pet-studio" "$APP_RESOURCES/skills/agent-pet-studio"
|
|
cp -R "$ROOT_DIR/skills/agent-pet-maker" "$APP_RESOURCES/skills/agent-pet-maker"
|
|
cp "$APP_ICON_SOURCE" "$APP_RESOURCES/$APP_ICON_NAME"
|
|
find "$APP_RESOURCES/skills" -type d -name '__pycache__' -prune -exec rm -rf {} +
|
|
find "$APP_RESOURCES/skills" -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete
|
|
chmod +x "$APP_BINARY" "$APP_RESOURCES/bin/petcore" "$APP_RESOURCES/bin/petcore-cli"
|
|
write_runtime_manifest
|
|
|
|
cat >"$INFO_PLIST" <<PLIST
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CFBundleDevelopmentRegion</key>
|
|
<string>en</string>
|
|
<key>CFBundleExecutable</key>
|
|
<string>$APP_NAME</string>
|
|
<key>CFBundleIdentifier</key>
|
|
<string>$BUNDLE_ID</string>
|
|
<key>CFBundleIconFile</key>
|
|
<string>$APP_ICON_NAME</string>
|
|
<key>CFBundleName</key>
|
|
<string>$APP_NAME</string>
|
|
<key>CFBundlePackageType</key>
|
|
<string>APPL</string>
|
|
<key>CFBundleShortVersionString</key>
|
|
<string>$RELEASE_VERSION</string>
|
|
<key>CFBundleVersion</key>
|
|
<string>$RELEASE_BUILD</string>
|
|
<key>APCBuildID</key>
|
|
<string>$BUILD_ID</string>
|
|
<key>APCReleaseChannel</key>
|
|
<string>$RELEASE_CHANNEL</string>
|
|
<key>APCRuntimeManifestSchemaVersion</key>
|
|
<string>apc.runtime-manifest.v1</string>
|
|
<key>LSApplicationCategoryType</key>
|
|
<string>public.app-category.developer-tools</string>
|
|
<key>LSMinimumSystemVersion</key>
|
|
<string>$MIN_SYSTEM_VERSION</string>
|
|
<key>UTExportedTypeDeclarations</key>
|
|
<array>
|
|
<dict>
|
|
<key>UTTypeIdentifier</key>
|
|
<string>dev.agentpet.petpack</string>
|
|
<key>UTTypeConformsTo</key>
|
|
<array>
|
|
<string>public.data</string>
|
|
</array>
|
|
<key>UTTypeDescription</key>
|
|
<string>Agent Pet Companion Pet Pack</string>
|
|
<key>UTTypeTagSpecification</key>
|
|
<dict>
|
|
<key>public.filename-extension</key>
|
|
<array>
|
|
<string>petpack</string>
|
|
</array>
|
|
<key>public.mime-type</key>
|
|
<string>application/vnd.agentpet.petpack+zip</string>
|
|
</dict>
|
|
</dict>
|
|
</array>
|
|
<key>NSHighResolutionCapable</key>
|
|
<true/>
|
|
<key>NSPrincipalClass</key>
|
|
<string>NSApplication</string>
|
|
</dict>
|
|
</plist>
|
|
PLIST
|
|
|
|
if [[ "$SIGN_DEVELOPMENT" == "1" ]]; then
|
|
command -v codesign >/dev/null 2>&1 || {
|
|
echo 'development signing requires codesign' >&2
|
|
exit 1
|
|
}
|
|
# Finder and copy operations can attach metadata that invalidates even an
|
|
# ad-hoc signature. Strip it from the staged bundle before signing so the
|
|
# exact artifact moved into dist is verifiable with --deep --strict.
|
|
xattr -cr "$STAGED_APP"
|
|
codesign --force --sign - --timestamp=none "$APP_BINARY"
|
|
codesign --force --sign - --timestamp=none "$APP_RESOURCES/bin/petcore"
|
|
codesign --force --sign - --timestamp=none "$APP_RESOURCES/bin/petcore-cli"
|
|
codesign --force --sign - --timestamp=none "$STAGED_APP"
|
|
fi
|
|
|
|
if [[ "$RELEASE_CHANNEL" == "release" ]]; then
|
|
VALIDATE_ARGS=(--github-release)
|
|
else
|
|
VALIDATE_ARGS=(--development)
|
|
fi
|
|
if [[ -n "$TARGET_ARCH" ]]; then
|
|
VALIDATE_ARGS+=(--architecture "$TARGET_ARCH")
|
|
fi
|
|
if [[ "$VALIDATION_PROFILE" == "static" ]]; then
|
|
VALIDATE_ARGS+=(--static-only)
|
|
fi
|
|
"$ROOT_DIR/script/validate_app_bundle.sh" "${VALIDATE_ARGS[@]}" "$STAGED_APP" >/dev/null
|
|
|
|
if [[ "$CREATE_DEVELOP_ARCHIVE" == "1" ]]; then
|
|
# Archive the clean, signed staging bundle before anything crosses into the
|
|
# File Provider workspace. Verify the exact archive payload independently.
|
|
command -v ditto >/dev/null 2>&1 || {
|
|
echo 'development packaging requires ditto' >&2
|
|
exit 1
|
|
}
|
|
ditto -c -k --norsrc --keepParent "$STAGED_APP" "$STAGED_DEVELOP_ARCHIVE"
|
|
ARCHIVE_VERIFY_DIR="$(mktemp -d "${TMPDIR:-/tmp}/apc-develop-archive.XXXXXX")"
|
|
ditto -x -k "$STAGED_DEVELOP_ARCHIVE" "$ARCHIVE_VERIFY_DIR"
|
|
codesign --verify --deep --strict "$ARCHIVE_VERIFY_DIR/$APP_NAME.app"
|
|
fi
|
|
|
|
mkdir -p "$(dirname "$APP_BUNDLE")"
|
|
rm -rf "$APP_BUNDLE"
|
|
mv "$STAGED_APP" "$APP_BUNDLE"
|
|
if [[ "$CREATE_DEVELOP_ARCHIVE" == "1" ]]; then
|
|
rm -f "$DEVELOP_ARCHIVE"
|
|
mv "$STAGED_DEVELOP_ARCHIVE" "$DEVELOP_ARCHIVE"
|
|
|
|
# Re-extract the final archive path and verify its payload, not only the
|
|
# temporary file that preceded the move into dist.
|
|
rm -rf "$ARCHIVE_VERIFY_DIR/$APP_NAME.app"
|
|
ditto -x -k "$DEVELOP_ARCHIVE" "$ARCHIVE_VERIFY_DIR"
|
|
codesign --verify --deep --strict "$ARCHIVE_VERIFY_DIR/$APP_NAME.app"
|
|
|
|
else
|
|
# Do not leave a stale handoff artifact that no longer corresponds to the
|
|
# App produced by this successful build.
|
|
rm -f "$DEVELOP_ARCHIVE"
|
|
fi
|
|
if [[ "$SIGN_DEVELOPMENT" == "1" ]]; then
|
|
verify_destination_development_bundle
|
|
fi
|
|
|
|
printf 'Built %s (%s%s, build %s)\n' \
|
|
"$APP_BUNDLE" \
|
|
"$CONFIGURATION" \
|
|
"$(
|
|
if [[ "$UNIVERSAL" == "1" ]]; then
|
|
printf ', universal'
|
|
elif [[ -n "$TARGET_ARCH" ]]; then
|
|
printf ', %s' "$TARGET_ARCH"
|
|
fi
|
|
)" \
|
|
"$BUILD_ID"
|
|
if [[ "$CREATE_DEVELOP_ARCHIVE" == "1" ]]; then
|
|
printf 'Packaged %s (ad-hoc development signature)\n' "$DEVELOP_ARCHIVE"
|
|
fi
|