Skip to content

Commit c6a1eb9

Browse files
author
CalorieApp Maintainer
committed
chore: whitelist tag_release.py for release automation
1 parent d3a8958 commit c6a1eb9

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ scripts/**
292292
!scripts/run.py
293293
!scripts/run.bat
294294
!scripts/run.sh
295+
!scripts/tag_release.py
295296

296297
# Development automation tools (private)
297298
tools/

scripts/tag_release.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""Release Tagging Helper
2+
3+
Automates version bump, changelog scaffold, release note creation.
4+
Usage:
5+
python scripts/tag_release.py --version 0.1.1-testnet --title "visual polish" --preview
6+
python scripts/tag_release.py --version 0.1.1-testnet --title "visual polish" --apply
7+
8+
Requires: clean working tree; run via PR process before tagging.
9+
"""
10+
11+
from __future__ import annotations
12+
import argparse, re, sys, datetime, pathlib
13+
14+
ROOT = pathlib.Path(__file__).resolve().parent.parent
15+
VERSION_FILE = ROOT / "src" / "VERSION.py"
16+
CHANGELOG = ROOT / "CHANGELOG.md"
17+
RELEASE_DIR = ROOT / "docs"
18+
19+
def read_version_py() -> str:
20+
text = VERSION_FILE.read_text(encoding="utf-8")
21+
m = re.search(r'__version__\s*=\s*"([^"]+)"', text)
22+
return m.group(1) if m else "UNKNOWN"
23+
24+
def update_version_py(new_version: str, apply: bool):
25+
text = VERSION_FILE.read_text(encoding="utf-8")
26+
text = re.sub(r'__version__\s*=\s*"[^"]+"', f'__version__ = "{new_version}"', text)
27+
text = re.sub(r'__date__\s*=\s*"[^"]+"', f'__date__ = "{datetime.date.today()}"', text)
28+
if apply:
29+
VERSION_FILE.write_text(text, encoding="utf-8")
30+
return text
31+
32+
def append_changelog(new_version: str, title: str, apply: bool):
33+
entry = (
34+
f"## [{new_version}] - {datetime.date.today()}\n\n"
35+
f"### Pending\n"
36+
f"- Placeholder: {title}\n"
37+
f"- Fill in merged items before tag.\n\n"
38+
)
39+
original = CHANGELOG.read_text(encoding="utf-8")
40+
updated = entry + original
41+
if apply:
42+
CHANGELOG.write_text(updated, encoding="utf-8")
43+
return entry
44+
45+
def create_release_note(new_version: str, title: str, apply: bool):
46+
fname = RELEASE_DIR / f"RELEASE_NOTE_{new_version.replace('.', '_').upper()}.md"
47+
content = (
48+
f"# Release {new_version}\n\n"
49+
f"## Draft - {title}\n\n"
50+
"Populate after merging PRs.\n\n"
51+
"## Checklist\n"
52+
"- [ ] Changelog finalized\n"
53+
"- [ ] Version bump committed\n"
54+
"- [ ] Security scan re-run\n"
55+
"- [ ] Feature flags validated\n"
56+
"- [ ] Tag annotated & pushed\n"
57+
)
58+
if apply:
59+
fname.write_text(content, encoding="utf-8")
60+
return fname, content
61+
62+
def main():
63+
p = argparse.ArgumentParser()
64+
p.add_argument("--version", required=True, help="New semantic version (e.g. 0.1.1-testnet)")
65+
p.add_argument("--title", required=True, help="Short release focus description")
66+
p.add_argument("--apply", action="store_true", help="Write changes to disk")
67+
p.add_argument("--preview", action="store_true", help="Preview without writing")
68+
args = p.parse_args()
69+
70+
current = read_version_py()
71+
if current == args.version:
72+
print(f"Version already at {current}; no bump performed.")
73+
sys.exit(0)
74+
75+
updated_version_py = update_version_py(args.version, apply=args.apply)
76+
changelog_entry = append_changelog(args.version, args.title, apply=args.apply)
77+
rn_path, rn_content = create_release_note(args.version, args.title, apply=args.apply)
78+
79+
print("--- Version.py Updated ---")
80+
print(updated_version_py.splitlines()[0:8])
81+
print("--- Changelog Entry ---")
82+
print(changelog_entry)
83+
print("--- Release Note Path ---")
84+
print(rn_path)
85+
if not args.apply and not args.preview:
86+
print("(Run with --preview or --apply)")
87+
88+
if __name__ == "__main__":
89+
main()

0 commit comments

Comments
 (0)