Download scripts/verify-mac.sh from smlflg/PromtGarageMac: direct link, hf CLI and curl.
- Browser
- Download file 15.2 kB
-
https://huggingface.co/smlflg/PromtGarageMac/resolve/main/scripts/verify-mac.sh
- Command line
-
hf download hf://smlflg/PromtGarageMac/scripts/verify-mac.sh
-
curl -L -o verify-mac.sh https://huggingface.co/smlflg/PromtGarageMac/resolve/main/scripts/verify-mac.sh
15.2 kB
| set -euo pipefail | |
| ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | |
| APP="$ROOT/dist/PromptGarage.app" | |
| INSTALL_APP="/Applications/PromptGarage.app" | |
| SOURCE_DB="" | |
| INSTALL=false | |
| LAUNCH=false | |
| SOURCE_SNAPSHOT_BEFORE="" | |
| SOURCE_SNAPSHOT_AFTER="" | |
| INSTALL_STAGING_DIR="" | |
| INSTALL_PREVIOUS_APP="" | |
| INSTALL_REJECTED_APP="" | |
| INSTALL_HAD_PREVIOUS=false | |
| INSTALL_PREVIOUS_MOVED=false | |
| INSTALL_NEW_IN_PLACE=false | |
| INSTALL_TRANSACTION_ACTIVE=false | |
| INSTALL_COMMITTED=false | |
| CLEANUP_TRAP_ACTIVE=false | |
| usage() { | |
| cat <<'EOF' | |
| Usage: ./scripts/verify-mac.sh [options] | |
| Options: | |
| --source-db PATH Expose a real database read-only to integration tests via | |
| PROMPTGARAGE_SOURCE_DB and prove its files are unchanged. | |
| --launch Launch the verified dist/PromptGarage.app and prove that a | |
| new PromptGarage process remains alive. | |
| --install Replace /Applications/PromptGarage.app, then launch and | |
| verify it. This is the only mode that writes to /Applications. | |
| -h, --help Show this help. | |
| Without --launch or --install, no app is opened. Without --install, nothing is | |
| written to /Applications. This script never writes to --source-db. | |
| EOF | |
| } | |
| fail() { | |
| printf 'verify-mac: %s\n' "$*" >&2 | |
| exit 1 | |
| } | |
| require_command() { | |
| command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1" | |
| } | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --source-db) | |
| [[ $# -ge 2 ]] || fail "--source-db requires a path" | |
| [[ -n "$2" ]] || fail "--source-db requires a non-empty path" | |
| SOURCE_DB="$2" | |
| shift 2 | |
| ;; | |
| --launch) | |
| LAUNCH=true | |
| shift | |
| ;; | |
| --install) | |
| INSTALL=true | |
| LAUNCH=true | |
| shift | |
| ;; | |
| -h|--help) | |
| usage | |
| exit 0 | |
| ;; | |
| *) | |
| fail "unknown option: $1 (use --help)" | |
| ;; | |
| esac | |
| done | |
| assert_supported_host() { | |
| [[ "$(uname -s)" == "Darwin" ]] || fail "this verification must run on macOS" | |
| [[ "$(uname -m)" == "arm64" ]] || fail "an Apple Silicon (arm64) Mac is required" | |
| local macos_version macos_major xcode_version xcode_major swift_version | |
| macos_version="$(sw_vers -productVersion)" | |
| macos_major="${macos_version%%.*}" | |
| [[ "$macos_major" =~ ^[0-9]+$ ]] || fail "could not parse macOS version: $macos_version" | |
| (( macos_major >= 26 )) || fail "macOS 26 or newer is required; found $macos_version" | |
| xcode_version="$(xcodebuild -version | awk 'NR == 1 { print $2 }')" | |
| xcode_major="${xcode_version%%.*}" | |
| [[ "$xcode_major" =~ ^[0-9]+$ ]] || fail "could not parse Xcode version: $xcode_version" | |
| (( xcode_major >= 26 )) || fail "Xcode 26 or newer is required; found $xcode_version" | |
| swift_version="$(swift --version | sed -n '1s/.*Swift version \([0-9][0-9.]*\).*/\1/p')" | |
| [[ "$swift_version" == 6.2* ]] || fail "Swift 6.2 is required; found ${swift_version:-unknown}" | |
| } | |
| assert_exact_arm64() { | |
| local binary="$1" | |
| local architectures | |
| architectures="$(lipo -archs "$binary" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" | |
| [[ "$architectures" == "arm64" ]] || fail "expected exactly arm64, found: ${architectures:-no architecture}" | |
| } | |
| snapshot_source_database() { | |
| local output="$1" | |
| local suffix candidate digest | |
| : > "$output" | |
| for suffix in "" "-wal" "-shm" "-journal"; do | |
| candidate="${SOURCE_DB}${suffix}" | |
| if [[ -e "$candidate" ]]; then | |
| if [[ ! -f "$candidate" ]]; then | |
| printf 'source database family contains a non-file: %s\n' "$candidate" >&2 | |
| return 1 | |
| fi | |
| digest="$(shasum -a 256 "$candidate" | awk '{ print $1 }')" || return 1 | |
| printf 'present\t%s\t%s\n' "$suffix" "$digest" >> "$output" | |
| else | |
| printf 'absent\t%s\n' "$suffix" >> "$output" | |
| fi | |
| done | |
| } | |
| ensure_cleanup_trap() { | |
| if [[ "$CLEANUP_TRAP_ACTIVE" != true ]]; then | |
| trap cleanup_on_exit EXIT | |
| CLEANUP_TRAP_ACTIVE=true | |
| fi | |
| } | |
| rollback_install_transaction() { | |
| local rollback_ok=true | |
| [[ "$INSTALL_TRANSACTION_ACTIVE" == true ]] || return 0 | |
| [[ "$INSTALL_COMMITTED" != true ]] || return 0 | |
| if [[ "$INSTALL_NEW_IN_PLACE" == true ]]; then | |
| if [[ -e "$INSTALL_APP" || -L "$INSTALL_APP" ]]; then | |
| if mv "$INSTALL_APP" "$INSTALL_REJECTED_APP"; then | |
| INSTALL_NEW_IN_PLACE=false | |
| else | |
| printf 'verify-mac: automatic rollback could not move the replacement aside: %s\n' "$INSTALL_APP" >&2 | |
| rollback_ok=false | |
| fi | |
| else | |
| printf 'verify-mac: automatic rollback could not find the installed replacement: %s\n' "$INSTALL_APP" >&2 | |
| rollback_ok=false | |
| fi | |
| fi | |
| if [[ "$INSTALL_PREVIOUS_MOVED" == true ]]; then | |
| if [[ -e "$INSTALL_APP" || -L "$INSTALL_APP" ]]; then | |
| printf 'verify-mac: automatic rollback cannot restore over an occupied path: %s\n' "$INSTALL_APP" >&2 | |
| rollback_ok=false | |
| elif mv "$INSTALL_PREVIOUS_APP" "$INSTALL_APP"; then | |
| INSTALL_PREVIOUS_MOVED=false | |
| printf 'verify-mac: previous app restored after verification failure\n' >&2 | |
| else | |
| printf 'verify-mac: automatic rollback failed; previous app remains at: %s\n' "$INSTALL_PREVIOUS_APP" >&2 | |
| rollback_ok=false | |
| fi | |
| elif [[ "$INSTALL_HAD_PREVIOUS" != true && ( -e "$INSTALL_REJECTED_APP" || -L "$INSTALL_REJECTED_APP" ) ]]; then | |
| printf 'verify-mac: replacement app removed after verification failure\n' >&2 | |
| fi | |
| if [[ "$rollback_ok" == true ]]; then | |
| if [[ -n "$INSTALL_STAGING_DIR" ]] && ! rm -rf -- "$INSTALL_STAGING_DIR"; then | |
| printf 'verify-mac: rollback restored the install path, but could not clean transaction files: %s\n' "$INSTALL_STAGING_DIR" >&2 | |
| return 1 | |
| fi | |
| INSTALL_TRANSACTION_ACTIVE=false | |
| return 0 | |
| fi | |
| printf 'verify-mac: install rollback incomplete; preserving transaction files at: %s\n' "$INSTALL_STAGING_DIR" >&2 | |
| return 1 | |
| } | |
| cleanup_on_exit() { | |
| local status=$? | |
| trap - EXIT | |
| if [[ -n "$SOURCE_SNAPSHOT_BEFORE" && -f "$SOURCE_SNAPSHOT_BEFORE" ]]; then | |
| if ! snapshot_source_database "$SOURCE_SNAPSHOT_AFTER"; then | |
| printf 'verify-mac: could not re-check source database after failure\n' >&2 | |
| status=1 | |
| elif ! cmp -s "$SOURCE_SNAPSHOT_BEFORE" "$SOURCE_SNAPSHOT_AFTER"; then | |
| printf 'verify-mac: SOURCE DATABASE CHANGED; refusing a successful result\n' >&2 | |
| diff -u "$SOURCE_SNAPSHOT_BEFORE" "$SOURCE_SNAPSHOT_AFTER" >&2 || true | |
| status=1 | |
| fi | |
| fi | |
| if (( status != 0 )); then | |
| rollback_install_transaction || status=1 | |
| elif [[ "$INSTALL_TRANSACTION_ACTIVE" == true && "$INSTALL_COMMITTED" != true ]]; then | |
| printf 'verify-mac: install transaction reached EXIT before commit; rolling back\n' >&2 | |
| rollback_install_transaction || status=1 | |
| status=1 | |
| fi | |
| [[ -z "$SOURCE_SNAPSHOT_BEFORE" ]] || rm -f "$SOURCE_SNAPSHOT_BEFORE" | |
| [[ -z "$SOURCE_SNAPSHOT_AFTER" ]] || rm -f "$SOURCE_SNAPSHOT_AFTER" | |
| exit "$status" | |
| } | |
| finish_source_guard() { | |
| [[ -n "$SOURCE_SNAPSHOT_BEFORE" ]] || return 0 | |
| snapshot_source_database "$SOURCE_SNAPSHOT_AFTER" || fail "could not re-check source database" | |
| if ! cmp -s "$SOURCE_SNAPSHOT_BEFORE" "$SOURCE_SNAPSHOT_AFTER"; then | |
| diff -u "$SOURCE_SNAPSHOT_BEFORE" "$SOURCE_SNAPSHOT_AFTER" >&2 || true | |
| fail "source database or a SQLite sidecar changed during verification" | |
| fi | |
| rm -f "$SOURCE_SNAPSHOT_BEFORE" "$SOURCE_SNAPSHOT_AFTER" | |
| SOURCE_SNAPSHOT_BEFORE="" | |
| SOURCE_SNAPSHOT_AFTER="" | |
| } | |
| resolve_xcode_scheme() { | |
| local listing schemes candidate | |
| listing="$(xcodebuild -list 2>&1)" || fail "xcodebuild could not inspect this Swift package" | |
| schemes="$(printf '%s\n' "$listing" | awk ' | |
| /^[[:space:]]*Schemes:[[:space:]]*$/ { inside = 1; next } | |
| inside && /^[[:space:]]+[^[:space:]]/ { | |
| line = $0 | |
| sub(/^[[:space:]]+/, "", line) | |
| sub(/[[:space:]]+$/, "", line) | |
| print line | |
| next | |
| } | |
| inside && NF > 0 { exit } | |
| ')" | |
| if [[ -n "${PROMPTGARAGE_XCODE_SCHEME:-}" ]]; then | |
| if printf '%s\n' "$schemes" | grep -Fxq "$PROMPTGARAGE_XCODE_SCHEME"; then | |
| printf '%s\n' "$PROMPTGARAGE_XCODE_SCHEME" | |
| return 0 | |
| fi | |
| fail "PROMPTGARAGE_XCODE_SCHEME is not available: $PROMPTGARAGE_XCODE_SCHEME" | |
| fi | |
| for candidate in PromptGarage-Package PromptGarage; do | |
| if printf '%s\n' "$schemes" | grep -Fxq "$candidate"; then | |
| printf '%s\n' "$candidate" | |
| return 0 | |
| fi | |
| done | |
| printf 'Available Xcode schemes:\n%s\n' "${schemes:-<none>}" >&2 | |
| fail "no PromptGarage package test scheme found; set PROMPTGARAGE_XCODE_SCHEME explicitly" | |
| } | |
| install_verified_app() { | |
| local install_parent staged_app | |
| [[ -d /Applications ]] || fail "/Applications does not exist" | |
| [[ -w /Applications ]] || fail "/Applications is not writable; install manually rather than rerunning with sudo" | |
| install_parent="$(dirname "$INSTALL_APP")" | |
| INSTALL_STAGING_DIR="$(mktemp -d "$install_parent/.PromptGarage.install.XXXXXX")" \ | |
| || fail "could not create same-filesystem install staging directory" | |
| ensure_cleanup_trap | |
| INSTALL_TRANSACTION_ACTIVE=true | |
| staged_app="$INSTALL_STAGING_DIR/PromptGarage.app" | |
| INSTALL_PREVIOUS_APP="$INSTALL_STAGING_DIR/Previous-PromptGarage.app" | |
| INSTALL_REJECTED_APP="$INSTALL_STAGING_DIR/Rejected-PromptGarage.app" | |
| ditto "$APP" "$staged_app" || fail "could not stage the verified app" | |
| assert_exact_arm64 "$staged_app/Contents/MacOS/PromptGarage" | |
| codesign --verify --deep --strict --verbose=2 "$staged_app" \ | |
| || fail "staged app failed code-signature verification; existing installation was not changed" | |
| if [[ -e "$INSTALL_APP" || -L "$INSTALL_APP" ]]; then | |
| mv "$INSTALL_APP" "$INSTALL_PREVIOUS_APP" \ | |
| || fail "could not preserve the existing installation; it was not changed" | |
| INSTALL_HAD_PREVIOUS=true | |
| INSTALL_PREVIOUS_MOVED=true | |
| fi | |
| mv "$staged_app" "$INSTALL_APP" || fail "could not move the staged app into place" | |
| INSTALL_NEW_IN_PLACE=true | |
| assert_exact_arm64 "$INSTALL_APP/Contents/MacOS/PromptGarage" | |
| codesign --verify --deep --strict --verbose=2 "$INSTALL_APP" \ | |
| || fail "installed app failed final code-signature verification" | |
| printf 'Installed app pending launch and source-family verification: %s\n' "$INSTALL_APP" | |
| } | |
| commit_install_transaction() { | |
| [[ "$INSTALL_TRANSACTION_ACTIVE" == true ]] || return 0 | |
| if ! rm -rf -- "$INSTALL_STAGING_DIR"; then | |
| fail "installation passed, but transaction cleanup failed: $INSTALL_STAGING_DIR" | |
| fi | |
| INSTALL_COMMITTED=true | |
| INSTALL_TRANSACTION_ACTIVE=false | |
| INSTALL_PREVIOUS_MOVED=false | |
| INSTALL_NEW_IN_PLACE=false | |
| printf 'Installed verified app: %s\n' "$INSTALL_APP" | |
| } | |
| launch_and_verify_new_process() { | |
| local launch_app="$1" | |
| local expected_executable="$launch_app/Contents/MacOS/PromptGarage" | |
| local before_pids pid process_command launched_pid="" | |
| before_pids=" $(pgrep -x PromptGarage 2>/dev/null | tr '\n' ' ' || true) " | |
| open -n "$launch_app" | |
| for _ in 1 2 3 4 5 6 7 8 9 10; do | |
| for pid in $(pgrep -x PromptGarage 2>/dev/null || true); do | |
| case "$before_pids" in | |
| *" $pid "*) continue ;; | |
| esac | |
| process_command="$(ps -p "$pid" -o command= 2>/dev/null || true)" | |
| case "$process_command" in | |
| "$expected_executable"*) launched_pid="$pid" ;; | |
| esac | |
| [[ -z "$launched_pid" ]] || break | |
| done | |
| [[ -z "$launched_pid" ]] || break | |
| sleep 1 | |
| done | |
| [[ -n "$launched_pid" ]] || fail "open returned, but no new process for $expected_executable stayed alive" | |
| sleep 2 | |
| ps -p "$launched_pid" >/dev/null 2>&1 || fail "PromptGarage process $launched_pid exited during launch verification" | |
| printf 'Launch verified: PromptGarage PID %s\n' "$launched_pid" | |
| } | |
| for command_name in uname sw_vers xcodebuild swift python3 awk sed grep lipo codesign shasum cmp diff; do | |
| require_command "$command_name" | |
| done | |
| if [[ "$INSTALL" == true ]]; then | |
| for command_name in dirname ditto mktemp mv rm; do | |
| require_command "$command_name" | |
| done | |
| fi | |
| if [[ "$LAUNCH" == true ]]; then | |
| for command_name in open pgrep ps tr sleep; do | |
| require_command "$command_name" | |
| done | |
| fi | |
| if [[ -n "$SOURCE_DB" ]]; then | |
| for command_name in dirname basename mktemp rm; do | |
| require_command "$command_name" | |
| done | |
| fi | |
| assert_supported_host | |
| if [[ -n "$SOURCE_DB" ]]; then | |
| [[ ! -L "$SOURCE_DB" ]] || fail "--source-db must not be a symlink; pass the real database path so SQLite sidecars are guarded" | |
| [[ -f "$SOURCE_DB" ]] || fail "source database not found: $SOURCE_DB" | |
| [[ -r "$SOURCE_DB" ]] || fail "source database is not readable: $SOURCE_DB" | |
| SOURCE_DB="$(cd "$(dirname "$SOURCE_DB")" && pwd -P)/$(basename "$SOURCE_DB")" | |
| [[ ! -L "$SOURCE_DB" ]] || fail "canonical --source-db resolved to a symlink; pass the real database path" | |
| SOURCE_SNAPSHOT_BEFORE="$(mktemp "${TMPDIR:-/tmp}/promptgarage-source-before.XXXXXX")" \ | |
| || fail "could not create the source-before snapshot" | |
| ensure_cleanup_trap | |
| SOURCE_SNAPSHOT_AFTER="$(mktemp "${TMPDIR:-/tmp}/promptgarage-source-after.XXXXXX")" \ | |
| || fail "could not create the source-after snapshot" | |
| snapshot_source_database "$SOURCE_SNAPSHOT_BEFORE" || fail "could not fingerprint source database" | |
| export PROMPTGARAGE_SOURCE_DB="$SOURCE_DB" | |
| fi | |
| cd "$ROOT" | |
| if [[ -n "$SOURCE_DB" ]]; then | |
| python3 verification/verify_contract.py --source-db "$SOURCE_DB" | |
| else | |
| python3 verification/verify_contract.py | |
| fi | |
| swift package resolve | |
| swift test --arch arm64 | |
| XCODE_SCHEME="$(resolve_xcode_scheme)" | |
| printf 'Running Xcode package tests with scheme: %s\n' "$XCODE_SCHEME" | |
| xcodebuild \ | |
| -scheme "$XCODE_SCHEME" \ | |
| -destination 'platform=macOS,arch=arm64' \ | |
| -configuration Debug \ | |
| ARCHS=arm64 \ | |
| ONLY_ACTIVE_ARCH=YES \ | |
| test | |
| "$ROOT/scripts/build-app.sh" | |
| [[ -x "$APP/Contents/MacOS/PromptGarage" ]] || fail "packaged app executable missing" | |
| assert_exact_arm64 "$APP/Contents/MacOS/PromptGarage" | |
| codesign --verify --deep --strict --verbose=2 "$APP" | |
| LAUNCH_APP="$APP" | |
| if [[ "$INSTALL" == true ]]; then | |
| install_verified_app | |
| LAUNCH_APP="$INSTALL_APP" | |
| fi | |
| if [[ "$LAUNCH" == true ]]; then | |
| launch_and_verify_new_process "$LAUNCH_APP" | |
| fi | |
| finish_source_guard | |
| if [[ "$INSTALL" == true ]]; then | |
| commit_install_transaction | |
| fi | |
| trap - EXIT | |
| printf 'Mac automated verification passed.\n' | |
| if [[ "$LAUNCH" == true ]]; then | |
| printf 'Complete verification/MAC_UI_SMOKE_CHECKLIST.md before calling the app done.\n' | |
| else | |
| printf 'No app was launched. Use --launch or --install only when ready for the UI smoke test.\n' | |
| fi | |