mirror of
https://github.com/bitsycore/compose-desktop-native.git
synced 2026-10-05 10:47:26 +00:00
137 lines
5.3 KiB
Python
137 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Vendor-drift tripwire (RENDERER.md P0.6 / §8).
|
|
|
|
Manually-vendored files (copy-edited from upstream, NON-IDEMPOTENT) carry a
|
|
machine-readable provenance line near their header:
|
|
|
|
// VENDOR-BASE: <upstream-repo-relative-path> @ <commit-or-tag>
|
|
// VENDOR-BASE(<VARNAME>): <path> @ <ref> for a non-core repo, naming the
|
|
compose.properties pin it tracks
|
|
(e.g. COMPOSE_REF for the umbrella repo)
|
|
|
|
recording the exact upstream ref the file was last reconciled against. This script
|
|
compares each to the CURRENT pin for its repo (COMPOSE_CORE_REF by default) from
|
|
scripts/compose-fork/compose.properties. If they differ, the manual vendor MAY be
|
|
stale - upstream could have changed that file since it was reconciled - and must be
|
|
re-checked by hand. Exits non-zero if any file is stale, so it can gate a ref bump.
|
|
|
|
Deeper (optional) check: if the repo's local clone is found (../cmp-ref[-core] for the
|
|
core repo, ../cmp-ref-compose-multiplatform for COMPOSE_REF), it runs
|
|
`git diff <base>..<pin> -- <path>` and reports whether the upstream file ACTUALLY changed
|
|
between the recorded base and the current pin - turning "might be stale" into
|
|
"did / didn't change".
|
|
|
|
Run at each pin bump (see RENDERER.md §9 ref-bump runbook).
|
|
No third-party deps; Windows / macOS / Linux.
|
|
"""
|
|
import re, sys, subprocess
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parents[2]
|
|
PROPS = REPO / "scripts" / "compose-fork" / "compose.properties"
|
|
MARKER = re.compile(r"VENDOR-BASE(?:\(([A-Z_][A-Z0-9_]*)\))?:\s*(\S+)\s*@\s*(\S+)")
|
|
|
|
# Pin variable -> candidate clone dir names (sibling of the repo checkout).
|
|
CLONES = {
|
|
"COMPOSE_CORE_REF": ("cmp-ref", "cmp-ref-core"),
|
|
"COMPOSE_REF": ("cmp-ref-compose-multiplatform",),
|
|
}
|
|
|
|
|
|
def read_pins() -> dict:
|
|
pins = {}
|
|
for line in PROPS.read_text(encoding="utf-8").splitlines():
|
|
s = line.strip()
|
|
if s and not s.startswith("#") and "=" in s:
|
|
name, val = s.split("=", 1)
|
|
pins[name.strip()] = val.strip()
|
|
if "COMPOSE_CORE_REF" not in pins:
|
|
raise SystemExit("check-vendor-drift: COMPOSE_CORE_REF not found in compose.properties")
|
|
return pins
|
|
|
|
|
|
def find_clone(var: str):
|
|
for name in CLONES.get(var, ()):
|
|
p = REPO.parent / name
|
|
if (p / ".git").exists():
|
|
return p
|
|
return None
|
|
|
|
|
|
def upstream_changed(clone: Path, path: str, base: str, pin: str):
|
|
"""True if <path> differs between base..pin in the clone; None if it couldn't run
|
|
(including the path not existing at the pin - a rename/move needs human eyes)."""
|
|
try:
|
|
present = subprocess.run(
|
|
["git", "-C", str(clone), "ls-tree", "--name-only", pin, "--", path],
|
|
capture_output=True, text=True,
|
|
)
|
|
if present.returncode != 0 or not present.stdout.strip():
|
|
return None # ref missing locally or path gone at the pin
|
|
r = subprocess.run(
|
|
["git", "-C", str(clone), "diff", "--quiet", f"{base}..{pin}", "--", path],
|
|
capture_output=True,
|
|
)
|
|
return r.returncode != 0 # `git diff --quiet` exits 1 when there ARE differences
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def main() -> int:
|
|
pins = read_pins()
|
|
|
|
entries = []
|
|
for f in REPO.rglob("*.kt"):
|
|
if "/src/vendor/" in f.as_posix():
|
|
continue # gitignored verbatim re-sync tree - not manual vendors
|
|
try:
|
|
text = f.read_text(encoding="utf-8", errors="ignore")
|
|
except Exception:
|
|
continue
|
|
for m in MARKER.finditer(text):
|
|
var = m.group(1) or "COMPOSE_CORE_REF"
|
|
entries.append((f, var, m.group(2), m.group(3)))
|
|
|
|
if not entries:
|
|
print("check-vendor-drift: no VENDOR-BASE annotations found.")
|
|
return 0
|
|
|
|
used_vars = sorted({var for _, var, _, _ in entries})
|
|
for var in used_vars:
|
|
print(f"check-vendor-drift: pin {var} = {pins.get(var, '(NOT IN compose.properties!)')}"
|
|
f" clone = {find_clone(var) or '(none - ref-compare only)'}")
|
|
print()
|
|
|
|
stale = []
|
|
for f, var, path, base in sorted(entries, key=lambda e: e[0].as_posix()):
|
|
rel = f.relative_to(REPO).as_posix()
|
|
pin = pins.get(var)
|
|
if pin is None:
|
|
stale.append(rel)
|
|
print(f" STALE {rel}\n tracks unknown pin variable {var!r}")
|
|
continue
|
|
if base == pin:
|
|
print(f" OK {rel} (base == pin {var})")
|
|
continue
|
|
note = ""
|
|
clone = find_clone(var)
|
|
if clone:
|
|
ch = upstream_changed(clone, path, base, pin)
|
|
note = (" <-- upstream CHANGED base..pin; RECONCILE" if ch is True
|
|
else " (upstream unchanged base..pin; safe to bump the VENDOR-BASE ref)"
|
|
if ch is False else " (clone diff unavailable)")
|
|
stale.append(rel)
|
|
print(f" STALE {rel}\n base @ {base}\n pin @ {pin} ({var}){note}")
|
|
|
|
if stale:
|
|
print(f"\ncheck-vendor-drift: {len(stale)} stale manual-vendor(s). Reconcile each against "
|
|
f"its pinned upstream, then update its VENDOR-BASE ref.")
|
|
return 1
|
|
print("\ncheck-vendor-drift: all manual vendors match their pins. OK.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|