Skip to content

Commit 60a723a

Browse files
authored
Create gen_changelog.py
增加执行脚本
1 parent c393442 commit 60a723a

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env python3
2+
"""
3+
If version already exists in changelog.md, exit 0 without modification.
4+
Usage: python .github/scripts/gen_changelog.py v4.10.9
5+
"""
6+
from __future__ import annotations
7+
import sys, json, re, html, urllib.request
8+
from datetime import datetime, timezone, timedelta
9+
from pathlib import Path
10+
11+
API_URL = "https://community.fit2cloud.com/v1/products/dataease/releases"
12+
CHANGELOG = Path('docs/changelog.md')
13+
MARKER = '## 2 更新内容'
14+
TZ = timezone(timedelta(hours=8))
15+
16+
TITLE_MAP = {
17+
'安全漏洞修复': ('Warning', '**安全漏洞修复**', 'fix'),
18+
'新增功能': ('Abstract', '新增功能 :star2:', 'feat'),
19+
'功能优化': ('Abstract', '功能优化 :sunflower:', 'refactor'),
20+
'问题修复': ('Abstract', '问题修复 :palm_tree:', 'fix'),
21+
"What's new": ('Abstract', '新增功能 :star2:', 'feat'),
22+
'Improvements': ('Abstract', '功能优化 :sunflower:', 'refactor'),
23+
'Bug fixes': ('Abstract', '问题修复 :palm_tree:', 'fix'),
24+
}
25+
H2_UL = re.compile(r'<h2><a[^>]*></a>([^<]+)</h2>\n?(<ul>.*?</ul>)', re.S)
26+
# 【新增】用于抓取 h2 后面紧跟的 p 标签 (感谢信息)
27+
# 逻辑:匹配 <h2>...</h2> 后面可能有的换行,然后匹配 <p>...</p>
28+
H2_P_THANKS = re.compile(r'<h2><a[^>]*></a>[^<]*</h2>\n?(<p>.*?</p>)', re.S)
29+
LI = re.compile(r'<li>(.*?)</li>', re.S)
30+
31+
32+
def fetch():
33+
with urllib.request.urlopen(API_URL, timeout=30) as r:
34+
return json.loads(r.read().decode())
35+
36+
37+
def normalize(v: str) -> str:
38+
return v.replace('-lts','')
39+
40+
41+
def find_release(target: str, data):
42+
for rel in data:
43+
if normalize(rel.get('version','')) == target:
44+
return rel
45+
return None
46+
47+
48+
def build_block(rel: dict):
49+
version = normalize(rel['version'])
50+
ts = rel.get('publishTime')
51+
dt = datetime.fromtimestamp(ts/1000, tz=TZ) if ts else datetime.now(tz=TZ)
52+
date_str = f"{dt.year}{dt.month}{dt.day}日"
53+
html_content = rel.get('releaseNoteH', '')
54+
sections = []
55+
56+
for title, ul_html in H2_UL.findall(html_content):
57+
items = LI.findall(ul_html)
58+
cleaned = [html.unescape(re.sub(r'\s+',' ', re.sub(r'<[^>]+>','', it)).strip()) for it in items if it.strip()]
59+
thanks_note = ""
60+
# 简单有效的正则:查找当前 title 对应的 h2 后面的 p
61+
# 构造动态正则: <h2>...Title...</h2> ... <ul>...</ul> ... <p>(内容)</p>
62+
# 由于 ul_html 内容可能很长且有特殊字符,直接用 title 定位最安全
63+
safe_title = re.escape(title.strip())
64+
# 正则解释:匹配包含 title 的 h2,后面任意字符(非贪婪),然后匹配一个 <p>...</p>
65+
p_pattern = re.compile(r'<h2><a[^>]*></a>' + safe_title + r'</h2>.*?<p>(.*?)</p>', re.S)
66+
p_match = p_pattern.search(html_content)
67+
if p_match:
68+
p_content = p_match.group(1)
69+
# 清理 HTML 标签,只留文本
70+
clean_p = html.unescape(re.sub(r'<[^>]+>', '', p_content)).strip()
71+
# 只有当内容包含"感谢"或者标题包含"安全"/"漏洞"时,才采纳
72+
if "感谢" in clean_p and "漏洞" in title:
73+
thanks_note = f"\n {clean_p}"
74+
if not cleaned:
75+
continue
76+
admon, nice, tag = TITLE_MAP.get(title, ('info', title, 'note'))
77+
lines = '\n'.join(f" - {i}" for i in cleaned)
78+
if thanks_note:
79+
if lines:
80+
lines += "\n" + thanks_note
81+
sections.append(f"!!! {admon} \"{nice}\"\n\n{lines}\n")
82+
if not sections:
83+
clean = html.unescape(re.sub(r'<[^>]+>','', html_content)).strip()
84+
if clean:
85+
sections.append(f"!!! info \"发布说明\"\n - note: {clean}\n")
86+
block = '\n'.join([f"### {version}", date_str, ''] + sections)
87+
return block
88+
89+
90+
def main():
91+
if len(sys.argv) < 2:
92+
print('Version arg required, e.g. v2.10.1 or v2.10.1-lts or 2.10.1', file=sys.stderr)
93+
return 1
94+
raw = sys.argv[1].strip()
95+
# Accept forms: v2.10.1 or v2.10.1-lts or 2.10.1
96+
if not raw.startswith('v'):
97+
raw = 'v' + raw
98+
target = normalize(raw) # remove -lts suffix if present
99+
data = fetch()
100+
rel = find_release(target, data)
101+
if not rel:
102+
print(f'Target version {raw} (normalized {target}) not found in API list', file=sys.stderr)
103+
return 1
104+
if not CHANGELOG.exists():
105+
print('Changelog file missing.')
106+
return 1
107+
content = CHANGELOG.read_text(encoding='utf-8')
108+
# 【修复】使用正则严格匹配 Markdown 标题行 (例如: ### v2.10.19)
109+
# ^ 表示行首,#+ 表示一个或多个#,\s* 表示可选空格,re.escape 防止版本号中的点被当作通配符
110+
pattern = re.compile(r'^#+\s*' + re.escape(target) + r'\s*$', re.MULTILINE)
111+
112+
if pattern.search(content):
113+
print(f'⚠️ Version {target} already exists in changelog.md (Detected by regex). Skip.')
114+
return 0
115+
block = build_block(rel)
116+
if MARKER in content:
117+
new_content = content.replace(MARKER, MARKER + '\n\n' + block, 1)
118+
else:
119+
new_content = MARKER + '\n\n' + block + content
120+
CHANGELOG.write_text(new_content, encoding='utf-8')
121+
print('Inserted changelog for', target)
122+
return 0
123+
124+
if __name__ == '__main__':
125+
raise SystemExit(main())

0 commit comments

Comments
 (0)