Rename Magisk branding to PronBox (author: TechnoPron team), stop customize.sh from forcing Box for Magisk, and ship wg-obfuscator plus Manage Profiles templates. Co-authored-by: Cursor <cursoragent@cursor.com>
94 lines
2.8 KiB
Python
94 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Build Magisk ZIP with LF scripts (Android unzip-safe)."""
|
|
from __future__ import annotations
|
|
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
EXCLUDE_DIRS = {
|
|
".git",
|
|
".github",
|
|
"docs",
|
|
"tools",
|
|
"phone_configs",
|
|
"__pycache__",
|
|
}
|
|
EXCLUDE_FILES = {
|
|
"CHANGELOG.md",
|
|
"update.json",
|
|
"build.sh",
|
|
".gitignore",
|
|
"RELEASE.md",
|
|
}
|
|
EXCLUDE_GLOBS_SUFFIX = (".zip", ".pyc", ".lf", ".tar.gz", ".bak")
|
|
|
|
|
|
def version() -> str:
|
|
for line in (ROOT / "module.prop").read_text(encoding="utf-8").splitlines():
|
|
if line.startswith("version="):
|
|
return line.split("=", 1)[1].strip()
|
|
raise SystemExit("version= missing in module.prop")
|
|
|
|
|
|
def should_skip(rel: Path) -> bool:
|
|
parts = rel.parts
|
|
if any(p in EXCLUDE_DIRS for p in parts):
|
|
return True
|
|
if any(p.startswith("phone_") for p in parts):
|
|
return True
|
|
if rel.name in EXCLUDE_FILES:
|
|
return True
|
|
if rel.name.endswith(EXCLUDE_GLOBS_SUFFIX):
|
|
return True
|
|
return False
|
|
|
|
|
|
def normalize_bytes(path: Path, data: bytes) -> bytes:
|
|
# Force LF for shell/scripts installed on Android
|
|
text_ext = {".sh", ".ini", ".cfg", ".prop", ".md", ".json", ".yaml", ".yml", ".toml", ".example"}
|
|
name = path.name
|
|
if (
|
|
path.suffix.lower() in text_ext
|
|
or name in {"sbfr", "box.stealth", "box.sidecar", "box.service", "box.iptables", "box.tool", "box.profile", "customize.sh", "uninstall.sh", "box_service.sh", "service.sh", "post-fs-data.sh"}
|
|
or name.startswith("box.")
|
|
):
|
|
try:
|
|
text = data.decode("utf-8")
|
|
except UnicodeDecodeError:
|
|
return data
|
|
return text.replace("\r\n", "\n").replace("\r", "\n").encode("utf-8")
|
|
return data
|
|
|
|
|
|
def main() -> None:
|
|
ver = version()
|
|
out = ROOT / f"pronbox-{ver}.zip"
|
|
if out.exists():
|
|
out.unlink()
|
|
count = 0
|
|
with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
for path in sorted(ROOT.rglob("*")):
|
|
if not path.is_file():
|
|
continue
|
|
rel = path.relative_to(ROOT)
|
|
if should_skip(rel):
|
|
continue
|
|
data = normalize_bytes(path, path.read_bytes())
|
|
# Unix paths inside zip
|
|
arc = rel.as_posix()
|
|
info = zipfile.ZipInfo(arc)
|
|
info.compress_type = zipfile.ZIP_DEFLATED
|
|
info.external_attr = 0o755 << 16 if (
|
|
path.parent.name == "scripts"
|
|
or path.name in {"sbfr", "customize.sh", "uninstall.sh", "box_service.sh", "service.sh"}
|
|
or path.suffix == ".sh"
|
|
) else (0o644 << 16)
|
|
zf.writestr(info, data)
|
|
count += 1
|
|
print(f"Wrote {out.name} ({count} files, {out.stat().st_size} bytes)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|