mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-20 03:35:40 +02:00
(138MB → 7MB), pre-compile python bytecode, parallelize Widevine, never silently quit
160 lines
5.8 KiB
Bash
Executable File
160 lines
5.8 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Build an embedded Python environment for the Electron app.
|
|
#
|
|
# Downloads a standalone Python build from python-build-standalone,
|
|
# creates a venv, and installs all backend dependencies.
|
|
# The resulting python-env/ directory is bundled into the Electron app.
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
ELECTRON_DIR="$PROJECT_ROOT/electron"
|
|
PYTHON_ENV_DIR="$ELECTRON_DIR/python-env"
|
|
|
|
PYTHON_VERSION="3.13"
|
|
PYTHON_FULL_VERSION="3.13.2"
|
|
ARCH="$(uname -m)"
|
|
|
|
if [[ "$ARCH" == "arm64" ]]; then
|
|
PLATFORM_TAG="aarch64-apple-darwin"
|
|
elif [[ "$ARCH" == "x86_64" ]]; then
|
|
PLATFORM_TAG="x86_64-apple-darwin"
|
|
else
|
|
echo "Unsupported architecture: $ARCH"
|
|
exit 1
|
|
fi
|
|
|
|
RELEASE_TAG="20250212"
|
|
TARBALL_NAME="cpython-${PYTHON_FULL_VERSION}+${RELEASE_TAG}-${PLATFORM_TAG}-install_only_stripped.tar.gz"
|
|
DOWNLOAD_URL="https://github.com/indygreg/python-build-standalone/releases/download/${RELEASE_TAG}/${TARBALL_NAME}"
|
|
TEMP_DIR="$(mktemp -d)"
|
|
|
|
cleanup() {
|
|
rm -rf "$TEMP_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
echo "=== Building Python Environment ==="
|
|
echo "Architecture: $ARCH ($PLATFORM_TAG)"
|
|
echo "Python: $PYTHON_FULL_VERSION"
|
|
|
|
# Remove old env if present
|
|
if [[ -d "$PYTHON_ENV_DIR" ]]; then
|
|
echo "Removing old python-env..."
|
|
rm -rf "$PYTHON_ENV_DIR"
|
|
fi
|
|
|
|
# Download standalone Python
|
|
echo "Downloading standalone Python from python-build-standalone..."
|
|
echo "URL: $DOWNLOAD_URL"
|
|
curl -fSL --progress-bar -o "$TEMP_DIR/python.tar.gz" "$DOWNLOAD_URL"
|
|
|
|
echo "Extracting..."
|
|
tar xzf "$TEMP_DIR/python.tar.gz" -C "$TEMP_DIR"
|
|
|
|
# The tarball extracts to python/
|
|
EXTRACTED_DIR="$TEMP_DIR/python"
|
|
if [[ ! -d "$EXTRACTED_DIR" ]]; then
|
|
echo "Error: Expected extracted directory at $EXTRACTED_DIR"
|
|
ls -la "$TEMP_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Move into place
|
|
mv "$EXTRACTED_DIR" "$PYTHON_ENV_DIR"
|
|
echo "Python installed to $PYTHON_ENV_DIR"
|
|
|
|
PYTHON_BIN="$PYTHON_ENV_DIR/bin/python${PYTHON_VERSION}"
|
|
if [[ ! -f "$PYTHON_BIN" ]]; then
|
|
PYTHON_BIN="$PYTHON_ENV_DIR/bin/python3"
|
|
fi
|
|
|
|
echo "Python binary: $PYTHON_BIN"
|
|
"$PYTHON_BIN" --version
|
|
|
|
# Install pip (standalone builds may not include it)
|
|
if ! "$PYTHON_BIN" -m pip --version &>/dev/null; then
|
|
echo "Installing pip..."
|
|
"$PYTHON_BIN" -m ensurepip --upgrade
|
|
fi
|
|
|
|
# Install backend dependencies
|
|
echo "Installing backend dependencies..."
|
|
"$PYTHON_BIN" -m pip install --upgrade pip
|
|
"$PYTHON_BIN" -m pip install -r "$PROJECT_ROOT/backend/requirements.txt"
|
|
|
|
# Install the debugger module
|
|
echo "Installing debugger module..."
|
|
"$PYTHON_BIN" -m pip install "$PROJECT_ROOT/debugger"
|
|
|
|
# Verify claude-agent-sdk and its bundled binary
|
|
echo "Verifying claude-agent-sdk..."
|
|
"$PYTHON_BIN" -c "import claude_agent_sdk; print(f'claude-agent-sdk installed')"
|
|
|
|
CLAUDE_BIN=$("$PYTHON_BIN" -c "
|
|
from pathlib import Path
|
|
import claude_agent_sdk
|
|
sdk_dir = Path(claude_agent_sdk.__file__).parent
|
|
bundled = sdk_dir / '_bundled' / 'claude'
|
|
print(bundled)
|
|
")
|
|
if [[ -f "$CLAUDE_BIN" ]]; then
|
|
echo "Claude binary found: $CLAUDE_BIN"
|
|
chmod +x "$CLAUDE_BIN"
|
|
else
|
|
echo "WARNING: Claude binary not found at $CLAUDE_BIN"
|
|
fi
|
|
|
|
# Clean up build artifacts to reduce size. Drop test packages and any
|
|
# stale __pycache__/.pyc from the upstream Python tarball — we want our
|
|
# own freshly-compiled bytecode (next step), not whatever the upstream
|
|
# build happened to ship.
|
|
echo "Cleaning up..."
|
|
find "$PYTHON_ENV_DIR" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
|
find "$PYTHON_ENV_DIR" -name "*.pyc" -delete 2>/dev/null || true
|
|
find "$PYTHON_ENV_DIR" -type d -name "tests" -exec rm -rf {} + 2>/dev/null || true
|
|
find "$PYTHON_ENV_DIR" -type d -name "test" -exec rm -rf {} + 2>/dev/null || true
|
|
|
|
# Strip parts of the Python distribution we provably don't use at runtime.
|
|
# Each removal here has been individually verified — see the audit notes in
|
|
# the project plan. Doing this BEFORE compileall would also work, but doing
|
|
# it after means the dirs are already definitely-not-imported (compileall
|
|
# would have surfaced any backend code that touches them).
|
|
#
|
|
# Conservative on purpose. NOT removing pip/, babel/locale-data/, pygments
|
|
# lexers, or PIL — each had at least one weak import-evidence trail.
|
|
echo "Stripping unused Python distribution files..."
|
|
# C headers — only needed when building C extensions, never at runtime.
|
|
rm -rf "$PYTHON_ENV_DIR/include"
|
|
# IDLE editor + Tk GUI toolkit — embedded headless backend has no UI.
|
|
rm -rf "$PYTHON_ENV_DIR/lib/python3.13/idlelib"
|
|
rm -rf "$PYTHON_ENV_DIR/lib/python3.13/tkinter"
|
|
# Pip bootstrap module — backend never installs packages at runtime.
|
|
rm -rf "$PYTHON_ENV_DIR/lib/python3.13/ensurepip"
|
|
# Educational drawing examples that ship with stdlib — never imported.
|
|
rm -rf "$PYTHON_ENV_DIR/lib/python3.13/turtledemo"
|
|
# Man pages / desktop-integration files — embedded Python doesn't read these.
|
|
rm -rf "$PYTHON_ENV_DIR/share"
|
|
|
|
# Pre-compile bytecode so cold backend startup skips the parse+compile
|
|
# step on every imported .py. Worth ~5-10s on Windows under Defender
|
|
# (parsing Python source is parser-bound; loading .pyc is just bytes).
|
|
# Concurrency capped at 4 — `-j 0` (all cores) is fine on dev boxes
|
|
# but unstable on small CI runners. Failures on individual files are
|
|
# survivable (compileall continues on SyntaxError-tagged files used by
|
|
# version-shim packages); a non-zero exit here would rather be visible
|
|
# than silent so we don't `|| true` the whole thing — but missing .pyc
|
|
# is non-fatal at runtime, so a hard fail isn't warranted either.
|
|
echo "Pre-compiling bytecode..."
|
|
"$PYTHON_BIN" -m compileall -q -j 4 "$PYTHON_ENV_DIR/lib" || \
|
|
echo "WARNING: some files failed to compile; runtime will fall back to in-memory compile."
|
|
|
|
TOTAL_SIZE=$(du -sh "$PYTHON_ENV_DIR" | cut -f1)
|
|
PYC_COUNT=$(find "$PYTHON_ENV_DIR" -name '*.pyc' -type f | wc -l | tr -d ' ')
|
|
echo ""
|
|
echo "=== Python Environment Ready ==="
|
|
echo "Location: $PYTHON_ENV_DIR"
|
|
echo "Size: $TOTAL_SIZE ($PYC_COUNT .pyc files)"
|
|
echo ""
|