#!/usr/bin/env python3 """Build a platform-local OpenSquilla wheelhouse release zip. The output is intentionally not a source checkout and not a macOS DMG. It is a zip containing the OpenSquilla wheel, dependency wheels for the current platform/Python, install scripts, a manifest, and operator-facing README. """ from __future__ import annotations import argparse import hashlib import json import os import platform import shutil import stat import subprocess import sys import tarfile import time import tomllib import urllib.parse import urllib.request import zipfile from dataclasses import dataclass from pathlib import Path from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo DEFAULT_RUNTIME_RELEASE = "20260414" DEFAULT_RUNTIME_PYTHON_VERSION = "3.12.13" PYTHON_BUILD_STANDALONE_REPO = "astral-sh/python-build-standalone" LFS_POINTER_LINE = "version https://git-lfs.github.com/spec/v1" RELEASE_NOTICE_RELS = ("LICENSE", "THIRD_PARTY_NOTICES.md") WHEEL_ROUTER_PREFIX = "opensquilla/squilla_router/models" ROUTER_PROVENANCE_WHEEL_PATH = ( f"{WHEEL_ROUTER_PREFIX}/v4.2_phase3_inference/PROVENANCE.md" ) TOKENJUICE_PROVENANCE_WHEEL_PATH = "opensquilla/plugins/tokenjuice/PROVENANCE.md" ALLOWED_SKILL_REFERENCE_WHEEL_PATHS = frozenset( { "opensquilla/skills/bundled/html-coder/references/add-css-style.md", "opensquilla/skills/bundled/html-coder/references/add-javascript.md", "opensquilla/skills/bundled/html-coder/references/attributes.md", "opensquilla/skills/bundled/html-coder/references/essentials.md", "opensquilla/skills/bundled/html-coder/references/global-attributes.md", "opensquilla/skills/bundled/html-coder/references/glossary.md", "opensquilla/skills/bundled/html-coder/references/standards.md", "opensquilla/skills/bundled/pptx/references/pptxgenjs.md", "opensquilla/skills/bundled/pptx/references/python_pptx.md", } ) ROUTER_ASSET_RELS = ( "v4.2_phase3_inference/PROVENANCE.md", "v4.2_phase3_inference/artifact_manifest.json", "v4.2_phase3_inference/lgbm_main.bin", "v4.2_phase3_inference/router.runtime.yaml", "v4.2_phase3_inference/mlp/model.onnx", "v4.2_phase3_inference/features/tfidf.pkl", "v4.2_phase3_inference/bge_onnx/model.onnx", ) REQUIRED_RUNTIME_MODULE_RELS = ( "opensquilla/cli/main.py", "opensquilla/cli/dist_cmd.py", "opensquilla/dist/__init__.py", "opensquilla/dist/workspace_state.py", ) FORBIDDEN_RELEASE_SEGMENTS = {".git", ".github", ".omx"} FORBIDDEN_RELEASE_ROOTS = {"docs", "tests", "scripts"} FORBIDDEN_RELEASE_TEXT_MARKERS = ( "INTERNAL_ORG_NAME", "github.com/internal-org/opensquilla", ".internal/evidence", "INTERNAL_RELEASE_NOTE.md", "LOCAL_AGENT_NOTES.md", ) TEXT_RELEASE_SUFFIXES = { "", ".cfg", ".ini", ".json", ".md", ".py", ".text", ".toml", ".txt", ".yaml", ".yml", } @dataclass(frozen=True) class RouterAssetCheck: missing_files: tuple[Path, ...] pointer_files: tuple[Path, ...] @property def ok(self) -> bool: return not self.missing_files and not self.pointer_files def run(args: list[str], *, cwd: Path, env: dict[str, str] | None = None) -> None: print("+", " ".join(args), flush=True) subprocess.run(args, cwd=cwd, check=True, env=env) def read_project_version(repo_root: Path) -> str: pyproject = tomllib.loads((repo_root / "pyproject.toml").read_text(encoding="utf-8")) return str(pyproject["project"]["version"]) def repo_root_from_script() -> Path: return Path(__file__).resolve().parents[1] def build_subprocess_env(work_dir: Path, *, repo_root: Path | None = None) -> dict[str, str]: env = os.environ.copy() cache_root = work_dir if repo_root is not None: resolved_repo_root = repo_root.resolve() cache_root = resolved_repo_root.parent / f".{resolved_repo_root.name}-wheelhouse-cache" uv_cache = cache_root / "uv-cache" pip_cache = cache_root / "pip-cache" uv_cache.mkdir(parents=True, exist_ok=True) pip_cache.mkdir(parents=True, exist_ok=True) env["UV_CACHE_DIR"] = str(uv_cache) env["PIP_CACHE_DIR"] = str(pip_cache) return env def copy_release_notices(release_root: Path, repo_root: Path | None = None) -> None: root = repo_root or repo_root_from_script() for rel in RELEASE_NOTICE_RELS: source = root / rel if not source.is_file(): raise SystemExit(f"Required release notice file is missing: {source}") shutil.copy2(source, release_root / rel) def platform_tag() -> str: system = platform.system().lower() or "unknown" machine = platform.machine().lower() or "unknown" aliases = { "darwin": "macos", "amd64": "x64", "x86_64": "x64", "aarch64": "arm64", } return f"{aliases.get(system, system)}-{aliases.get(machine, machine)}" def release_name( *, app_version: str, platform_tag: str, python_major: int, python_minor: int, profile: str, portable: bool, ) -> str: kind = "portable" if portable else "wheelhouse" return ( f"OpenSquilla-{app_version}-{platform_tag}-" f"py{python_major}{python_minor}-{profile}-{kind}" ) def python_runtime_target_triple(platform_tag: str) -> str: triples = { "linux-arm64": "aarch64-unknown-linux-gnu", "linux-x64": "x86_64-unknown-linux-gnu", "macos-arm64": "aarch64-apple-darwin", "macos-x64": "x86_64-apple-darwin", "windows-arm64": "aarch64-pc-windows-msvc", "windows-x64": "x86_64-pc-windows-msvc", } try: return triples[platform_tag] except KeyError as exc: raise SystemExit(f"No bundled Python runtime mapping for platform: {platform_tag}") from exc def python_runtime_asset_name( *, python_version: str, runtime_release: str, platform_tag: str ) -> str: triple = python_runtime_target_triple(platform_tag) return f"cpython-{python_version}+{runtime_release}-{triple}-install_only_stripped.tar.gz" def python_runtime_asset_url(asset_name: str, runtime_release: str) -> str: quoted = urllib.parse.quote(asset_name, safe="") return ( "https://github.com/" f"{PYTHON_BUILD_STANDALONE_REPO}/releases/download/{runtime_release}/{quoted}" ) def required_router_assets(model_root: Path) -> tuple[Path, ...]: return tuple(model_root / rel for rel in ROUTER_ASSET_RELS) def check_router_assets(model_root: Path) -> RouterAssetCheck: missing_files: list[Path] = [] pointer_files: list[Path] = [] for path in required_router_assets(model_root): if not path.is_file(): missing_files.append(path) continue first_line = path.read_text(encoding="utf-8", errors="ignore").splitlines() if first_line and first_line[0].strip() == LFS_POINTER_LINE: pointer_files.append(path) return RouterAssetCheck(tuple(missing_files), tuple(pointer_files)) def _release_name(path: str) -> str: name = path.replace("\\", "/") while name.startswith("./"): name = name[2:] return name def _contains_forbidden_release_segment(path: str) -> bool: return any(part in FORBIDDEN_RELEASE_SEGMENTS for part in _release_name(path).split("/")) def _is_allowed_runtime_markdown(path: str) -> bool: name = _release_name(path) if name == ROUTER_PROVENANCE_WHEEL_PATH: return True if name == TOKENJUICE_PROVENANCE_WHEEL_PATH: return True if name in ALLOWED_SKILL_REFERENCE_WHEEL_PATHS: return True if name.startswith("opensquilla/skills/bundled/") and name.endswith("/SKILL.md"): return True return name.startswith("opensquilla/identity/templates/bootstrap/") and name.endswith(".md") def forbidden_release_wheel_entries(names: list[str] | tuple[str, ...]) -> list[str]: violations: list[str] = [] for raw_name in names: name = _release_name(raw_name) if not name or name.endswith("/"): continue root = name.split("/", 1)[0] if ( root in FORBIDDEN_RELEASE_ROOTS or _contains_forbidden_release_segment(name) or (name.endswith(".md") and not _is_allowed_runtime_markdown(name)) ): violations.append(name) return violations def forbidden_release_wheel_paths(wheel_path: Path) -> list[str]: with ZipFile(wheel_path) as archive: return forbidden_release_wheel_entries(tuple(archive.namelist())) def forbidden_release_text_hits(wheel_path: Path) -> list[str]: hits: list[str] = [] with ZipFile(wheel_path) as archive: for info in archive.infolist(): name = _release_name(info.filename) if not name or name.endswith("/"): continue suffix = Path(name).suffix.lower() basename = Path(name).name if suffix not in TEXT_RELEASE_SUFFIXES and basename != "METADATA": continue text = archive.read(info).decode("utf-8", errors="ignore") for marker in FORBIDDEN_RELEASE_TEXT_MARKERS: if marker in text: hits.append(f"{name}: {marker}") return hits def missing_router_assets_in_wheel(wheel_path: Path) -> list[str]: expected = {f"{WHEEL_ROUTER_PREFIX}/{rel}" for rel in ROUTER_ASSET_RELS} with ZipFile(wheel_path) as archive: names = set(archive.namelist()) return sorted(expected - names) def missing_required_runtime_modules_in_wheel(wheel_path: Path) -> list[str]: with ZipFile(wheel_path) as archive: names = set(archive.namelist()) return sorted(set(REQUIRED_RUNTIME_MODULE_RELS) - names) def find_built_wheel(wheel_dir: Path) -> Path: wheels = sorted(wheel_dir.glob("opensquilla-*.whl")) if len(wheels) != 1: raise SystemExit(f"Expected one OpenSquilla wheel in {wheel_dir}, found {len(wheels)}") return wheels[0] def build_wheel(repo_root: Path, wheel_dir: Path, env: dict[str, str]) -> Path: args = ["uv", "build", "--wheel", "--out-dir", str(wheel_dir), "--clear"] try: run(args, cwd=repo_root, env=env) except subprocess.CalledProcessError as exc: print( f"uv build failed with exit code {exc.returncode}; retrying once.", file=sys.stderr, flush=True, ) time.sleep(2) run(args, cwd=repo_root, env=env) return find_built_wheel(wheel_dir) def pip_command(python_major: int, python_minor: int, *args: str) -> list[str]: if sys.version_info[:2] != (python_major, python_minor): raise SystemExit( "Wheelhouse Python mismatch: " f"running {sys.version_info.major}.{sys.version_info.minor}, " f"target is {python_major}.{python_minor}." ) return [ "uv", "run", "--python", sys.executable, "--with", "pip", "python", "-m", "pip", *args, ] def build_wheelhouse_command( package_dir: Path, wheel_path: Path, profile: str, *, target_platform_tag: str, python_major: int, python_minor: int, ) -> list[str]: validate_wheelhouse_target_platform(target_platform_tag) extras = () if profile == "core" else (profile,) target = str(wheel_path if not extras else f"{wheel_path}[{','.join(extras)}]") return pip_command( python_major, python_minor, "wheel", "--wheel-dir", str(package_dir), target, ) def validate_wheelhouse_target_platform(target_platform_tag: str) -> None: host_platform_tag = platform_tag() if target_platform_tag == host_platform_tag: return raise SystemExit( "Wheelhouse builds must run on the target platform so dependency markers " f"resolve correctly: host={host_platform_tag}, target={target_platform_tag}. " "Use --skip-wheelhouse only for metadata/package-layout checks." ) def download_wheelhouse( package_dir: Path, wheel_path: Path, profile: str, env: dict[str, str], *, target_platform_tag: str, python_major: int, python_minor: int, ) -> None: validate_wheelhouse_target_platform(target_platform_tag) run( build_wheelhouse_command( package_dir, wheel_path, profile, target_platform_tag=target_platform_tag, python_major=python_major, python_minor=python_minor, ), cwd=wheel_path.parent, env=env, ) def download_python_runtime_archive( *, download_dir: Path, python_version: str, runtime_release: str, platform_tag: str, ) -> tuple[Path, str]: asset_name = python_runtime_asset_name( python_version=python_version, runtime_release=runtime_release, platform_tag=platform_tag, ) archive_path = download_dir / asset_name if archive_path.exists(): return archive_path, asset_name download_dir.mkdir(parents=True, exist_ok=True) url = python_runtime_asset_url(asset_name, runtime_release) print(f"+ download {url}", flush=True) with urllib.request.urlopen(url, timeout=120) as response: archive_path.write_bytes(response.read()) return archive_path, asset_name def _safe_extract_tar(archive: tarfile.TarFile, destination: Path) -> None: destination_resolved = destination.resolve() for member in archive.getmembers(): target = (destination / member.name).resolve() if target != destination_resolved and destination_resolved not in target.parents: raise SystemExit(f"Refusing unsafe runtime archive member: {member.name}") archive.extractall(destination, filter="data") def extract_python_runtime_archive(archive_path: Path, runtime_root: Path) -> None: if runtime_root.exists(): shutil.rmtree(runtime_root) extract_dir = runtime_root.parent / f"{runtime_root.name}.extract" if extract_dir.exists(): shutil.rmtree(extract_dir) extract_dir.mkdir(parents=True) try: with tarfile.open(archive_path, "r:gz") as archive: _safe_extract_tar(archive, extract_dir) candidate = extract_dir / "python" if not candidate.is_dir(): child_dirs = [path for path in extract_dir.iterdir() if path.is_dir()] if len(child_dirs) != 1: raise SystemExit(f"Could not locate Python runtime root in {archive_path}") candidate = child_dirs[0] shutil.copytree(candidate, runtime_root) finally: shutil.rmtree(extract_dir, ignore_errors=True) def prune_portable_runtime(runtime_root: Path) -> None: site_packages = runtime_root / "Lib" / "site-packages" if site_packages.is_dir(): removable_names = { "_distutils_hack", "pip", "pkg_resources", "setuptools", "wheel", } removable_globs = ( "pip-*.dist-info", "setuptools-*.dist-info", "wheel-*.dist-info", ) for name in removable_names: path = site_packages / name if path.is_dir(): shutil.rmtree(path) elif path.is_file(): path.unlink() for pattern in removable_globs: for path in site_packages.glob(pattern): if path.is_dir(): shutil.rmtree(path) elif path.is_file(): path.unlink() for pycache in sorted(runtime_root.rglob("__pycache__"), reverse=True): if pycache.is_dir(): shutil.rmtree(pycache) for pyc in runtime_root.rglob("*.pyc"): if pyc.is_file(): pyc.unlink() def install_portable_wheelhouse(release_root: Path) -> None: """Preinstall wheelhouse contents into the bundled Python runtime. Portable zips should start like an app, not like a package manager. Avoid runtime venv/ensurepip/pip work on user machines; Windows PowerShell and antivirus hooks can make that path look hung even when the wheels are local. """ package_dir = release_root / "packages" site_packages = release_root / "runtime" / "python" / "Lib" / "site-packages" if not package_dir.is_dir() or not site_packages.is_dir(): raise SystemExit("Portable wheelhouse preinstall requires packages and site-packages.") for wheel_path in sorted(package_dir.glob("*.whl")): with zipfile.ZipFile(wheel_path) as wheel: for info in wheel.infolist(): name = info.filename if not name or name.endswith("/"): continue target_rel: Path | None if ".data/" in name: prefix, data_rel = name.split(".data/", 1) _ = prefix kind, _, remainder = data_rel.partition("/") if kind in {"purelib", "platlib"} and remainder: target_rel = Path(remainder) else: continue else: target_rel = Path(name) target = site_packages / target_rel target.parent.mkdir(parents=True, exist_ok=True) with wheel.open(info) as src, target.open("wb") as dst: shutil.copyfileobj(src, dst) def render_install_sh( *, wheel_name: str, profile: str, python_major: int, python_minor: int, ) -> str: wheel_target = f"${{PACKAGE_DIR}}/{wheel_name}" if profile != "core": wheel_target = f"{wheel_target}[{profile}]" return f"""#!/bin/sh if [ -z "${{BASH_VERSION:-}}" ]; then exec /usr/bin/env bash "$0" "$@" fi set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)" PACKAGE_DIR="${{SCRIPT_DIR}}/packages" REQUIRED_PYTHON_MAJOR={python_major} REQUIRED_PYTHON_MINOR={python_minor} find_python() {{ local candidate for candidate in "python${{REQUIRED_PYTHON_MAJOR}}.${{REQUIRED_PYTHON_MINOR}}" python3 python; do if ! command -v "${{candidate}}" >/dev/null 2>&1; then continue fi if "${{candidate}}" - </dev/null 2>&1 import sys expected = (${{REQUIRED_PYTHON_MAJOR}}, ${{REQUIRED_PYTHON_MINOR}}) raise SystemExit(0 if sys.version_info[:2] == expected else 1) PY then command -v "${{candidate}}" return 0 fi done return 1 }} resolve_opensquilla_bin() {{ if command -v opensquilla >/dev/null 2>&1; then command -v opensquilla return 0 fi if [[ -x "${{HOME}}/.local/bin/opensquilla" ]]; then printf '%s\\n' "${{HOME}}/.local/bin/opensquilla" return 0 fi local user_python_bin user_python_bin="${{HOME}}/Library/Python/${{REQUIRED_PYTHON_MAJOR}}.${{REQUIRED_PYTHON_MINOR}}/bin/opensquilla" if [[ -x "${{user_python_bin}}" ]]; then printf '%s\\n' "${{user_python_bin}}" return 0 fi return 1 }} if [[ ! -d "${{PACKAGE_DIR}}" ]]; then echo "OpenSquilla package directory not found: ${{PACKAGE_DIR}}" >&2 exit 1 fi PYTHON_BIN="$(find_python || true)" if [[ -z "${{PYTHON_BIN}}" ]]; then echo "Python ${{REQUIRED_PYTHON_MAJOR}}.${{REQUIRED_PYTHON_MINOR}} is required." >&2 echo "Install it, then rerun: bash install.sh" >&2 exit 1 fi echo "Installing OpenSquilla from local wheelhouse..." if command -v uv >/dev/null 2>&1; then uv tool install \\ --python "${{PYTHON_BIN}}" \\ --reinstall \\ --no-index \\ --find-links "${{PACKAGE_DIR}}" \\ "{wheel_target}" else "${{PYTHON_BIN}}" -m pip install \\ --user \\ --no-index \\ --find-links "${{PACKAGE_DIR}}" \\ "{wheel_target}" fi OPENSQUILLA_BIN="$(resolve_opensquilla_bin || true)" if [[ -z "${{OPENSQUILLA_BIN}}" ]]; then echo "OpenSquilla installed, but the executable was not found on PATH." >&2 echo "Add ~/.local/bin or your Python user scripts directory to PATH, then run opensquilla." >&2 exit 1 fi "${{OPENSQUILLA_BIN}}" onboard --if-needed echo echo "OpenSquilla is installed." echo "Start it with:" echo " opensquilla gateway run" echo echo "Then open:" echo " http://127.0.0.1:18791/control/" """ def render_install_ps1( *, wheel_name: str, profile: str, python_major: int, python_minor: int, ) -> str: wheel_target = f"$PackageDir\\{wheel_name}" if profile != "core": wheel_target = f"{wheel_target}[{profile}]" python_version = f"{python_major}.{python_minor}" return f"""$ErrorActionPreference = "Stop" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $PackageDir = Join-Path $ScriptDir 'packages' $RequiredPythonMajor = {python_major} $RequiredPythonMinor = {python_minor} function Find-Python {{ $candidates = @("py -{python_version}", "python") foreach ($candidate in $candidates) {{ $parts = $candidate.Split(" ") $exe = $parts[0] $rest = @() if ($parts.Length -gt 1) {{ $rest = $parts[1..($parts.Length - 1)] }} if (-not (Get-Command $exe -ErrorAction SilentlyContinue)) {{ continue }} $check = "import sys; expected = ($RequiredPythonMajor, $RequiredPythonMinor); " + "raise SystemExit(0 if sys.version_info[:2] == expected else 1)" & $exe @rest -c $check *> $null if ($LASTEXITCODE -eq 0) {{ return @($exe) + $rest }} }} return $null }} function Resolve-OpenSquilla {{ $cmd = Get-Command opensquilla -ErrorAction SilentlyContinue if ($cmd) {{ return $cmd.Source }} $scriptDir = Join-Path $env:APPDATA "Python\\Python{python_major}{python_minor}\\Scripts" $script = Join-Path $scriptDir "opensquilla.exe" if (Test-Path $script) {{ return $script }} return $null }} if (-not (Test-Path $PackageDir)) {{ throw "OpenSquilla package directory not found: $PackageDir" }} $Python = Find-Python if (-not $Python) {{ throw "Python {python_version} is required. Install it, then rerun .\\install.ps1." }} Write-Host "Installing OpenSquilla from local wheelhouse..." if (Get-Command uv -ErrorAction SilentlyContinue) {{ & uv tool install ` --python "{python_version}" ` --reinstall ` --no-index ` --find-links $PackageDir ` "{wheel_target}" }} else {{ $PythonExe = $Python[0] $PythonArgs = @() if ($Python.Length -gt 1) {{ $PythonArgs = $Python[1..($Python.Length - 1)] }} & $PythonExe @PythonArgs -m pip install ` --user ` --no-index ` --find-links $PackageDir ` "{wheel_target}" }} if ($LASTEXITCODE -ne 0) {{ throw "OpenSquilla installation failed with exit code $LASTEXITCODE." }} $OpenSquillaBin = Resolve-OpenSquilla if (-not $OpenSquillaBin) {{ throw "OpenSquilla installed, but the executable was not found on PATH." }} & $OpenSquillaBin onboard --if-needed if ($LASTEXITCODE -ne 0) {{ throw "OpenSquilla onboarding failed with exit code $LASTEXITCODE." }} Write-Host "" Write-Host "OpenSquilla is installed." Write-Host "Start it with:" Write-Host " opensquilla gateway run" Write-Host "" Write-Host "Then open:" Write-Host " http://127.0.0.1:18791/control/" """ def _install_target(base: str, profile: str) -> str: extras = () if profile == "core" else (profile,) return base if not extras else f"{base}[{','.join(extras)}]" def render_start_sh(profile: str = "recommended") -> str: target = _install_target("opensquilla", profile) script = """#!/bin/sh if [ -z "${BASH_VERSION:-}" ]; then exec /usr/bin/env bash "$0" "$@" fi set -euo pipefail CLI_MODE=0 if [[ "${1:-}" == "--cli" ]]; then CLI_MODE=1 shift fi SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PACKAGE_DIR="${SCRIPT_DIR}/packages" PYTHON_BIN="${SCRIPT_DIR}/runtime/python/bin/python3" if [[ -z "${OPENSQUILLA_LLM_API_KEY:-}" && -n "${OPENROUTER_API_KEY:-}" ]]; then export OPENSQUILLA_LLM_API_KEY="${OPENROUTER_API_KEY}" fi if [[ ! -x "${PYTHON_BIN}" ]]; then echo "Bundled Python runtime not found: ${PYTHON_BIN}" >&2 exit 1 fi if [[ ! -d "${PACKAGE_DIR}" ]]; then echo "OpenSquilla package directory not found: ${PACKAGE_DIR}" >&2 exit 1 fi OPENSQUILLA_WHEEL="$( find "${PACKAGE_DIR}" -maxdepth 1 -type f -name 'opensquilla-*.whl' | sort | head -n 1 )" if [[ -z "${OPENSQUILLA_WHEEL}" ]]; then echo "OpenSquilla wheel not found in ${PACKAGE_DIR}" >&2 exit 1 fi WHEEL_HASH="$( "${PYTHON_BIN}" -c ' import hashlib, pathlib, sys print(hashlib.sha256(pathlib.Path(sys.argv[1]).read_bytes()).hexdigest()[:12]) ' "${OPENSQUILLA_WHEEL}" )" VENV_DIR="${SCRIPT_DIR}/.venv-${WHEEL_HASH}" VENV_PYTHON="${VENV_DIR}/bin/python" INSTALL_MARKER="${VENV_DIR}/.opensquilla-wheelhouse-${WHEEL_HASH}" export PATH="${SCRIPT_DIR}:${VENV_DIR}/bin:${SCRIPT_DIR}/runtime/python/bin:${PATH}" RELEASE_ID="$( "${PYTHON_BIN}" -c ' import hashlib, sys print(hashlib.sha256(f"{sys.argv[1]}|{sys.argv[2]}".encode("utf-8")).hexdigest()[:12]) ' "${SCRIPT_DIR}" "${WHEEL_HASH}" )" DATA_BASE="${XDG_DATA_HOME:-${HOME}/.local/share}" PORTABLE_DATA_DIR="${OPENSQUILLA_PORTABLE_HOME:-${DATA_BASE}/OpenSquilla/portable/${RELEASE_ID}}" if [[ -z "${OPENSQUILLA_GATEWAY_CONFIG_PATH:-}" ]]; then export OPENSQUILLA_GATEWAY_CONFIG_PATH="${PORTABLE_DATA_DIR}/config.toml" fi if [[ -z "${OPENSQUILLA_STATE_DIR:-}" ]]; then export OPENSQUILLA_STATE_DIR="${PORTABLE_DATA_DIR}" fi if [[ -z "${OPENSQUILLA_GATEWAY_STATE_DIR:-}" ]]; then export OPENSQUILLA_GATEWAY_STATE_DIR="${OPENSQUILLA_STATE_DIR}/state" fi if [[ -z "${OPENSQUILLA_GATEWAY_WORKSPACE_DIR:-}" ]]; then export OPENSQUILLA_GATEWAY_WORKSPACE_DIR="${OPENSQUILLA_STATE_DIR}/workspace" fi mkdir -p "${OPENSQUILLA_STATE_DIR}" if [[ ! -x "${VENV_PYTHON}" ]]; then echo "Creating local OpenSquilla environment..." "${PYTHON_BIN}" -m venv --without-pip "${VENV_DIR}" fi if [[ ! -f "${INSTALL_MARKER}" ]]; then echo "Installing OpenSquilla from bundled wheels..." SITE_PACKAGES="$("${VENV_PYTHON}" -c 'import site; print(site.getsitepackages()[0])')" "${PYTHON_BIN}" - "${PACKAGE_DIR}" "${SITE_PACKAGES}" <<'PY' import pathlib import shutil import sys import zipfile package_dir = pathlib.Path(sys.argv[1]) site_packages = pathlib.Path(sys.argv[2]) site_packages.mkdir(parents=True, exist_ok=True) for wheel_path in sorted(package_dir.glob("*.whl")): with zipfile.ZipFile(wheel_path) as wheel: for info in wheel.infolist(): name = info.filename if not name or name.endswith("/"): continue if ".data/" in name: _prefix, data_rel = name.split(".data/", 1) kind, _sep, remainder = data_rel.partition("/") if kind not in {"purelib", "platlib"} or not remainder: continue target_rel = pathlib.Path(remainder) else: target_rel = pathlib.Path(name) target = site_packages / target_rel target.parent.mkdir(parents=True, exist_ok=True) with wheel.open(info) as src, target.open("wb") as dst: shutil.copyfileobj(src, dst) PY touch "${INSTALL_MARKER}" fi OPENSQUILLA_BIN="${VENV_PYTHON}" OPENSQUILLA_MODULE=( "-m" "opensquilla.cli.main" ) if [[ "${CLI_MODE}" == "1" ]]; then exec "${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" "$@" fi if [[ ! -f "${OPENSQUILLA_GATEWAY_CONFIG_PATH}" && -n "${OPENROUTER_API_KEY:-}" ]]; then "${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" onboard \\ --provider openrouter \\ --api-key-env OPENROUTER_API_KEY \\ --minimal else "${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" onboard fi echo echo "Starting OpenSquilla gateway." echo "Web UI: http://127.0.0.1:18791/control/" echo "Press Ctrl+C in this terminal to stop the gateway." if [[ -t 1 ]]; then exec "${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" gateway run else LOG_DIR="${OPENSQUILLA_STATE_DIR}/logs" mkdir -p "${LOG_DIR}" CONSOLE_LOG="${OPENSQUILLA_STATE_DIR}/logs/gateway-console.log" echo "Console log: ${CONSOLE_LOG}" "${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" gateway run 2>&1 | tee -a "${CONSOLE_LOG}" exit "${PIPESTATUS[0]}" fi """ return script.replace("__TARGET__", target) def render_start_ps1(profile: str = "recommended") -> str: target = _install_target("opensquilla", profile) requires_router_runtime = "$true" if profile == "recommended" else "$false" script = """param( [switch]$Cli, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$CliArgs ) $ErrorActionPreference = "Stop" $env:PYTHONUTF8 = '1' $env:PYTHONIOENCODING = 'utf-8:replace' $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $PackageDir = Join-Path $ScriptDir 'packages' $PythonBin = Join-Path $ScriptDir 'runtime\\python\\python.exe' $VenvBase = if ($env:LOCALAPPDATA) { $env:LOCALAPPDATA } else { $env:TEMP } $VenvRoot = Join-Path $VenvBase 'OpenSquilla\\venvs' $RequiresRouterRuntime = __REQUIRES_ROUTER_RUNTIME__ if ((-not $env:OPENSQUILLA_LLM_API_KEY) -and $env:OPENROUTER_API_KEY) { $env:OPENSQUILLA_LLM_API_KEY = $env:OPENROUTER_API_KEY } if (-not (Test-Path $PythonBin)) { throw "Bundled Python runtime not found: $PythonBin" } if (-not (Test-Path $PackageDir)) { throw "OpenSquilla package directory not found: $PackageDir" } function Test-WindowsVCRedistInstalled { if (-not [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform( [System.Runtime.InteropServices.OSPlatform]::Windows )) { return $true } $runtimeKeys = @( 'HKLM:\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64', 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64' ) foreach ($key in $runtimeKeys) { if (-not (Test-Path $key)) { continue } $runtime = Get-ItemProperty -Path $key -ErrorAction SilentlyContinue if ($runtime -and $runtime.Installed -eq 1 -and $runtime.Major -ge 14) { return $true } } return $false } function Test-WindowsAdmin { if (-not [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform( [System.Runtime.InteropServices.OSPlatform]::Windows )) { return $true } $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = [Security.Principal.WindowsPrincipal]::new($identity) return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Get-WindowsVCRedistInstaller { $redistUrl = 'https://aka.ms/vs/17/release/vc_redist.x64.exe' $candidateInstallers = @( (Join-Path $ScriptDir 'vc_redist.x64.exe'), (Join-Path $ScriptDir 'redist\\vc_redist.x64.exe'), (Join-Path $ScriptDir 'runtime\\vc_redist.x64.exe') ) $installerPath = $candidateInstallers | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1 if ($installerPath) { return $installerPath } $downloadDir = Join-Path ([System.IO.Path]::GetTempPath()) 'OpenSquilla' $installerPath = Join-Path $downloadDir 'vc_redist.x64.exe' New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null Write-Host ( 'OpenSquilla: downloading Microsoft Visual C++ Redistributable ' + '2015-2022 x64 from Microsoft.' ) try { Invoke-WebRequest -Uri $redistUrl -OutFile $installerPath -UseBasicParsing return $installerPath } catch { Write-Warning ( 'OpenSquilla: could not download Microsoft Visual C++ ' + "Redistributable from $redistUrl. Error: $($_.Exception.Message)" ) return $null } } function Install-WindowsVCRedistWithInstaller { param( [switch]$Repair ) $installerPath = Get-WindowsVCRedistInstaller if (-not $installerPath) { return $false } $action = if ($Repair) { 'repairing' } else { 'installing' } Write-Host ( "OpenSquilla: $action Microsoft Visual C++ Redistributable 2015-2022 x64..." ) $redistArgs = if ($Repair) { @('/repair', '/quiet', '/norestart') } else { @('/install', '/quiet', '/norestart') } try { if (Test-WindowsAdmin) { $process = Start-Process -FilePath $installerPath ` -ArgumentList $redistArgs ` -Wait ` -PassThru } else { Write-Host ( 'OpenSquilla: administrator approval may be requested to ' + 'install or repair Microsoft Visual C++ Redistributable.' ) $process = Start-Process -FilePath $installerPath ` -ArgumentList $redistArgs ` -Verb RunAs ` -Wait ` -PassThru } } catch { Write-Warning ( 'OpenSquilla: Visual C++ Redistributable installer could not be ' + "started. Error: $($_.Exception.Message)" ) return $false } if ($process.ExitCode -in @(0, 1638, 3010)) { Write-Host 'OpenSquilla: Microsoft Visual C++ Redistributable is ready.' if ($process.ExitCode -eq 3010) { Write-Warning ( 'OpenSquilla: the Visual C++ installer requested a reboot; ' + 'restart Windows if ONNX Runtime still fails to load.' ) } return $true } Write-Warning ( 'OpenSquilla: Microsoft Visual C++ Redistributable installer exited ' + "with code $($process.ExitCode)." ) return $false } function Install-WindowsVCRedistIfNeeded { param( [switch]$Repair ) if (-not $RequiresRouterRuntime) { return $true } if ($env:OPENSQUILLA_SKIP_VC_REDIST -eq '1') { Write-Host ( 'OpenSquilla: skipping Microsoft Visual C++ Redistributable check ' + 'because OPENSQUILLA_SKIP_VC_REDIST=1.' ) return $true } if ((Test-WindowsVCRedistInstalled) -and -not $Repair) { return $true } $redistUrl = 'https://aka.ms/vs/17/release/vc_redist.x64.exe' if ($Repair) { if (Install-WindowsVCRedistWithInstaller -Repair) { return $true } } elseif (Install-WindowsVCRedistWithInstaller) { return $true } $winget = if ($Repair) { $null } else { Get-Command winget -ErrorAction SilentlyContinue } if ($winget) { Write-Host ( 'OpenSquilla: Microsoft Visual C++ Redistributable not detected; ' + 'installing with winget.' ) $wingetArgs = @( 'install', '--id', 'Microsoft.VCRedist.2015+.x64', '--exact', '--silent', '--accept-package-agreements', '--accept-source-agreements' ) & winget @wingetArgs if ($LASTEXITCODE -eq 0) { Write-Host 'OpenSquilla: Microsoft Visual C++ Redistributable installation completed.' return $true } Write-Warning ( 'OpenSquilla: winget could not install Microsoft Visual C++ ' + "Redistributable (exit $LASTEXITCODE)." ) } Write-Warning ( 'OpenSquilla: Microsoft Visual C++ Redistributable 2015-2022 x64 is ' + 'required for the bundled ONNX router.' ) Write-Warning ( 'OpenSquilla can still start with safe router fallback, but bundled ' + 'ONNX model routing is disabled until this runtime is installed.' ) Write-Warning ( "If automatic installation fails, install it manually: $redistUrl" ) Write-Warning ( 'After installing, reopen PowerShell and restart OpenSquilla.' ) return $false } function Test-OnnxRuntimeImport { if (-not (Test-Path $VenvPython)) { return $false } & $VenvPython -c "import onnxruntime as ort; print('onnxruntime', ort.__version__)" | Out-Host return ($LASTEXITCODE -eq 0) } function Repair-WindowsVCRedistForOnnxIfNeeded { if (-not $RequiresRouterRuntime) { return } if ($env:OPENSQUILLA_SKIP_VC_REDIST -eq '1') { return } if (Test-OnnxRuntimeImport) { return } Write-Warning ( 'OpenSquilla: ONNX Runtime failed to import after setup. Attempting ' + 'Visual C++ Redistributable repair before starting the gateway.' ) Install-WindowsVCRedistIfNeeded -Repair | Out-Null if (Test-OnnxRuntimeImport) { return } Write-Warning ( 'OpenSquilla: ONNX Runtime still failed after Visual C++ repair. If ' + 'the router warning remains, check CPU/VM AVX compatibility or install ' + 'the Microsoft Visual C++ Redistributable manually: ' + 'https://aka.ms/vs/17/release/vc_redist.x64.exe' ) } if (-not (Test-Path $VenvRoot)) { New-Item -ItemType Directory -Path $VenvRoot -Force | Out-Null } $OpenSquillaWheel = Get-ChildItem -Path $PackageDir -Filter 'opensquilla-*.whl' | Sort-Object Name | Select-Object -First 1 if (-not $OpenSquillaWheel) { throw "OpenSquilla wheel not found in $PackageDir" } $Sha256 = [System.Security.Cryptography.SHA256]::Create() $WheelStream = [System.IO.File]::OpenRead($OpenSquillaWheel.FullName) try { $WheelHashFull = -join ($Sha256.ComputeHash($WheelStream) | ForEach-Object { $_.ToString('x2') }) } finally { $WheelStream.Dispose() $Sha256.Dispose() } $WheelHash = $WheelHashFull.Substring(0, 12).ToLowerInvariant() $Hash = [System.Security.Cryptography.SHA256]::Create().ComputeHash( [System.Text.Encoding]::UTF8.GetBytes("$ScriptDir|$WheelHash") ) $ReleaseId = -join ($Hash[0..5] | ForEach-Object { $_.ToString('x2') }) $VenvDir = Join-Path $VenvRoot $ReleaseId $VenvPython = Join-Path $VenvDir 'Scripts\\python.exe' $InstallMarker = Join-Path $VenvDir ".opensquilla-wheelhouse-$WheelHash" $env:PATH = "$ScriptDir;$ScriptDir\\runtime\\python;$env:PATH" $env:PATH = "$VenvDir\\Scripts;$env:PATH" $PortableDataDir = if ($env:OPENSQUILLA_PORTABLE_HOME) { $env:OPENSQUILLA_PORTABLE_HOME } else { Join-Path $VenvBase "OpenSquilla\\portable\\$ReleaseId" } if (-not $env:OPENSQUILLA_GATEWAY_CONFIG_PATH) { $env:OPENSQUILLA_GATEWAY_CONFIG_PATH = Join-Path $PortableDataDir 'config.toml' } if (-not $env:OPENSQUILLA_STATE_DIR) { $env:OPENSQUILLA_STATE_DIR = $PortableDataDir } if (-not $env:OPENSQUILLA_GATEWAY_STATE_DIR) { $env:OPENSQUILLA_GATEWAY_STATE_DIR = Join-Path $env:OPENSQUILLA_STATE_DIR 'state' } if (-not $env:OPENSQUILLA_GATEWAY_WORKSPACE_DIR) { $env:OPENSQUILLA_GATEWAY_WORKSPACE_DIR = Join-Path $env:OPENSQUILLA_STATE_DIR 'workspace' } New-Item -ItemType Directory -Path $env:OPENSQUILLA_STATE_DIR -Force | Out-Null Install-WindowsVCRedistIfNeeded | Out-Null if (-not (Test-Path $VenvPython)) { Write-Host "Creating local OpenSquilla environment..." & $PythonBin -m venv --without-pip $VenvDir if ($LASTEXITCODE -ne 0) { throw "OpenSquilla environment creation failed with exit code $LASTEXITCODE." } } if (-not (Test-Path $InstallMarker)) { Write-Host "Installing OpenSquilla from bundled wheels..." $SitePackages = & $VenvPython -c "import site; print(site.getsitepackages()[0])" if ($LASTEXITCODE -ne 0) { throw "OpenSquilla site-packages lookup failed with exit code $LASTEXITCODE." } $WheelInstallScript = @' import pathlib import shutil import sys import zipfile package_dir = pathlib.Path(sys.argv[1]) site_packages = pathlib.Path(sys.argv[2]) site_packages.mkdir(parents=True, exist_ok=True) for wheel_path in sorted(package_dir.glob("*.whl")): with zipfile.ZipFile(wheel_path) as wheel: for info in wheel.infolist(): name = info.filename if not name or name.endswith("/"): continue if ".data/" in name: _prefix, data_rel = name.split(".data/", 1) kind, _sep, remainder = data_rel.partition("/") if kind not in {"purelib", "platlib"} or not remainder: continue target_rel = pathlib.Path(remainder) else: target_rel = pathlib.Path(name) target = site_packages / target_rel target.parent.mkdir(parents=True, exist_ok=True) with wheel.open(info) as src, target.open("wb") as dst: shutil.copyfileobj(src, dst) '@ $WheelInstallScript | & $PythonBin - $PackageDir $SitePackages if ($LASTEXITCODE -ne 0) { throw "OpenSquilla bundled wheel installation failed with exit code $LASTEXITCODE." } New-Item -ItemType File -Path $InstallMarker -Force | Out-Null } Repair-WindowsVCRedistForOnnxIfNeeded $OpenSquillaArgs = @("-m", "opensquilla.cli.main") if ($Cli) { & $VenvPython @OpenSquillaArgs @CliArgs exit $LASTEXITCODE } if ((-not (Test-Path $env:OPENSQUILLA_GATEWAY_CONFIG_PATH)) -and $env:OPENROUTER_API_KEY) { & $VenvPython @OpenSquillaArgs onboard ` --provider openrouter ` --api-key-env OPENROUTER_API_KEY ` --minimal } else { & $VenvPython @OpenSquillaArgs onboard } if ($LASTEXITCODE -ne 0) { throw "OpenSquilla onboarding failed with exit code $LASTEXITCODE." } Write-Host "" Write-Host "Starting OpenSquilla gateway." Write-Host "Web UI: http://127.0.0.1:18791/control/" Write-Host "Press Ctrl+C in this terminal to stop the gateway." $OutputRedirected = [Console]::IsOutputRedirected if (-not $OutputRedirected) { & $VenvPython @OpenSquillaArgs gateway run $GatewayExitCode = $LASTEXITCODE } else { $LogDir = Join-Path $env:OPENSQUILLA_STATE_DIR 'logs' New-Item -ItemType Directory -Path $LogDir -Force | Out-Null $ConsoleLog = Join-Path $LogDir 'gateway-console.log' Write-Host "Console log: $ConsoleLog" $PreviousErrorActionPreference = $ErrorActionPreference $ErrorActionPreference = "Continue" try { & $VenvPython @OpenSquillaArgs gateway run 2>&1 | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.ToString() } else { $_ } } | Tee-Object -FilePath $ConsoleLog -Append $GatewayExitCode = $LASTEXITCODE } finally { $ErrorActionPreference = $PreviousErrorActionPreference } } exit $GatewayExitCode """ return script.replace("__TARGET__", target).replace( "__REQUIRES_ROUTER_RUNTIME__", requires_router_runtime ) def render_cli_sh() -> str: return """#!/bin/sh SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" exec "${SCRIPT_DIR}/start.sh" --cli "$@" """ def render_cli_cmd() -> str: return ( "@echo off\r\n" 'cd /d "%~dp0"\r\n' 'set "OSQ_POWERSHELL=powershell.exe"\r\n' 'where pwsh.exe >nul 2>nul && set "OSQ_POWERSHELL=pwsh.exe"\r\n' '"%OSQ_POWERSHELL%" -NoLogo -NoProfile -ExecutionPolicy Bypass ' '-File "%~dp0start.ps1" -Cli %*\r\n' ) def render_shell_cmd() -> str: return ( "@echo off\r\n" "title OpenSquilla Shell\r\n" 'cd /d "%~dp0"\r\n' 'set "OSQ_POWERSHELL=powershell.exe"\r\n' 'where pwsh.exe >nul 2>nul && set "OSQ_POWERSHELL=pwsh.exe"\r\n' '"%OSQ_POWERSHELL%" -NoLogo -NoExit -NoProfile -ExecutionPolicy Bypass -Command ' "\"Set-Location -LiteralPath '%~dp0'; " "function global:opensquilla { & (Join-Path (Get-Location) 'opensquilla.cmd') @args }; " "Write-Host 'OpenSquilla portable shell'; " "Write-Host 'Run commands like:'; " "Write-Host ' opensquilla onboard --provider openrouter'; " "Write-Host ' opensquilla gateway run'\"\r\n" ) def render_start_cmd() -> str: return ( "@echo off\r\n" "title OpenSquilla Gateway\r\n" 'cd /d "%~dp0"\r\n' 'set "OSQ_POWERSHELL=powershell.exe"\r\n' 'where pwsh.exe >nul 2>nul && set "OSQ_POWERSHELL=pwsh.exe"\r\n' '"%OSQ_POWERSHELL%" -NoLogo -NoExit -NoProfile -ExecutionPolicy Bypass ' '-File "%~dp0start.ps1"\r\n' ) def render_readme( *, app_version: str, profile: str, platform_tag: str, python_major: int, python_minor: int, portable: bool, ) -> str: windows_target = platform_tag.startswith("windows-") if portable: release_kind = "Portable Release" unix_commands = "bash start.sh" windows_command = ".\\start.ps1" python_note = "Python is bundled in this zip." setup_note = ( "Config, workspace, logs, memory, and runtime state use the normal " "user-level OpenSquilla directory." ) else: release_kind = "Wheelhouse Release" unix_commands = "bash install.sh\nopensquilla gateway run" windows_command = ".\\install.ps1\nopensquilla gateway run" python_note = f"Requires Python {python_major}.{python_minor}." setup_note = ( "The installer runs idempotent onboarding after installation. To " "reconfigure later, run `opensquilla onboard` for the full wizard or " "`opensquilla configure
` for one area." ) if windows_target: if portable: command_section = f"""## Windows 1. Right-click `Start OpenSquilla.cmd` -> **Run as administrator**. 2. Complete onboarding. 3. Open `http://127.0.0.1:18791/control/`. Notes: - Keep the terminal open. Closing it stops the gateway. - OpenSquilla 0.1.0 preview builds are unsigned. The supported portable launch path is administrator launch. - If SmartScreen appears, choose **More info** -> **Run anyway**. - If Smart App Control or enterprise policy blocks the unsigned app, use the `uv tool install` wheel path instead. - Microsoft documents that SmartScreen checks downloaded apps and Smart App Control can block unknown, unsigned code.
Advanced portable usage Use these options only when you want scripted setup or portable CLI commands. To start from PowerShell: ```powershell Set-ExecutionPolicy -Scope Process Bypass {windows_command} ``` If `OPENROUTER_API_KEY` is present and no local config exists, the launcher writes an OpenRouter env-reference config and starts the gateway without asking you to paste the key. The portable package does not install a global `opensquilla` command. For a terminal where `opensquilla ...` commands work, run `OpenSquilla Shell.cmd`, or run commands from this folder: ```powershell .\\opensquilla.cmd onboard ```
""" else: command_section = f"""## Windows PowerShell ```powershell Set-ExecutionPolicy -Scope Process Bypass {windows_command} ``` """ else: if portable: command_section = f"""## macOS / Linux 1. Run the launcher from the extracted folder: ```sh {unix_commands} ``` 2. Keep the terminal open. Closing the terminal stops the gateway. 3. Complete onboarding. On first run, choose a provider and paste the requested keys; later starts let you review or change the config. 4. Open `http://127.0.0.1:18791/control/`. """ else: command_section = f"""## macOS / Linux ```sh {unix_commands} ``` """ web_ui_note = ( "" if portable else "Open `http://127.0.0.1:18791/control/`.\n\n" ) return f"""# OpenSquilla {app_version} {release_kind} Build target: - platform: `{platform_tag}` - Python: `{python_major}.{python_minor}` - profile: `{profile}` {command_section.rstrip()} {web_ui_note}{python_note} {setup_note} """ def write_manifest( path: Path, *, app_version: str, profile: str, platform_tag: str, python_major: int, python_minor: int, wheel_name: str, package_count: int, include_router_assets: bool, portable: bool, runtime_release: str, runtime_asset: str, ) -> None: payload = { "name": "OpenSquilla wheelhouse zip", "version": app_version, "profile": profile, "platform_tag": platform_tag, "python": f"{python_major}.{python_minor}", "wheel_name": wheel_name, "package_count": package_count, "include_router_assets": include_router_assets, "portable": portable, "runtime_release": runtime_release, "runtime_asset": runtime_asset, } path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8") def prepare_release_tree( release_root: Path, wheel_path: Path, *, app_version: str, profile: str, platform_tag: str, python_major: int, python_minor: int, include_router_assets: bool, portable: bool, runtime_release: str, runtime_asset: str, runtime_root: Path | None = None, ) -> Path: if release_root.exists(): shutil.rmtree(release_root) package_dir = release_root / "packages" package_dir.mkdir(parents=True) bundled_wheel = package_dir / wheel_path.name shutil.copy2(wheel_path, bundled_wheel) if portable: if runtime_root is None or not runtime_root.is_dir(): raise SystemExit("Portable release requires a Python runtime directory.") runtime_target = release_root / "runtime" / "python" shutil.copytree(runtime_root, runtime_target) prune_portable_runtime(runtime_target) start_sh = release_root / "start.sh" start_sh.write_text(render_start_sh(profile), encoding="utf-8") start_sh.chmod(start_sh.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) cli_sh = release_root / "opensquilla" cli_sh.write_text(render_cli_sh(), encoding="utf-8") cli_sh.chmod(cli_sh.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) (release_root / "start.ps1").write_text(render_start_ps1(profile), encoding="utf-8") if platform_tag.startswith("windows-"): (release_root / "Start OpenSquilla.cmd").write_text( render_start_cmd(), encoding="utf-8", newline="", ) (release_root / "opensquilla.cmd").write_text( render_cli_cmd(), encoding="utf-8", newline="", ) (release_root / "OpenSquilla Shell.cmd").write_text( render_shell_cmd(), encoding="utf-8", newline="", ) else: install_sh = release_root / "install.sh" install_sh.write_text( render_install_sh( wheel_name=wheel_path.name, profile=profile, python_major=python_major, python_minor=python_minor, ), encoding="utf-8", ) install_sh.chmod(install_sh.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) (release_root / "install.ps1").write_text( render_install_ps1( wheel_name=wheel_path.name, profile=profile, python_major=python_major, python_minor=python_minor, ), encoding="utf-8", ) (release_root / "README.md").write_text( render_readme( app_version=app_version, profile=profile, platform_tag=platform_tag, python_major=python_major, python_minor=python_minor, portable=portable, ), encoding="utf-8", ) copy_release_notices(release_root) write_manifest( release_root / "manifest.json", app_version=app_version, profile=profile, platform_tag=platform_tag, python_major=python_major, python_minor=python_minor, wheel_name=wheel_path.name, package_count=len(list(package_dir.glob("*.whl"))), include_router_assets=include_router_assets, portable=portable, runtime_release=runtime_release, runtime_asset=runtime_asset, ) return bundled_wheel def create_zip(release_root: Path, zip_path: Path, *, archive_root: str | None = None) -> None: zip_path.parent.mkdir(parents=True, exist_ok=True) if zip_path.exists(): zip_path.unlink() root_parent = release_root.parent with ZipFile(zip_path, "w", ZIP_DEFLATED) as archive: for path in sorted(p for p in release_root.rglob("*") if p.is_file()): if archive_root: rel = Path(archive_root, path.relative_to(release_root)).as_posix() else: rel = path.relative_to(root_parent).as_posix() info = ZipInfo(rel) info.compress_type = ZIP_DEFLATED source_mode = stat.S_IMODE(path.stat().st_mode) executable_by_path = rel.endswith(("/install.sh", "/start.sh")) or ( "/runtime/python/bin/" in rel ) mode = ( 0o755 if executable_by_path or source_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) else 0o644 ) info.external_attr = mode << 16 archive.writestr(info, path.read_bytes()) def sha256_digest(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() def write_sha256(path: Path) -> Path: digest = sha256_digest(path) checksum_path = path.with_suffix(path.suffix + ".sha256") checksum_path.write_text(f"{digest} {path.name}\n", encoding="utf-8") return checksum_path def write_sha256s(paths: tuple[Path, ...] | list[Path], checksum_path: Path) -> Path: lines = [f"{sha256_digest(path)} {path.name}" for path in sorted(paths, key=lambda p: p.name)] checksum_path.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8") return checksum_path def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--profile", choices=("recommended", "core"), default="recommended") parser.add_argument("--output-dir", type=Path, default=Path("dist")) parser.add_argument("--work-dir", type=Path, default=Path("build/wheelhouse-zip")) parser.add_argument( "--bundle-python-runtime", action="store_true", help="Bundle a portable python-build-standalone runtime and start scripts.", ) parser.add_argument( "--platform-tag", choices=( "linux-arm64", "linux-x64", "macos-arm64", "macos-x64", "windows-arm64", "windows-x64", ), help="Target platform tag. Defaults to the current host platform.", ) parser.add_argument( "--python-runtime-release", default=DEFAULT_RUNTIME_RELEASE, help="python-build-standalone release tag to bundle.", ) parser.add_argument( "--python-runtime-version", default=DEFAULT_RUNTIME_PYTHON_VERSION, help="Full CPython runtime version from python-build-standalone.", ) parser.add_argument( "--python-runtime-archive", type=Path, help="Use a pre-downloaded python-build-standalone install_only archive.", ) parser.add_argument( "--skip-wheelhouse", action="store_true", help="Only place the OpenSquilla wheel in packages/ for script debugging.", ) parser.add_argument( "--skip-zip", action="store_true", help="Prepare the release directory without creating the zip.", ) return parser def main(argv: list[str] | None = None) -> int: args = build_parser().parse_args(argv) repo_root = Path(__file__).resolve().parents[1] app_version = read_project_version(repo_root) include_router_assets = args.profile == "recommended" if include_router_assets: model_root = repo_root / "src" / "opensquilla" / "squilla_router" / "models" asset_check = check_router_assets(model_root) if not asset_check.ok: for path in asset_check.missing_files: print(f"Missing router asset: {path}", file=sys.stderr) for path in asset_check.pointer_files: print(f"Git LFS pointer file is not hydrated: {path}", file=sys.stderr) print( 'Run: git lfs pull --include="src/opensquilla/squilla_router/models/**"', file=sys.stderr, ) return 1 work_dir = (repo_root / args.work_dir).resolve() env = build_subprocess_env(work_dir, repo_root=repo_root) wheel_dir = work_dir / "wheels" tag = args.platform_tag or platform_tag() if not args.skip_wheelhouse: validate_wheelhouse_target_platform(tag) name = release_name( app_version=app_version, platform_tag=tag, python_major=sys.version_info.major, python_minor=sys.version_info.minor, profile=args.profile, portable=args.bundle_python_runtime, ) release_root = work_dir / name runtime_root: Path | None = None runtime_asset = "" if args.bundle_python_runtime: if sys.version_info[:2] != (3, 12): raise SystemExit("Portable release builds currently require Python 3.12.") if args.python_runtime_archive: runtime_archive = args.python_runtime_archive.resolve() runtime_asset = runtime_archive.name else: runtime_archive, runtime_asset = download_python_runtime_archive( download_dir=work_dir / "runtime-downloads", python_version=args.python_runtime_version, runtime_release=args.python_runtime_release, platform_tag=tag, ) runtime_root = work_dir / "runtime" / "python" extract_python_runtime_archive(runtime_archive, runtime_root) wheel_path = build_wheel(repo_root, wheel_dir, env) missing_runtime_modules = missing_required_runtime_modules_in_wheel(wheel_path) if missing_runtime_modules: print("Built wheel is missing required runtime modules:", file=sys.stderr) for entry in missing_runtime_modules: print(f" {entry}", file=sys.stderr) return 1 wheel_violations = forbidden_release_wheel_paths(wheel_path) if wheel_violations: print("Built wheel contains forbidden release entries:", file=sys.stderr) for entry in wheel_violations: print(f" {entry}", file=sys.stderr) return 1 text_violations = forbidden_release_text_hits(wheel_path) if text_violations: print("Built wheel contains internal release text markers:", file=sys.stderr) for entry in text_violations: print(f" {entry}", file=sys.stderr) return 1 if include_router_assets: missing_from_wheel = missing_router_assets_in_wheel(wheel_path) if missing_from_wheel: print("Router assets are hydrated but missing from the built wheel:", file=sys.stderr) for entry in missing_from_wheel: print(f" {entry}", file=sys.stderr) return 1 bundled_wheel = prepare_release_tree( release_root, wheel_path, app_version=app_version, profile=args.profile, platform_tag=tag, python_major=sys.version_info.major, python_minor=sys.version_info.minor, include_router_assets=include_router_assets, portable=args.bundle_python_runtime, runtime_release=args.python_runtime_release if args.bundle_python_runtime else "", runtime_asset=runtime_asset, runtime_root=runtime_root, ) package_dir = bundled_wheel.parent if not args.skip_wheelhouse: download_wheelhouse( package_dir, bundled_wheel, args.profile, env, target_platform_tag=tag, python_major=sys.version_info.major, python_minor=sys.version_info.minor, ) write_manifest( release_root / "manifest.json", app_version=app_version, profile=args.profile, platform_tag=tag, python_major=sys.version_info.major, python_minor=sys.version_info.minor, wheel_name=bundled_wheel.name, package_count=len(list(package_dir.glob("*.whl"))), include_router_assets=include_router_assets, portable=args.bundle_python_runtime, runtime_release=args.python_runtime_release if args.bundle_python_runtime else "", runtime_asset=runtime_asset, ) if args.skip_zip: print(release_root) return 0 zip_path = (repo_root / args.output_dir / f"{name}.zip").resolve() archive_root = f"OpenSquilla-{app_version}" if args.bundle_python_runtime else None create_zip(release_root, zip_path, archive_root=archive_root) checksum_path = write_sha256(zip_path) checksums_path = write_sha256s( tuple(zip_path.parent.glob("*.zip")), zip_path.parent / "SHA256SUMS" ) print(zip_path) print(checksum_path) print(checksums_path) return 0 if __name__ == "__main__": raise SystemExit(main())