"""Wire staged bionic inputs to PM without exposing its installer executable.""" from __future__ import annotations import argparse import os from pathlib import Path import subprocess import sys if __package__ in (None, ""): sys.path.insert(0, str(Path(__file__).resolve().parents[2])) def prepare_tools(source: Path, root: Path) -> None: from pm.paths import lockfile_path from scripts.bundles.payload import record_tools store = root / "tools" store.mkdir(parents=True, exist_ok=True) for name in ("python", "uv"): entry = store / name if entry.absolute() != (source / name).absolute(): entry.symlink_to((source / name).resolve(), target_is_directory=True) record_tools(root, lockfile_path(), "linux-arm64-bionic", {name: name for name in ("python", "uv")}) def application(root: Path, requirements: Path, python: Path) -> None: from pm import build_requirements_environment build_requirements_environment( requirements.read_text(encoding="utf-8-sig").splitlines(), out=root / "venv", python=python, wheelhouse=root / "wheelhouse", offline=True, sealed=True, explicit=True, ) def assemble(root: Path, requirements: Path, python: Path) -> None: from pm import stage_manager_runtime from scripts.bundles.payload import seal_pm_runtime prepare_tools(root / "tools", root) manager = stage_manager_runtime( python=python, destination=root / "pm-runtime", project=root / "app/pm", wheelhouse=root / "wheelhouse", offline=True, ) # The staged manager is complete but not published. Execute the application # build in it before sealing removes its build-time executable links. subprocess.run( [str(manager), "-I", "-B", str(Path(__file__).resolve()), "application", "--root", str(root), "--requirements", str(requirements), "--python", str(python)], check=True, ) seal_pm_runtime(root, python) def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("phase", choices=["prepare-tools", "assemble", "application"]) parser.add_argument("--root", type=Path, required=True) parser.add_argument("--source-tools", type=Path) parser.add_argument("--requirements", type=Path) parser.add_argument("--python", type=Path) args = parser.parse_args() root = args.root.resolve() os.environ["HERMES_RUNTIME_DIR"] = str(root / "tools") if args.phase == "prepare-tools": if args.source_tools is None: parser.error("prepare-tools requires --source-tools") prepare_tools(args.source_tools, root) else: if args.requirements is None or args.python is None: parser.error("environment assembly requires --requirements and --python") operation = assemble if args.phase == "assemble" else application operation(root, args.requirements.resolve(), args.python.absolute()) return 0 if __name__ == "__main__": raise SystemExit(main())