-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathcheck_updates.py
More file actions
138 lines (108 loc) · 3.95 KB
/
check_updates.py
File metadata and controls
138 lines (108 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env python3
"""
Fetch and format changelog entries between two versions of a package.
Fetches CHANGELOG.md from the package's GitHub repo and extracts
entries between the old and new version, formatted as markdown.
Usage:
python check_updates.py --package policyengine-us \
--old-version 1.596.5 --new-version 1.604.1
"""
import argparse
import re
import sys
import requests
# Map package names to GitHub repos
REPO_MAP = {
"policyengine-us": "PolicyEngine/policyengine-us",
"policyengine-uk": "PolicyEngine/policyengine-uk",
}
def fetch_changelog(package):
"""Fetch CHANGELOG.md from the package's GitHub repo."""
repo = REPO_MAP.get(package)
if not repo:
return None
for branch in ("main", "master"):
url = f"https://raw.githubusercontent.com/{repo}/{branch}/CHANGELOG.md"
resp = requests.get(url, timeout=30)
if resp.status_code == 200:
return resp.text
return None
def parse_version(version_str):
"""Parse a version string into a comparable tuple."""
return tuple(map(int, version_str.split(".")))
def parse_changelog(text):
"""Parse Keep-a-Changelog markdown into structured entries."""
if not text:
return []
entries = []
current_entry = None
current_category = None
for line in text.splitlines():
version_match = re.match(r"^##\s+\[(\d+\.\d+\.\d+)\]", line)
if version_match:
current_entry = {
"version": version_match.group(1),
"changes": {},
}
entries.append(current_entry)
current_category = None
continue
if current_entry is None:
continue
category_match = re.match(r"^###\s+(\w+)", line)
if category_match:
current_category = category_match.group(1).lower()
continue
item_match = re.match(r"^-\s+(.+)", line)
if item_match and current_category:
current_entry["changes"].setdefault(current_category, [])
current_entry["changes"][current_category].append(item_match.group(1))
return entries
def get_changes_between(changelog, old_version, new_version):
"""Extract changelog entries between two versions."""
old_v = parse_version(old_version)
new_v = parse_version(new_version)
return [
e
for e in changelog
if "version" in e and old_v < parse_version(e["version"]) <= new_v
]
def format_changes(entries):
"""Format changelog entries as markdown sections."""
buckets = {"added": [], "changed": [], "fixed": [], "removed": []}
for entry in entries:
for cat, items in entry.get("changes", {}).items():
if cat in buckets:
buckets[cat].extend(items)
sections = []
for cat, items in buckets.items():
if items:
sections.append(
f"### {cat.capitalize()}\n" + "\n".join(f"- {item}" for item in items)
)
return "\n\n".join(sections) if sections else ""
def main():
parser = argparse.ArgumentParser(
description="Fetch changelog entries between two package versions."
)
parser.add_argument(
"--package",
required=True,
help="Package name (e.g., policyengine-us)",
)
parser.add_argument("--old-version", required=True, help="Current pinned version")
parser.add_argument("--new-version", required=True, help="New version from PyPI")
args = parser.parse_args()
changelog_text = fetch_changelog(args.package)
if not changelog_text:
print(f"Could not fetch changelog for {args.package}.", file=sys.stderr)
return 0
entries = parse_changelog(changelog_text)
changes = get_changes_between(entries, args.old_version, args.new_version)
if changes:
print(format_changes(changes))
else:
print("No changelog entries found between these versions.")
return 0
if __name__ == "__main__":
sys.exit(main())