mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
100 lines
4.7 KiB
Bash
Executable File
100 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Smoke a SIGNED, NOTARIZED Mac build the way a user receives it.
|
|
#
|
|
# "Works in dev" has repeatedly not meant "works packaged" here: dictation died in prod because a
|
|
# Finder-launched app inherits a PATH with no brew, and the bundled Python and 9Router live at
|
|
# different paths than dev. So this runs the real .app out of the real DMG, dequarantined the way
|
|
# a download would be, and checks the things that have actually broken before.
|
|
#
|
|
# bash scripts/smoke-packaged-mac.sh path/to/OpenSwarm-arm64.dmg
|
|
#
|
|
# Exits non-zero on the first hard failure. Every check prints PASS or FAIL with what it saw, so a
|
|
# red line is a finding and not a puzzle.
|
|
|
|
set -uo pipefail
|
|
DMG="${1:?usage: smoke-packaged-mac.sh <path-to-dmg>}"
|
|
MNT="/tmp/osw-smoke-$$"
|
|
APP=""
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
ok() { PASS=$((PASS+1)); printf " PASS %s%s\n" "$1" "${2:+ ($2)}"; }
|
|
bad() { FAIL=$((FAIL+1)); printf " FAIL %s%s\n" "$1" "${2:+ ($2)}"; }
|
|
step() { printf "\n=== %s ===\n" "$1"; }
|
|
|
|
cleanup() {
|
|
[ -n "${APP:-}" ] && pkill -f "$MNT/OpenSwarm.app" 2>/dev/null
|
|
hdiutil detach "$MNT" -quiet 2>/dev/null
|
|
rm -rf "$MNT"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
step "1. Mount the DMG the way a download arrives"
|
|
mkdir -p "$MNT"
|
|
if hdiutil attach "$DMG" -mountpoint "$MNT" -nobrowse -quiet; then
|
|
ok "mounted" "$(basename "$DMG")"
|
|
else
|
|
bad "could not mount the DMG"; exit 1
|
|
fi
|
|
APP="$MNT/OpenSwarm.app"
|
|
[ -d "$APP" ] && ok "OpenSwarm.app present" || { bad "no .app inside the DMG"; exit 1; }
|
|
|
|
step "2. Signing, notarization and DRM"
|
|
codesign --verify --deep --strict "$APP" 2>/dev/null && ok "codesign valid" || bad "codesign INVALID"
|
|
codesign -dv --requirements - "$APP" 2>&1 | grep -q "Developer ID Application" \
|
|
&& ok "signed with a Developer ID" || bad "not a Developer ID signature"
|
|
SPCTL=$(spctl -a -vvv -t install "$APP" 2>&1 | tr '\n' ' ')
|
|
grep -q "Notarized Developer ID" <<<"$SPCTL" && ok "notarized" || bad "NOT notarized" "$SPCTL"
|
|
xcrun stapler validate "$APP" >/dev/null 2>&1 && ok "notarization stapled" || bad "staple missing"
|
|
# The Widevine signature is what makes Spotify/Netflix play in the embedded browser. Shipped builds
|
|
# carried a DEVELOPMENT certificate for a month because sign-pkg was handed the wrong path.
|
|
FW="$APP/Contents/Frameworks/Electron Framework.framework"
|
|
[ -f "$FW/Resources/Electron Framework.sig" ] \
|
|
&& ok "Widevine VMP signature present" || bad "no VMP signature (DRM will be dead)"
|
|
|
|
step "3. The version and the code actually inside the bundle"
|
|
VER=$(defaults read "$APP/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null)
|
|
[ -n "$VER" ] && ok "version" "$VER" || bad "no version in Info.plist"
|
|
RES="$APP/Contents/Resources"
|
|
# The build is only worth smoking if it contains the fixes it claims to.
|
|
grep -rq "pending_continuation" "$RES/backend/apps/agents/manager/run/TurnRunner.py" 2>/dev/null \
|
|
&& ok "MCP activation hard-stop is in the bundle" \
|
|
|| bad "MCP hard-stop MISSING (stale build)"
|
|
grep -rq "lend_credential_for_cloud" "$RES/backend/apps/workflows/cloud/handover.py" 2>/dev/null \
|
|
&& ok "cloud credential lease wiring is in the bundle" \
|
|
|| bad "credential lease wiring MISSING (cloud runs cannot work)"
|
|
grep -rq "sign-in has expired" "$RES/backend/apps/tools_lib/mcp_failure_reason.py" 2>/dev/null \
|
|
&& ok "readable MCP failures are in the bundle" \
|
|
|| bad "MCP failure translation MISSING"
|
|
|
|
step "4. Bundled runtimes, at their packaged paths"
|
|
PY=$(ls -d "$RES/python-env/bin/python3"* 2>/dev/null | head -1)
|
|
[ -n "$PY" ] && ok "bundled Python present" "$(basename "$PY")" || bad "no bundled Python"
|
|
[ -n "$PY" ] && { "$PY" -c "import fastapi, anthropic" 2>/dev/null \
|
|
&& ok "bundled Python imports its deps" || bad "bundled Python cannot import fastapi/anthropic"; }
|
|
ls "$RES/router" >/dev/null 2>&1 && ok "9Router bundled" || bad "9Router missing from Resources"
|
|
# The dictation regression: whisper shelled out to ffmpeg at boot, and a Finder launch has no brew.
|
|
grep -rq -- "--convert" "$RES/backend/apps" 2>/dev/null \
|
|
&& bad "whisper --convert is back (dictation dies without brew on PATH)" \
|
|
|| ok "no whisper --convert (the prod dictation killer)"
|
|
|
|
step "5. Launch it with a Finder-like PATH and see the backend come up"
|
|
PATH="/usr/bin:/bin:/usr/sbin:/sbin" "$APP/Contents/MacOS/OpenSwarm" >/tmp/osw-smoke.log 2>&1 &
|
|
LAUNCHED=$!
|
|
BOOTED=0
|
|
for _ in $(seq 1 60); do
|
|
sleep 2
|
|
curl -s -m 3 -o /dev/null "http://127.0.0.1:8324/api/settings" && { BOOTED=1; break; }
|
|
kill -0 "$LAUNCHED" 2>/dev/null || break
|
|
done
|
|
if [ "$BOOTED" = 1 ]; then
|
|
ok "backend answered on :8324 from a brew-less PATH"
|
|
else
|
|
bad "backend never answered" "see /tmp/osw-smoke.log"
|
|
fi
|
|
kill "$LAUNCHED" 2>/dev/null
|
|
|
|
printf "\n%s\n" "$(printf '=%.0s' {1..60})"
|
|
printf "PACKAGED SMOKE: %d passed, %d failed\n" "$PASS" "$FAIL"
|
|
[ "$FAIL" -eq 0 ] || exit 1
|