|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Check that all QPK git references match the canonical QPK_PIN. |
| 3 | +Usage: python scripts/check_qpk_pin_consistency.py [--fix] |
| 4 | +""" |
| 5 | +import re, subprocess, sys |
| 6 | +from pathlib import Path |
| 7 | + |
| 8 | +QPK_PIN_URL = "https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/QPK_PIN" |
| 9 | +SHA_RE = re.compile(r"@([a-f0-9]{40})") |
| 10 | + |
| 11 | +def fetch_pin() -> str: |
| 12 | + import urllib.request |
| 13 | + with urllib.request.urlopen(QPK_PIN_URL, timeout=10) as r: |
| 14 | + sha = r.read().decode().strip().split()[0] |
| 15 | + if len(sha) == 40: return sha |
| 16 | + raise RuntimeError(f"Invalid QPK_PIN: {sha}") |
| 17 | + |
| 18 | +def main(): |
| 19 | + fix = "--fix" in sys.argv |
| 20 | + target = fetch_pin() |
| 21 | + print(f"QPK_PIN: {target[:12]}...") |
| 22 | + errors = 0 |
| 23 | + for path in sorted(Path.cwd().glob("**/requirements*.txt")) + sorted(Path.cwd().glob("**/pyproject.toml")): |
| 24 | + if "external" in str(path): continue |
| 25 | + content = path.read_text() |
| 26 | + for m in SHA_RE.finditer(content): |
| 27 | + sha = m.group(1) |
| 28 | + if "QuantPlatformKit" not in content[max(0,m.start()-200):m.end()]: continue |
| 29 | + if sha != target: |
| 30 | + errors += 1 |
| 31 | + print(f" ❌ {path}: QPK@{sha[:12]} (expected {target[:12]})") |
| 32 | + if fix: |
| 33 | + path.write_text(content.replace(sha, target)) |
| 34 | + print(f" → fixed") |
| 35 | + if errors: |
| 36 | + print(f"\n{errors} mismatch(es). Run with --fix to auto-fix.") |
| 37 | + return 1 |
| 38 | + print("✅ All QPK pins match.") |
| 39 | + return 0 |
| 40 | + |
| 41 | +if __name__ == "__main__": |
| 42 | + raise SystemExit(main()) |
0 commit comments