79 lines
3.0 KiB
Python
79 lines
3.0 KiB
Python
"""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())
|