874b6f20c2
Основа — Horizon (MIT). История апстрима не переносится: этот репозиторий ведёт свою.
70 lines
2.9 KiB
Python
70 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Reject local game artifacts even when somebody uses git add -f."""
|
|
|
|
import argparse
|
|
from pathlib import PurePosixPath
|
|
import subprocess
|
|
import sys
|
|
|
|
PRIVATE_ROOTS = {".local", "decompiled", "minecraft", "assets", "run", "logs"}
|
|
PRIVATE_EXTENSIONS = {".jar", ".class", ".jfr", ".hprof", ".java"}
|
|
ORIGINAL_JDK_TOOLS = {"tools/JfrSummary.java", "tests/java/JfrFixture.java"}
|
|
|
|
|
|
def prohibited(path):
|
|
parsed = PurePosixPath(path)
|
|
# Original JDK-only tools are an exact allowlist; recovered game Java stays private.
|
|
if path in ORIGINAL_JDK_TOOLS:
|
|
return False
|
|
# Original Horizon mod sources may also be tracked.
|
|
if parsed.suffix == ".java" and path.startswith(("mods/horizon/src/", "mods/horizon-lab/src/")):
|
|
return not ("/dev/horizonlab/" in path)
|
|
return bool(parsed.parts and parsed.parts[0] in PRIVATE_ROOTS) or parsed.suffix.lower() in PRIVATE_EXTENSIONS
|
|
|
|
|
|
def paths_in_repository(history=False, revisions=None):
|
|
if not history:
|
|
result = subprocess.check_output(["git", "ls-files", "-z"])
|
|
return result.decode("utf-8").split("\0")
|
|
# An empty push (including deletions only) must not fall back to HEAD or --all.
|
|
if revisions is not None and not revisions:
|
|
return []
|
|
roots = ["--all"] if revisions is None else revisions
|
|
# Include diffs against merge parents so merge-resolution-only artifacts are caught.
|
|
# Git pushes the stored objects, not the history presented by local replace refs.
|
|
result = subprocess.check_output(["git", "--no-replace-objects", "log", "--format=", "--name-only",
|
|
"-z", "-m", *roots, "--"])
|
|
return [path.lstrip("\n") for path in result.decode("utf-8").split("\0")]
|
|
|
|
|
|
def pushed_revisions(stream):
|
|
"""Read Git's pre-push ref updates and retain the non-deleted local object IDs."""
|
|
revisions = []
|
|
for line in stream:
|
|
_local_ref, local_oid, _remote_ref, _remote_oid = line.split()
|
|
if local_oid.strip("0"):
|
|
revisions.append(local_oid)
|
|
return revisions
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
mode = parser.add_mutually_exclusive_group()
|
|
mode.add_argument("--history", action="store_true", help="Inspect paths in all local Git history")
|
|
mode.add_argument("--pre-push", action="store_true", help="Inspect history reachable from Git's ref updates on stdin")
|
|
args = parser.parse_args()
|
|
revisions = pushed_revisions(sys.stdin) if args.pre_push else None
|
|
rejected = sorted({path for path in paths_in_repository(args.history or args.pre_push, revisions)
|
|
if path and prohibited(path)})
|
|
if rejected:
|
|
print("Private game/profiling artifacts must stay outside Git:", file=sys.stderr)
|
|
for path in rejected:
|
|
print(f" {path}", file=sys.stderr)
|
|
return 1
|
|
print("Public tree check passed.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|