Files
Shadowbroker/start.sh
T
Shadowbroker a930497e14 fix(start-scripts): find bundled privacy_core.dll next to script (#319) (#324)
* fix(start-scripts): find bundled privacy_core.dll next to script

start.bat and start.sh only checked the source-tree DLL path
(``privacy-core/target/release/privacy_core.dll``), not the bundled
location where MSI/AppImage/DMG installers stage the library directly
next to the script in backend-runtime/.

Users running start.bat from inside an MSI install dir (a documented
workaround when the desktop shell crashes) saw a scary "install Rust"
warning even though the DLL was sitting right next to them. See issue
#319 for the user-reported confusion.

Fix: add a fallback check for the bundled location before falling
through to the "build privacy-core from source" warning. Source-tree
behavior unchanged — the source path is still preferred when present.

Also re-stamps the v0.9.81 source archive: ``release_digests.json``
v0.9.81 zip hash updated to point at the rebuilt source archive that
contains these script changes. MSI/EXE/sig hashes are unchanged (the
scripts live at the repo root, not inside the desktop bundle).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(#319): bundle start.bat + start.sh into the MSI/EXE installers

Follow-up to the start-script DLL fallback fix in the prior commit.

ChrisMTheMan's report on #319 made it clear the workaround flow was:

  1. MSI install crashes on launch (different bug, fixed in v0.9.81)
  2. User goes looking for start.bat to launch the backend manually
  3. start.bat isn't in their install dir, so they go fetch it from GitHub
  4. They get a working script but it doesn't know about the bundled
     privacy_core.dll layout, so they see a scary "install Rust" warning

The prior commit fixed step 4. This commit fixes step 3 — start.bat and
start.sh now ship inside the MSI/EXE installers (staged into
backend-runtime/ next to the privacy_core.dll they expect to find).
After the rebuild lands, an MSI user looking for these scripts finds
them right inside their install dir, already pointing at the correct
bundled DLL location.

What changed
------------

* ``build-backend-runtime.cjs`` now has a ``stageStartScripts()`` step
  that copies start.bat and start.sh from the repo root into the
  staged backend-runtime/. Preserves the executable bit on .sh under
  POSIX.

* ``release_digests.json`` v0.9.81 block hashes refreshed for the
  rebuilt MSI / EXE / source-zip (the scripts being bundled changed
  the MSI/EXE contents; the source zip also includes the start-script
  fix from the prior commit).

  ShadowBroker_v0.9.81.zip                  6.06 MB
    af8c87ccdece8fbb9aadc6be63cce10d3fcba74e6d87ef83289dda6d555fd270
  ShadowBroker_0.9.81_x64_en-US.msi       122.4 MB
    8977c9a1c54e1f0d030436be9c4e3d81d766cc0080699eb747649095f360c7ff
  ShadowBroker_0.9.81_x64-setup.exe        76.5 MB
    4e866fa0423c0c2470ed32f4809167a7815dc23ee7762b69e95681c1f3a28250

Post-merge plan
---------------

Force-move the v0.9.81 tag to this commit and replace ALL release
assets on the GitHub release: zip, msi, exe, both .sig files,
latest.json, SHA256SUMS.txt, release-manifest.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 21:34:59 -06:00

273 lines
10 KiB
Bash

#!/bin/bash
# Graceful shutdown: stop child processes without signaling the parent shell.
cleanup() {
trap - EXIT SIGINT SIGTERM
if command -v pkill >/dev/null 2>&1; then
pkill -P $$ 2>/dev/null || true
fi
}
trap cleanup EXIT SIGINT SIGTERM
echo "======================================================="
echo " S H A D O W B R O K E R - macOS / Linux Start "
echo "======================================================="
echo ""
# Check for stale docker-compose.yml from pre-migration clones
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ -f "$SCRIPT_DIR/docker-compose.yml" ] && grep -q '^\s*build:' "$SCRIPT_DIR/docker-compose.yml" 2>/dev/null; then
echo ""
echo "================================================================"
echo " [!] WARNING: Your docker-compose.yml is outdated."
echo ""
echo " It contains 'build:' directives, which means Docker will"
echo " compile from local source instead of pulling pre-built images."
echo " You will NOT receive updates this way."
echo ""
echo " If you use Docker, re-clone the repository:"
echo " git clone https://github.com/BigBodyCobain/Shadowbroker.git"
echo " cd Shadowbroker && docker compose pull && docker compose up -d"
echo "================================================================"
echo ""
fi
# Check for Node.js
if ! command -v npm &> /dev/null; then
echo "[!] ERROR: npm is not installed. Please install Node.js 18+ (https://nodejs.org/)"
exit 1
fi
echo "[*] Found Node.js $(node --version)"
# Check for Python 3
PYTHON_CMD=""
if command -v python3 &> /dev/null; then
PYTHON_CMD="python3"
elif command -v python &> /dev/null; then
PYTHON_CMD="python"
else
echo "[!] ERROR: Python is not installed."
echo "[!] Install Python 3.10-3.12 from https://python.org"
exit 1
fi
PYVER=$($PYTHON_CMD --version 2>&1 | awk '{print $2}')
echo "[*] Found Python $PYVER"
export BACKEND_BASE_PYTHON="$PYTHON_CMD"
PY_MINOR=$(echo "$PYVER" | cut -d. -f2)
if [ "$PY_MINOR" -ge 13 ] 2>/dev/null; then
echo "[!] WARNING: Python $PYVER detected. Some packages may fail to build."
echo "[!] Recommended: Python 3.10, 3.11, or 3.12."
echo ""
fi
# Get the directory where this script lives
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# ── Zombie cleanup ─────────────────────────────────────────────────
# Kill leftover processes from a previous crashed session.
echo ""
echo "[*] Clearing zombie processes..."
# Kill anything listening on ports 8000 or 3000
for PORT in 8000 3000 8787; do
if command -v lsof &> /dev/null; then
PIDS=$(lsof -ti :$PORT 2>/dev/null)
elif command -v ss &> /dev/null; then
PIDS=$(ss -tlnp "sport = :$PORT" 2>/dev/null | grep -oP 'pid=\K[0-9]+' | sort -u)
elif command -v fuser &> /dev/null; then
PIDS=$(fuser $PORT/tcp 2>/dev/null)
else
PIDS=""
fi
for P in $PIDS; do
kill -9 "$P" 2>/dev/null
done
done
# Kill orphaned uvicorn and ais_proxy processes
pkill -9 -f "uvicorn.*main:app" 2>/dev/null
pkill -9 -f "ais_proxy" 2>/dev/null
pkill -9 -f "wormhole_server.py" 2>/dev/null
# Brief pause for OS to release ports
sleep 1
echo "[*] Ports clear."
# ───────────────────────────────────────────────────────────────────
echo ""
echo "[*] Setting up backend..."
cd "$SCRIPT_DIR/backend"
VENV_MARKER=".venv-dir"
PINNED_VENV_DIR=""
if [ -f "$VENV_MARKER" ]; then
PINNED_VENV_DIR="$(head -n 1 "$VENV_MARKER" | tr -d '\r')"
fi
# Check if UV is available (preferred, much faster installs)
if command -v uv &> /dev/null; then
echo "[*] Using UV for Python dependency management."
PRIMARY_VENV_DIR="venv"
if [ -n "$PINNED_VENV_DIR" ]; then
PRIMARY_VENV_DIR="$PINNED_VENV_DIR"
fi
REPAIR_VENV_DIR="venv-repair-$$"
VENV_DIR="$PRIMARY_VENV_DIR"
VENV_PY="$VENV_DIR/bin/python3"
if [ -x "$VENV_PY" ] && ! "$VENV_PY" --version >/dev/null 2>&1; then
echo "[*] Existing backend Python venv is stale. Rebuilding it..."
rm -rf "$PRIMARY_VENV_DIR" 2>/dev/null || true
if [ -d "$PRIMARY_VENV_DIR" ]; then
VENV_DIR="$REPAIR_VENV_DIR"
VENV_PY="$VENV_DIR/bin/python3"
echo "[*] Primary venv could not be replaced cleanly. Falling back to $REPAIR_VENV_DIR..."
fi
fi
if [ "$VENV_DIR" != "$PRIMARY_VENV_DIR" ] && [ -x "$VENV_PY" ] && ! "$VENV_PY" --version >/dev/null 2>&1; then
rm -rf "$VENV_DIR"
fi
export BACKEND_VENV_DIR="$VENV_DIR"
if [ ! -d "$VENV_DIR" ]; then
echo "[*] Creating Python virtual environment..."
rm -rf "$VENV_DIR"
uv venv "$VENV_DIR"
if [ $? -ne 0 ]; then
echo "[!] ERROR: Failed to create virtual environment."
exit 1
fi
fi
"$VENV_PY" --version >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "[!] ERROR: Backend virtual environment could not start Python after repair."
exit 1
fi
echo "[*] Installing Python dependencies via UV (fast)..."
cd "$SCRIPT_DIR"
UV_PROJECT_ENVIRONMENT="$SCRIPT_DIR/backend/$VENV_DIR" uv sync --frozen --no-dev
cd "$SCRIPT_DIR/backend"
else
echo "[*] UV not found, using pip (install UV for faster installs: https://docs.astral.sh/uv/)"
PRIMARY_VENV_DIR="venv"
if [ -n "$PINNED_VENV_DIR" ]; then
PRIMARY_VENV_DIR="$PINNED_VENV_DIR"
fi
REPAIR_VENV_DIR="venv-repair-$$"
VENV_DIR="$PRIMARY_VENV_DIR"
VENV_PY="$VENV_DIR/bin/python3"
if [ -x "$VENV_PY" ] && ! "$VENV_PY" --version >/dev/null 2>&1; then
echo "[*] Existing backend Python venv is stale. Rebuilding it..."
rm -rf "$PRIMARY_VENV_DIR" 2>/dev/null || true
if [ -d "$PRIMARY_VENV_DIR" ]; then
VENV_DIR="$REPAIR_VENV_DIR"
VENV_PY="$VENV_DIR/bin/python3"
echo "[*] Primary venv could not be replaced cleanly. Falling back to $REPAIR_VENV_DIR..."
fi
fi
if [ "$VENV_DIR" != "$PRIMARY_VENV_DIR" ] && [ -x "$VENV_PY" ] && ! "$VENV_PY" --version >/dev/null 2>&1; then
rm -rf "$VENV_DIR"
fi
export BACKEND_VENV_DIR="$VENV_DIR"
if [ ! -d "$VENV_DIR" ]; then
echo "[*] Creating Python virtual environment..."
$PYTHON_CMD -m venv "$VENV_DIR"
if [ $? -ne 0 ]; then
echo "[!] ERROR: Failed to create virtual environment."
exit 1
fi
fi
"$VENV_PY" --version >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "[!] ERROR: Backend virtual environment could not start Python after repair."
exit 1
fi
echo "[*] Installing Python dependencies (this may take a minute)..."
"$VENV_PY" -m pip install -q .
fi
if [ $? -ne 0 ]; then
echo ""
echo "[!] ERROR: Python dependency install failed. See errors above."
echo "[!] If you see Rust/cargo errors, your Python version may be too new."
echo "[!] Recommended: Python 3.10, 3.11, or 3.12."
exit 1
fi
printf '%s\n' "$VENV_DIR" > "$VENV_MARKER"
echo "[*] Backend dependencies OK."
if [ ! -d "node_modules/ws" ]; then
echo "[*] Installing backend Node.js dependencies..."
npm ci --omit=dev --silent
fi
echo "[*] Backend Node.js dependencies OK."
echo ""
echo "[*] Checking privacy-core shared library..."
PRIVACY_CORE_SO="$SCRIPT_DIR/privacy-core/target/release/libprivacy_core.so"
PRIVACY_CORE_DYLIB="$SCRIPT_DIR/privacy-core/target/release/libprivacy_core.dylib"
# MSI/AppImage/DMG installers stage the platform-specific shared library
# directly alongside this script (in backend-runtime/). If somebody runs
# start.sh from an installed app dir without Rust, they shouldn't see a
# spurious "install Rust" warning — the library is right next to them,
# just at a different path than the source-tree build.
if [ ! -f "$PRIVACY_CORE_SO" ] && [ -f "$SCRIPT_DIR/libprivacy_core.so" ]; then
PRIVACY_CORE_SO="$SCRIPT_DIR/libprivacy_core.so"
fi
if [ ! -f "$PRIVACY_CORE_DYLIB" ] && [ -f "$SCRIPT_DIR/libprivacy_core.dylib" ]; then
PRIVACY_CORE_DYLIB="$SCRIPT_DIR/libprivacy_core.dylib"
fi
if [ ! -f "$PRIVACY_CORE_SO" ] && [ ! -f "$PRIVACY_CORE_DYLIB" ]; then
if command -v cargo >/dev/null 2>&1; then
echo "[*] Building privacy-core release library..."
cargo build --release --manifest-path "$SCRIPT_DIR/privacy-core/Cargo.toml"
if [ $? -ne 0 ]; then
echo "[!] ERROR: privacy-core build failed. Infonet private lanes need this library."
exit 1
fi
else
echo "[!] WARNING: privacy-core shared library is missing and Rust/Cargo is not installed."
echo "[!] Infonet private lanes and gates need this library."
echo "[!] Install Rust from https://rustup.rs/ and run:"
echo "[!] cargo build --release --manifest-path \"$SCRIPT_DIR/privacy-core/Cargo.toml\""
echo ""
fi
fi
if [ -f "$PRIVACY_CORE_SO" ] || [ -f "$PRIVACY_CORE_DYLIB" ]; then
echo "[*] privacy-core shared library OK."
"$VENV_PY" "$SCRIPT_DIR/scripts/refresh_privacy_core_pin.py" || {
echo "[!] WARNING: privacy-core trust pin refresh failed. Startup may fail if backend/.env pins an old hash."
echo ""
}
fi
cd "$SCRIPT_DIR"
echo ""
echo "[*] Setting up frontend..."
cd "$SCRIPT_DIR/frontend"
FRONTEND_DEPS_OK=1
if [ ! -d "node_modules" ]; then
FRONTEND_DEPS_OK=0
fi
if [ "$FRONTEND_DEPS_OK" -eq 1 ]; then
node -e "require.resolve('next/dist/bin/next',{paths:['.']});require.resolve('lucide-react',{paths:['.']});require.resolve('maplibre-gl',{paths:['.']});require.resolve('@swc/helpers/_/_interop_require_default',{paths:['.']})" >/dev/null 2>&1 || FRONTEND_DEPS_OK=0
fi
if [ "$FRONTEND_DEPS_OK" -eq 0 ]; then
echo "[*] Frontend install is missing required packages. Repairing with npm ci..."
npm ci
if [ $? -ne 0 ]; then
echo "[!] ERROR: frontend dependency install failed. See errors above."
exit 1
fi
fi
echo "[*] Frontend dependencies OK."
echo ""
echo "======================================================="
echo " Starting services... "
echo " Dashboard: http://localhost:3000 "
echo " Keep this window open! Initial load takes ~10s. "
echo "======================================================="
echo " (Press Ctrl+C to stop)"
echo ""
node scripts/dev-all.cjs