Files
stablyai--orca/config/scripts/macos-launch-diagnostics.sh
2026-07-13 13:05:33 +08:00

367 lines
9.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Capture one-shot macOS launch diagnostics for a published Orca release.
#
# Usage:
# ORCA_DIAGNOSTIC_TAG=v1.4.42-rc.1 bash config/scripts/macos-launch-diagnostics.sh
# bash config/scripts/macos-launch-diagnostics.sh --tag v1.4.42-rc.1
set -euo pipefail
REPO="${ORCA_DIAGNOSTIC_REPO:-stablyai/orca}"
TAG="${ORCA_DIAGNOSTIC_TAG:-}"
KEEP=0
while [[ $# -gt 0 ]]; do
case "$1" in
--tag)
TAG="${2:-}"
shift 2
;;
--repo)
REPO="${2:-}"
shift 2
;;
--keep)
KEEP=1
shift
;;
-h|--help)
cat <<'EOF'
Capture one-shot macOS launch diagnostics for a published Orca release.
Usage:
ORCA_DIAGNOSTIC_TAG=v1.4.42-rc.1 bash config/scripts/macos-launch-diagnostics.sh
bash config/scripts/macos-launch-diagnostics.sh --tag v1.4.42-rc.1
EOF
exit 0
;;
*)
echo "Unknown argument: $1" >&2
echo "Usage: $0 --tag vX.Y.Z-rc.N [--repo owner/repo] [--keep]" >&2
exit 2
;;
esac
done
if [[ "$(uname -s)" != "Darwin" ]]; then
echo "This diagnostic script only supports macOS." >&2
exit 2
fi
if [[ -z "$TAG" ]]; then
echo "Set ORCA_DIAGNOSTIC_TAG or pass --tag vX.Y.Z-rc.N." >&2
exit 2
fi
ARCH="$(uname -m)"
case "$ARCH" in
arm64) ASSET="orca-macos-arm64.dmg" ;;
x86_64) ASSET="orca-macos-x64.dmg" ;;
*)
echo "Unsupported macOS architecture: $ARCH" >&2
exit 2
;;
esac
TIMESTAMP="$(date -u '+%Y%m%dT%H%M%SZ')"
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/orca-launch-diagnostics.XXXXXXXX")"
OUT_DIR="$WORK_DIR/output"
MOUNT_DIR="$WORK_DIR/mount"
APP_DIR="$WORK_DIR/Orca.app"
DMG_PATH="$WORK_DIR/$ASSET"
mkdir -p "$OUT_DIR" "$MOUNT_DIR"
if [[ -d "$HOME/Desktop" ]]; then
ZIP_PATH="$HOME/Desktop/orca-launch-diagnostics-${TAG}-${TIMESTAMP}.zip"
else
ZIP_PATH="$PWD/orca-launch-diagnostics-${TAG}-${TIMESTAMP}.zip"
fi
diag_log() {
printf '[orca-diagnostics] %s\n' "$*"
}
run_capture() {
local name="$1"
shift
{
echo "\$ $*"
"$@"
} >"$OUT_DIR/$name.out" 2>"$OUT_DIR/$name.err" || {
local code=$?
echo "exit=$code" >>"$OUT_DIR/$name.err"
return 0
}
}
cleanup() {
if mount | grep -F " on $MOUNT_DIR " >/dev/null 2>&1; then
hdiutil detach "$MOUNT_DIR" -quiet >/dev/null 2>&1 || true
fi
if [[ "$KEEP" -eq 0 ]]; then
rm -rf "$WORK_DIR"
else
diag_log "kept working directory: $WORK_DIR"
fi
}
trap cleanup EXIT
write_environment_report() {
{
echo "tag=$TAG"
echo "repo=$REPO"
echo "asset=$ASSET"
echo "timestamp_utc=$TIMESTAMP"
echo
echo "## sw_vers"
sw_vers || true
echo
echo "## uname"
uname -a || true
echo
echo "## shell"
echo "$SHELL"
echo
echo "## current Orca processes before diagnostics"
pgrep -fl 'Orca|orca' || true
} >"$OUT_DIR/environment.txt"
}
ensure_no_existing_orca() {
local existing
existing="$(pgrep -x Orca || true)"
if [[ -z "$existing" ]]; then
return 0
fi
{
echo "An Orca process is already running. Close Orca and run this script again."
echo
ps -p "$(printf '%s' "$existing" | paste -sd, -)" -o pid=,ppid=,command= || true
} | tee "$OUT_DIR/existing-orca-process.txt" >&2
exit 2
}
download_and_copy_app() {
local url="https://github.com/$REPO/releases/download/$TAG/$ASSET"
diag_log "downloading $url"
curl -fL --retry 3 --retry-delay 2 -o "$DMG_PATH" "$url"
shasum -a 256 "$DMG_PATH" >"$OUT_DIR/dmg.sha256" || true
diag_log "mounting DMG"
hdiutil attach "$DMG_PATH" -nobrowse -readonly -mountpoint "$MOUNT_DIR" >"$OUT_DIR/hdiutil-attach.txt"
local source_app
source_app="$(find "$MOUNT_DIR" -maxdepth 1 -name '*.app' -type d | head -n 1)"
if [[ -z "$source_app" ]]; then
echo "No .app bundle found in $DMG_PATH" >&2
exit 1
fi
diag_log "copying app to isolated temp path"
ditto "$source_app" "$APP_DIR"
hdiutil detach "$MOUNT_DIR" -quiet
}
write_app_report() {
{
echo "app=$APP_DIR"
echo
echo "## Info.plist"
plutil -p "$APP_DIR/Contents/Info.plist" || true
echo
echo "## spctl"
spctl -a -vvv "$APP_DIR" || true
echo
echo "## codesign display"
codesign -dv --verbose=4 "$APP_DIR" || true
echo
echo "## codesign verify"
codesign --verify --strict --deep "$APP_DIR" || true
echo
echo "## xattr"
xattr -lr "$APP_DIR" || true
echo
echo "## native modules"
find "$APP_DIR/Contents/Resources" -name '*.node' -print || true
} >"$OUT_DIR/app-report.txt" 2>&1
}
start_log_stream() {
local file="$1"
local predicate='process == "Orca" OR eventMessage CONTAINS[c] "Orca" OR eventMessage CONTAINS[c] "com.stablyai.orca"'
if command -v log >/dev/null 2>&1; then
command log stream --style compact --predicate "$predicate" >"$file" 2>&1 &
echo "$!"
else
echo ""
fi
}
stop_log_stream() {
local pid="$1"
if [[ -n "$pid" ]] && kill -0 "$pid" >/dev/null 2>&1; then
kill "$pid" >/dev/null 2>&1 || true
wait "$pid" >/dev/null 2>&1 || true
fi
}
latest_orca_pid_for_app() {
pgrep -nf "$APP_DIR/Contents/MacOS/Orca" || true
}
sample_process_once() {
local pid="$1"
local file="$2"
if [[ -n "$pid" ]] && command -v sample >/dev/null 2>&1; then
sample "$pid" 1 -file "$file" >"$file.command.out" 2>"$file.command.err" || true
fi
}
terminate_app_processes() {
local pid
pid="$(latest_orca_pid_for_app)"
if [[ -n "$pid" ]]; then
kill "$pid" >/dev/null 2>&1 || true
sleep 1
if kill -0 "$pid" >/dev/null 2>&1; then
kill -9 "$pid" >/dev/null 2>&1 || true
fi
fi
}
wait_for_probe() {
local runner_pid="$1"
local label="$2"
local sample_file="$OUT_DIR/$label.sample.txt"
local sampled=0
local i
for i in $(seq 1 200); do
local app_pid
app_pid="$(latest_orca_pid_for_app)"
if [[ "$sampled" -eq 0 && -n "$app_pid" ]]; then
sampled=1
echo "sampled_pid=$app_pid" >>"$OUT_DIR/$label.meta"
sample_process_once "$app_pid" "$sample_file"
fi
if ! kill -0 "$runner_pid" >/dev/null 2>&1; then
set +e
wait "$runner_pid"
local runner_exit=$?
set -e
echo "runner_exit=$runner_exit" >>"$OUT_DIR/$label.meta"
return 0
fi
sleep 0.1
done
echo "runner_timeout_after_seconds=20" >>"$OUT_DIR/$label.meta"
terminate_app_processes
wait "$runner_pid" >/dev/null 2>&1 || true
}
run_launchservices_probe() {
local label="$1"
local extra_env_name="${2:-}"
local extra_env_value="${3:-}"
local stderr_file="$OUT_DIR/$label.stderr.log"
local stdout_file="$OUT_DIR/$label.stdout.log"
local bootstrap_file="$OUT_DIR/$label.bootstrap.log"
local stream_file="$OUT_DIR/$label.system-stream.log"
local stream_pid
diag_log "running LaunchServices probe: $label"
stream_pid="$(start_log_stream "$stream_file")"
{
echo "label=$label"
echo "started_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "bootstrap_file=$bootstrap_file"
[[ -n "$extra_env_name" ]] && echo "$extra_env_name=$extra_env_value"
} >"$OUT_DIR/$label.meta"
if [[ -n "$extra_env_name" ]]; then
open -n -W \
--stdout "$stdout_file" \
--stderr "$stderr_file" \
--env ELECTRON_ENABLE_LOGGING=1 \
--env ORCA_STARTUP_DIAGNOSTICS=trace \
--env ORCA_STARTUP_DIAGNOSTICS_TRACE_LIMIT=30000 \
--env ORCA_STARTUP_DIAGNOSTICS_FILE="$bootstrap_file" \
--env "$extra_env_name=$extra_env_value" \
"$APP_DIR" &
else
open -n -W \
--stdout "$stdout_file" \
--stderr "$stderr_file" \
--env ELECTRON_ENABLE_LOGGING=1 \
--env ORCA_STARTUP_DIAGNOSTICS=trace \
--env ORCA_STARTUP_DIAGNOSTICS_TRACE_LIMIT=30000 \
--env ORCA_STARTUP_DIAGNOSTICS_FILE="$bootstrap_file" \
"$APP_DIR" &
fi
local runner_pid="$!"
wait_for_probe "$runner_pid" "$label"
echo "ended_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >>"$OUT_DIR/$label.meta"
stop_log_stream "$stream_pid"
}
run_direct_exec_probe() {
local label="direct-exec"
local stderr_file="$OUT_DIR/$label.stderr.log"
local stdout_file="$OUT_DIR/$label.stdout.log"
local bootstrap_file="$OUT_DIR/$label.bootstrap.log"
local stream_file="$OUT_DIR/$label.system-stream.log"
local stream_pid
diag_log "running direct exec probe"
stream_pid="$(start_log_stream "$stream_file")"
{
echo "label=$label"
echo "started_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "bootstrap_file=$bootstrap_file"
} >"$OUT_DIR/$label.meta"
ELECTRON_ENABLE_LOGGING=1 \
ORCA_STARTUP_DIAGNOSTICS=trace \
ORCA_STARTUP_DIAGNOSTICS_TRACE_LIMIT=30000 \
ORCA_STARTUP_DIAGNOSTICS_FILE="$bootstrap_file" \
"$APP_DIR/Contents/MacOS/Orca" >"$stdout_file" 2>"$stderr_file" &
local runner_pid="$!"
wait_for_probe "$runner_pid" "$label"
echo "ended_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >>"$OUT_DIR/$label.meta"
stop_log_stream "$stream_pid"
}
write_system_log_snapshot() {
local predicate='process == "Orca" OR eventMessage CONTAINS[c] "Orca" OR eventMessage CONTAINS[c] "com.stablyai.orca"'
if command -v log >/dev/null 2>&1; then
diag_log "capturing recent unified log snapshot"
command log show --style syslog --last 10m --predicate "$predicate" >"$OUT_DIR/system-log-last-10m.log" 2>&1 || true
fi
}
package_results() {
{
echo "Diagnostics completed."
echo "Tag: $TAG"
echo "Output zip: $ZIP_PATH"
echo
echo "Please attach this zip to the GitHub issue:"
echo " $ZIP_PATH"
} >"$OUT_DIR/README.txt"
diag_log "creating zip"
(cd "$OUT_DIR" && zip -qry "$ZIP_PATH" .)
diag_log "wrote $ZIP_PATH"
}
write_environment_report
ensure_no_existing_orca
download_and_copy_app
write_app_report
run_launchservices_probe "launchservices-trace"
run_launchservices_probe "launchservices-bypass-lock" "ORCA_BYPASS_SINGLE_INSTANCE_LOCK" "1"
run_direct_exec_probe
write_system_log_snapshot
package_results