From d6a5569d60cb0568a263109ee577f2566a3b1605 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Tue, 30 Jun 2026 22:43:47 +0800 Subject: [PATCH] feat: add QPK_PIN consistency check to CI --- .github/workflows/ci.yml | 4 +++ scripts/check_qpk_pin_consistency.py | 42 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 scripts/check_qpk_pin_consistency.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5463bc7..997715f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,10 @@ jobs: python -m pip install --no-deps -e external/QuantPlatformKit -e external/UsEquityStrategies - name: Run ruff + + - name: Check QPK pin consistency + run: python scripts/check_qpk_pin_consistency.py + continue-on-error: true run: | set -euo pipefail ruff check . diff --git a/scripts/check_qpk_pin_consistency.py b/scripts/check_qpk_pin_consistency.py new file mode 100644 index 0000000..ad435c6 --- /dev/null +++ b/scripts/check_qpk_pin_consistency.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +"""Check that all QPK git references match the canonical QPK_PIN. +Usage: python scripts/check_qpk_pin_consistency.py [--fix] +""" +import re, subprocess, sys +from pathlib import Path + +QPK_PIN_URL = "https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/QPK_PIN" +SHA_RE = re.compile(r"@([a-f0-9]{40})") + +def fetch_pin() -> str: + import urllib.request + with urllib.request.urlopen(QPK_PIN_URL, timeout=10) as r: + sha = r.read().decode().strip().split()[0] + if len(sha) == 40: return sha + raise RuntimeError(f"Invalid QPK_PIN: {sha}") + +def main(): + fix = "--fix" in sys.argv + target = fetch_pin() + print(f"QPK_PIN: {target[:12]}...") + errors = 0 + for path in sorted(Path.cwd().glob("**/requirements*.txt")) + sorted(Path.cwd().glob("**/pyproject.toml")): + if "external" in str(path): continue + content = path.read_text() + for m in SHA_RE.finditer(content): + sha = m.group(1) + if "QuantPlatformKit" not in content[max(0,m.start()-200):m.end()]: continue + if sha != target: + errors += 1 + print(f" ❌ {path}: QPK@{sha[:12]} (expected {target[:12]})") + if fix: + path.write_text(content.replace(sha, target)) + print(f" → fixed") + if errors: + print(f"\n{errors} mismatch(es). Run with --fix to auto-fix.") + return 1 + print("✅ All QPK pins match.") + return 0 + +if __name__ == "__main__": + raise SystemExit(main())