Ctrl K
ComunidadGuías…

bootstrapper/_run.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env sh
#
# Shared dispatcher used by start.sh, stop.sh, and
# bootstrapper/generate_supabase_keys.sh.
#
# Picks `uv` when available (fast venv resolution, locked deps), falls
# back to system `python3` otherwise. Usage:
#
#   sh bootstrapper/_run.sh <script.py> [args...]
#
# The <script.py> path is interpreted relative to this file's directory
# (i.e. bootstrapper/), so callers can pass plain `start.py`, `stop.py`,
# or `generate_supabase_keys.py`.

set -e

if [ $# -lt 1 ]; then
    echo "Usage: $0 <python-script-relative-to-bootstrapper> [args...]" >&2
    exit 64
fi

SCRIPT_REL="$1"
shift

# Resolve the bootstrapper directory (this script's parent).
# CDPATH= keeps cd from echoing a path into the captured value.
SELF_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"

if command -v uv >/dev/null 2>&1; then
    # Banner to stderr: stdout must stay clean so `<script> --format json`
    # (e.g. `doctor --format json`) is directly parseable without a shim (#650).
    echo "📦 Using uv for dependency management..." >&2
    # --no-dev: the launcher needs only the runtime dependencies, the same set
    # the system-Python branch installs. The dev group (test and docs
    # toolchain) stays opt-in: `uv sync --group dev`, as CI does.
    exec uv run --no-dev --directory "$SELF_DIR" "$SCRIPT_REL" "$@"
elif command -v python3 >/dev/null 2>&1; then
    echo "📦 Using system Python (install uv for better dependency management)..." >&2
    BOOTSTRAPPER_VENV="${ATLAS_BOOTSTRAPPER_VENV:-$SELF_DIR/.venv}"
    VENV_PYTHON="$BOOTSTRAPPER_VENV/bin/python"
    BOOTSTRAPPER_LOCK="$SELF_DIR/requirements-locked.txt"
    PYTHON_SUPPORTED='import sys; raise SystemExit(sys.version_info < (3, 11))'
    REQUIRED_IMPORTS="import click, jsonschema, PIL, requests, rich, textual, textual_image, urllib3, yaml"

    # Fingerprint the DEPENDENCY DECLARATION, not the module list. The guard
    # used to be presence-only: if those modules imported at ANY version, the
    # install was skipped forever. So the security floors declared in
    # pyproject.toml (click, urllib3, pillow) were never applied to an
    # existing venv, and the two dispatch branches produced materially
    # different dependency states from identical inputs (the uv branch
    # re-resolves against uv.lock every run). A new dependency added to
    # pyproject.toml but not to REQUIRED_IMPORTS was likewise never installed.
    DEPS_STAMP="$BOOTSTRAPPER_VENV/.atlas-deps-stamp"
    if command -v shasum >/dev/null 2>&1; then
        WANT_STAMP="$(shasum -a 256 "$SELF_DIR/pyproject.toml" "$BOOTSTRAPPER_LOCK" | shasum -a 256 | cut -d' ' -f1)"
    elif command -v sha256sum >/dev/null 2>&1; then
        WANT_STAMP="$(sha256sum "$SELF_DIR/pyproject.toml" "$BOOTSTRAPPER_LOCK" | sha256sum | cut -d' ' -f1)"
    elif command -v cksum >/dev/null 2>&1; then
        # POSIX fallback: unlike the old size-only stamp, CRC+length changes
        # for same-size edits to either locked dependency input.
        WANT_STAMP="$(cksum "$SELF_DIR/pyproject.toml" "$BOOTSTRAPPER_LOCK" | cksum | awk '{print $1 "-" $2}')"
    else
        echo "❌ No checksum tool is available to validate the locked dependency graph." >&2
        exit 1
    fi
    HAVE_STAMP=""
    if [ -f "$DEPS_STAMP" ]; then
        HAVE_STAMP="$(cat "$DEPS_STAMP" 2>/dev/null || true)"
    fi

    # Does the venv meet the Python floor and satisfy every import? The stamp
    # also decides whether a locked refresh is required; failures always close.
    VENV_USABLE=0
    if [ -x "$VENV_PYTHON" ] \
        && "$VENV_PYTHON" -c "$PYTHON_SUPPORTED" >/dev/null 2>&1 \
        && "$VENV_PYTHON" -c "$REQUIRED_IMPORTS" >/dev/null 2>&1; then
        VENV_USABLE=1
    fi

    if [ "$VENV_USABLE" = "0" ] || [ "$HAVE_STAMP" != "$WANT_STAMP" ]; then
        # Create or REPAIR whenever the venv cannot satisfy the imports —
        # absent, or present but broken. `python3 -m venv` on an existing
        # directory re-runs ensurepip, which is what restores a missing pip in
        # a `uv venv`-created environment; gating this on absence alone turned
        # "pip-less venv plus a newly added dependency" from a self-repair into
        # a hard launch failure.
        #
        # A pure stamp refresh (venv usable, declaration changed) skips this
        # entirely and so does NOT re-run the interpreter gate: that venv
        # already has a supported Python, and gating on the SYSTEM python3
        # would refuse to apply a security floor on a host whose system
        # interpreter has since aged out.
        if [ "$VENV_USABLE" = "0" ]; then
            if [ -x "$VENV_PYTHON" ]; then
                echo "Repairing Atlas bootstrapper environment at $BOOTSTRAPPER_VENV..." >&2
            else
                echo "Creating Atlas bootstrapper environment at $BOOTSTRAPPER_VENV..." >&2
            fi
            if ! python3 -c "$PYTHON_SUPPORTED"; then
                echo "❌ Atlas requires Python 3.11 or newer." >&2
                exit 1
            fi
            if ! python3 -m venv "$BOOTSTRAPPER_VENV"; then
                echo "❌ Could not create the Atlas bootstrapper virtual environment." >&2
                echo "   Install the Python venv module or install uv, then retry." >&2
                exit 1
            fi
            # venv creation can retain an older bin/python symlink. Never
            # install into or launch an interpreter below the supported floor.
            if ! "$VENV_PYTHON" -c "$PYTHON_SUPPORTED"; then
                echo "❌ Atlas requires Python 3.11 or newer." >&2
                echo "   The existing environment still has an unsupported interpreter." >&2
                echo "   Delete $BOOTSTRAPPER_VENV and rerun this command to recreate it." >&2
                echo "   Or create a fresh environment and select it with ATLAS_BOOTSTRAPPER_VENV," >&2
                echo "   or install uv, then retry." >&2
                exit 1
            fi
        else
            echo "Refreshing Atlas bootstrapper dependencies (declaration changed)..." >&2
        fi
        # The scripts run from this directory, so install only the locked
        # runtime closure, not the bootstrapper itself (no build-backend fetch).
        if "$VENV_PYTHON" -m pip install --disable-pip-version-check --upgrade --constraint "$BOOTSTRAPPER_LOCK" --requirement "$BOOTSTRAPPER_LOCK"; then
            # Stamp only AFTER a successful install, so a failed run retries.
            printf '%s\n' "$WANT_STAMP" > "$DEPS_STAMP" 2>/dev/null || true
        else
            echo "❌ Could not install Atlas bootstrapper dependencies from the locked graph." >&2
            echo "   Install uv, or run: $VENV_PYTHON -m ensurepip --upgrade" >&2
            exit 1
        fi
    fi

    exec "$VENV_PYTHON" "$SELF_DIR/$SCRIPT_REL" "$@"
else
    echo "❌ Neither 'uv' nor 'python3' was found on PATH." >&2
    echo "" >&2
    echo "  Install one of:" >&2
    echo "    • uv (recommended):  https://github.com/astral-sh/uv" >&2
    echo "    • Python 3.11+:      https://www.python.org/downloads/" >&2
    echo "" >&2
    echo "  Then re-run the script that invoked this dispatcher." >&2
    exit 127
fi