Skip to content

Commit dca44e5

Browse files
committed
Add a script for bumping the repo version number
1 parent 09f5f14 commit dca44e5

1 file changed

Lines changed: 149 additions & 0 deletions

File tree

update_version.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#! /usr/bin/env python
2+
import argparse
3+
import re
4+
import sys
5+
from datetime import date
6+
from pathlib import Path
7+
8+
9+
def parse_args():
10+
parser = argparse.ArgumentParser(
11+
description=(
12+
"Bump version across files (CITATION.cff, __init__.py, meta.yaml)."
13+
)
14+
)
15+
parser.add_argument(
16+
"-v",
17+
"--version",
18+
required=True,
19+
help="New version in the form X.Y.Z (e.g., 1.3.0)",
20+
)
21+
return parser.parse_args()
22+
23+
24+
def ensure_semver(version: str):
25+
m = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)", version)
26+
if not m:
27+
sys.exit("Error: version must be in the form X.Y.Z (e.g., 1.3.0).")
28+
return tuple(int(p) for p in m.groups())
29+
30+
31+
def read_text(path: Path):
32+
try:
33+
return path.read_text(encoding="utf-8")
34+
except FileNotFoundError:
35+
return None
36+
37+
38+
def write_text_if_changed(path: Path, content: str) -> bool:
39+
old = read_text(path)
40+
if old is None:
41+
return False
42+
if old != content:
43+
path.write_text(content, encoding="utf-8")
44+
return True
45+
return False
46+
47+
48+
def bump_citation(root: Path, version: str, today: str) -> bool:
49+
path = root / "CITATION.cff"
50+
content = read_text(path)
51+
if content is None:
52+
print(f"Skip (not found): {path}")
53+
return False
54+
55+
# version: 1.2.1 -> version: 1.2.2
56+
content_new = re.sub(
57+
r"(?m)^(version:\s*)(.+)\s*$", rf"\g<1>{version}", content, count=1
58+
)
59+
# date-released: '2025-06-12' -> date-released: 'YYYY-MM-DD'
60+
content_new = re.sub(
61+
r"""(?m)^(date-released:\s*)['"]?\d{4}-\d{2}-\d{2}['"]?\s*$""",
62+
rf"\g<1>'{today}'",
63+
content_new,
64+
count=1,
65+
)
66+
# Ensure a final newline at EOF
67+
if not content_new.endswith("\n"):
68+
content_new += "\n"
69+
70+
changed = write_text_if_changed(path, content_new)
71+
if changed:
72+
print(f"Updated: {path}")
73+
else:
74+
print(f"No changes: {path}")
75+
return changed
76+
77+
78+
def bump_init(pkg_dir: Path, version_tuple) -> bool:
79+
path = pkg_dir / "mpas_tools" / "__init__.py"
80+
content = read_text(path)
81+
if content is None:
82+
print(f"Skip (not found): {path}")
83+
return False
84+
85+
major, minor, patch = version_tuple
86+
content_new = re.sub(
87+
r"(?m)^__version_info__\s*=\s*\(.+\)\s*$",
88+
f"__version_info__ = ({major}, {minor}, {patch})",
89+
content,
90+
count=1,
91+
)
92+
93+
changed = write_text_if_changed(path, content_new)
94+
if changed:
95+
print(f"Updated: {path}")
96+
else:
97+
print(f"No changes: {path}")
98+
return changed
99+
100+
101+
def bump_meta(recipe_dir: Path, version: str) -> bool:
102+
path = recipe_dir / "meta.yaml"
103+
content = read_text(path)
104+
if content is None:
105+
print(f"Skip (not found): {path}")
106+
return False
107+
108+
# {% set version = "1.2.1" %} -> {% set version = "1.2.2" %}
109+
content_new = re.sub(
110+
r'(?m)^\{\%\s*set\s+version\s*=\s*["\']([^"\']+)["\']\s*\%\}',
111+
f'{{% set version = "{version}" %}}',
112+
content,
113+
count=1,
114+
)
115+
116+
changed = write_text_if_changed(path, content_new)
117+
if changed:
118+
print(f"Updated: {path}")
119+
else:
120+
print(f"No changes: {path}")
121+
return changed
122+
123+
124+
def main():
125+
args = parse_args()
126+
version = args.version.strip()
127+
version_tuple = ensure_semver(version)
128+
today = date.today().isoformat()
129+
130+
# Resolve repo layout relative to this script
131+
script_dir = Path(__file__).resolve().parent
132+
repo_root = script_dir
133+
pkg_dir = script_dir / "conda_package"
134+
recipe_dir = pkg_dir / "recipe"
135+
136+
changed = []
137+
changed.append(bump_citation(repo_root, version, today))
138+
changed.append(bump_init(pkg_dir, version_tuple))
139+
changed.append(bump_meta(recipe_dir, version))
140+
141+
if not any(changed):
142+
print("No files modified.")
143+
return 0
144+
print(f"Done. Bumped to {version} (date-released: {today}).")
145+
return 0
146+
147+
148+
if __name__ == "__main__":
149+
raise SystemExit(main())

0 commit comments

Comments
 (0)