4jcraft/scripts/extract_4jlibs.py
2026-03-20 11:32:15 +11:00

131 lines
3.3 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import site
import shutil
import subprocess
import sys
from pathlib import Path
PATHS_TO_KEEP = [
"4J.Common",
"4J.Input",
"4J.Profile",
"4J.Render",
"4J.Storage",
"4jlibs",
]
def run(cmd: list[str], cwd: Path | None = None) -> None:
subprocess.run(cmd, cwd=cwd, check=True)
def require_clean_destination(dest: Path) -> None:
if dest.exists():
raise SystemExit(f"Destination already exists: {dest}")
def resolve_filter_repo_command() -> list[str]:
script_name = "git-filter-repo.exe" if sys.platform == "win32" else "git-filter-repo"
path_candidates = [
shutil.which("git-filter-repo"),
shutil.which(script_name),
]
user_script_dirs = [
Path(site.USER_BASE) / "Scripts",
Path(site.getusersitepackages()).parent / "Scripts",
]
for script_dir in user_script_dirs:
user_scripts = script_dir / script_name
path_candidates.append(str(user_scripts) if user_scripts.exists() else None)
for candidate in path_candidates:
if candidate:
return [candidate]
raise SystemExit(
"git-filter-repo is required. Install it first and rerun this script."
)
def clone_source(source: Path, dest: Path) -> None:
run(["git", "clone", "--no-local", str(source), str(dest)])
def filter_history(dest: Path) -> None:
cmd = resolve_filter_repo_command() + ["--force"]
for path in PATHS_TO_KEEP:
cmd.extend(["--path", path])
run(cmd, cwd=dest)
def rewrite_staged_layout(dest: Path) -> None:
staged_root = dest / "4jlibs"
meson_src = staged_root / "meson.build"
builddef_src = staged_root / "builddef"
if not meson_src.exists() or not builddef_src.exists():
raise SystemExit("Filtered tree is missing the staged 4jlibs Meson files.")
shutil.move(str(meson_src), str(dest / "meson.build"))
shutil.move(str(builddef_src), str(dest / "builddef"))
try:
staged_root.rmdir()
except OSError:
pass
builddef_meson = dest / "builddef" / "meson.build"
builddef_smoke = dest / "builddef" / "header_smoke.cpp"
builddef_meson.write_text(
builddef_meson.read_text(encoding="utf-8").replace("../../", "../"),
encoding="utf-8",
)
builddef_smoke.write_text(
builddef_smoke.read_text(encoding="utf-8").replace("../../", "../"),
encoding="utf-8",
)
def main() -> int:
parser = argparse.ArgumentParser(
description=(
"Create a filtered 4jlibs bootstrap repo from the current 4jcraft tree."
)
)
parser.add_argument(
"destination",
type=Path,
help="Path to create the filtered 4jlibs clone in",
)
parser.add_argument(
"--source",
type=Path,
default=Path.cwd(),
help="Source 4jcraft repo to clone from (default: current working directory)",
)
args = parser.parse_args()
source = args.source.resolve()
destination = args.destination.resolve()
require_clean_destination(destination)
clone_source(source, destination)
filter_history(destination)
rewrite_staged_layout(destination)
print(f"Created filtered 4jlibs bootstrap repo at: {destination}")
return 0
if __name__ == "__main__":
sys.exit(main())