check_copies.py
Python, 63 lines from UI overview
#!/usr/bin/env python3
"""check_copies.py: fail when copies of the same runtime module have drifted apart.
The bug it prevents: the UI runtime module lived in three places (live StarterGui copy, the
Builder's private copy in ServerStorage, the source tree). Fixes went into one; the Builder's copy
fell 430 lines behind, and the next rebuild would have installed it and crashed the HUD on init.
Give it a manifest of groups. Each group lists files that must be byte-identical (after normalising
line endings and trailing whitespace). Paths are relative to the manifest. Export Studio copies to
disk first (e.g. a snapshot folder) so they can be compared here.
copies.json:
{
"ui-runtime": ["src/ui/client/UI.luau", "snapshot/ServerStorage/UIBuild/Client/UI.luau",
"snapshot/StarterGui/MainUI/UI.luau"],
"controller": ["src/ui/client/Controller.client.luau", "snapshot/ServerStorage/UIBuild/Client/Controller.luau"]
}
Usage: python3 check_copies.py copies.json (exit 1 on any drift; run before and after every push)
"""
import hashlib
import json
import os
import sys
def norm_hash(path):
text = open(path, encoding="utf-8").read().replace("\r\n", "\n")
text = "\n".join(line.rstrip() for line in text.split("\n")).strip() + "\n"
return hashlib.sha256(text.encode()).hexdigest()[:12], text.count("\n")
def main(manifest):
base = os.path.dirname(os.path.abspath(manifest))
groups = json.load(open(manifest))
bad = 0
for name, files in groups.items():
seen = {}
for rel in files:
p = os.path.join(base, rel)
if not os.path.exists(p):
print(f"[{name}] MISSING {rel}")
bad += 1
continue
h, lines = norm_hash(p)
seen.setdefault(h, []).append((rel, lines))
if len(seen) > 1:
bad += 1
print(f"[{name}] DRIFT: {len(seen)} different versions")
for h, items in seen.items():
for rel, lines in items:
print(f" {h} {lines:5d} lines {rel}")
print(" -> copy the canonical file (source tree) over the others, then re-push.")
else:
print(f"[{name}] ok ({len(files)} copies)")
sys.exit(1 if bad else 0)
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit(__doc__)
main(sys.argv[1])