Skip to content

Commit 205ac19

Browse files
authored
feat: add QPK_PIN dependency consistency mechanism
feat: add QPK_PIN dependency consistency mechanism
2 parents 5b76090 + b0dde89 commit 205ac19

3 files changed

Lines changed: 148 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Update QPK Pin
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths-ignore:
7+
- "QPK_PIN"
8+
- "docs/**"
9+
- "**.md"
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
update-pin:
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 5
18+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
19+
steps:
20+
- uses: actions/checkout@v6
21+
22+
- name: Update QPK_PIN
23+
run: |
24+
SHA=$(git rev-parse HEAD)
25+
echo "$SHA" > QPK_PIN
26+
27+
- name: Commit and push updated pin
28+
run: |
29+
git config user.name "github-actions[bot]"
30+
git config user.email "github-actions[bot]@users.noreply.github.com"
31+
git add QPK_PIN
32+
if git diff --cached --quiet; then
33+
echo "QPK_PIN already up to date"
34+
else
35+
git commit -m "chore: update QPK_PIN to $SHA"
36+
git push
37+
fi

QPK_PIN

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
5b76090a358073f00b1705409fff0902bd5f0ca4
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
"""Verify all dependent repos reference the same QPK SHA as QPK_PIN.
3+
4+
Usage:
5+
python scripts/check_qpk_pin_consistency.py [--fix]
6+
7+
Checks that all git-based dependencies in requirements.txt / pyproject.toml
8+
reference the same QPK commit as recorded in QPK_PIN.
9+
"""
10+
from __future__ import annotations
11+
12+
import os
13+
import re
14+
import subprocess
15+
import sys
16+
from pathlib import Path
17+
18+
QPK_REPO_URL = "https://github.com/QuantStrategyLab/QuantPlatformKit.git"
19+
GIT_SHA_RE = re.compile(r"@([a-f0-9]{40})")
20+
21+
22+
def get_qpk_pin_sha() -> str:
23+
"""Read the canonical QPK SHA from QPK_PIN, falling back to remote main."""
24+
pin_path = Path(__file__).resolve().parent.parent / "QPK_PIN"
25+
if pin_path.exists():
26+
sha = pin_path.read_text().strip().split()[0]
27+
if len(sha) == 40:
28+
return sha
29+
30+
# Fallback: query remote
31+
try:
32+
result = subprocess.run(
33+
["git", "ls-remote", "https://github.com/QuantStrategyLab/QuantPlatformKit.git", "HEAD"],
34+
capture_output=True, text=True, check=True,
35+
)
36+
sha = result.stdout.split()[0]
37+
if len(sha) == 40:
38+
return sha
39+
except subprocess.CalledProcessError:
40+
pass
41+
42+
raise RuntimeError("Cannot resolve QPK pin SHA from QPK_PIN or git remote")
43+
44+
45+
def find_dep_files() -> list[Path]:
46+
"""Find all files that might contain QPK dependency pins."""
47+
files: list[Path] = []
48+
cwd = Path.cwd()
49+
for pattern in ("**/requirements*.txt", "**/pyproject.toml"):
50+
for path in cwd.glob(pattern):
51+
if "external" not in str(path):
52+
files.append(path)
53+
return sorted(set(files))
54+
55+
56+
def extract_qpk_shas(path: Path) -> list[tuple[int, str, str]]:
57+
"""Yield (line_num, raw_match, sha) for each QPK git reference in file."""
58+
results: list[tuple[int, str, str]] = []
59+
for i, line in enumerate(path.read_text().splitlines(), 1):
60+
if "QuantPlatformKit" not in line:
61+
continue
62+
matches = GIT_SHA_RE.findall(line)
63+
for sha in matches:
64+
results.append((i, line.strip(), sha))
65+
return results
66+
67+
68+
def main() -> int:
69+
fix_mode = "--fix" in sys.argv
70+
target_sha = get_qpk_pin_sha()
71+
target_short = target_sha[:12]
72+
print(f"Target QPK SHA: {target_short}...")
73+
74+
errors: list[str] = []
75+
files_checked = 0
76+
mismatches = 0
77+
78+
for path in find_dep_files():
79+
refs = extract_qpk_shas(path)
80+
if not refs:
81+
continue
82+
files_checked += 1
83+
for line_num, raw_line, sha in refs:
84+
if sha != target_sha:
85+
mismatches += 1
86+
msg = (
87+
f"❌ {path}:{line_num} references QPK@{sha[:12]} "
88+
f"(expected {target_short})"
89+
)
90+
errors.append(msg)
91+
print(msg)
92+
if fix_mode:
93+
new_content = path.read_text().replace(sha, target_sha)
94+
path.write_text(new_content)
95+
print(f" → Fixed to {target_short}")
96+
97+
if mismatches == 0:
98+
print(f"✅ All {files_checked} files reference QPK@{target_short}")
99+
return 0
100+
101+
total_refs = sum(1 for p in find_dep_files() for _ in extract_qpk_shas(p))
102+
print(f"\n{mismatches}/{total_refs} mismatches in {files_checked} files")
103+
if fix_mode:
104+
print("Fixed. Please commit the changes.")
105+
return 0
106+
return 1
107+
108+
109+
if __name__ == "__main__":
110+
raise SystemExit(main())

0 commit comments

Comments
 (0)