From 3686bb6b74ace6831d09f161b17e374cf381128e Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 23 Apr 2026 02:28:16 -0500 Subject: [PATCH] feat(fetch_repository_wheels): add script to download bundled wheels for offline-first installs --- scripts/build/fetch_repository_wheels.py | 95 ++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 scripts/build/fetch_repository_wheels.py diff --git a/scripts/build/fetch_repository_wheels.py b/scripts/build/fetch_repository_wheels.py new file mode 100644 index 0000000..4aa65e3 --- /dev/null +++ b/scripts/build/fetch_repository_wheels.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Download repository bundled wheels at build time (offline-first installs). + +Wheels are written to ``meshchatx/public/repository-server-bundled/bundled`` so they +ship with the same artifact layout as the Vite output. At runtime, +:class:`~meshchatx.src.backend.repository_server_manager.RepositoryServerManager` +copies any missing ``*.whl`` files from that directory into each identity's +``repository-server/bundled`` folder (no network required). + +Usage:: + + python3 scripts/build/fetch_repository_wheels.py [--dest DIR] + +Environment:: + + MESHCHATX_SKIP_REPOSITORY_WHEELS_FETCH If ``1``/``true``, exit without downloading. +""" + +from __future__ import annotations + +import argparse +import logging +import os +import sys +from pathlib import Path + +DEFAULT_DEST = ( + Path(__file__).resolve().parent.parent.parent + / "meshchatx" + / "public" + / "repository-server-bundled" + / "bundled" +) + + +def _is_truthy(value: str | None) -> bool: + return value is not None and value.strip().lower() in {"1", "true", "yes", "on"} + + +def main(argv: list[str] | None = None) -> int: + logging.basicConfig(level=logging.INFO, format="%(message)s") + if _is_truthy(os.environ.get("MESHCHATX_SKIP_REPOSITORY_WHEELS_FETCH")): + logging.info( + "Skipping repository wheels fetch (MESHCHATX_SKIP_REPOSITORY_WHEELS_FETCH)." + ) + return 0 + + repo_root = Path(__file__).resolve().parent.parent.parent + rr = str(repo_root) + if rr not in sys.path: + sys.path.insert(0, rr) + + parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) + parser.add_argument( + "--dest", + type=Path, + default=DEFAULT_DEST, + help=f"Output directory (default: {DEFAULT_DEST})", + ) + args = parser.parse_args(argv) + dest: Path = args.dest.resolve() + + from meshchatx.src.backend.repository_server_manager import ( + download_bundled_wheels_to_directory, + ) + + dest.mkdir(parents=True, exist_ok=True) + for old in dest.glob("*.whl"): + try: + old.unlink() + except OSError as e: + logging.warning("Could not remove %s: %s", old, e) + + result = download_bundled_wheels_to_directory(dest, pip=None) + failed = result.get("failed") or {} + ok = result.get("downloaded") or [] + if failed: + for pkg, msg in failed.items(): + logging.error("%s: %s", pkg, msg[:500]) + if ok: + logging.info("Repository wheels OK: %s", ", ".join(ok)) + if not ok and failed: + return 1 + if failed: + logging.warning( + "Repository wheels partial success (%d ok, %d failed).", + len(ok), + len(failed), + ) + return 0 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())