Skip to content

Commit fcf89be

Browse files
committed
feat(script): enhance version bumping script with manual version setting and validation
1 parent b00158a commit fcf89be

File tree

1 file changed

+84
-54
lines changed

1 file changed

+84
-54
lines changed

scripts/bump_version.py

Lines changed: 84 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,68 @@
33
Version bumping script for ModelScope MCP Server releases.
44
55
Usage:
6-
python scripts/bump_version.py patch # 1.2.3 -> 1.2.4
7-
python scripts/bump_version.py minor # 1.2.3 -> 1.3.0
8-
python scripts/bump_version.py major # 1.2.3 -> 2.0.0
6+
python scripts/bump_version.py patch # 1.2.3 -> 1.2.4
7+
python scripts/bump_version.py minor # 1.2.3 -> 1.3.0
8+
python scripts/bump_version.py major # 1.2.3 -> 2.0.0
9+
python scripts/bump_version.py set {version} # PEP 440 format, e.g. 1.2.3a1, 1.2.3.dev1
910
"""
1011

1112
import re
1213
import subprocess
1314
import sys
1415
from pathlib import Path
1516

17+
# Constants
18+
PROJECT_ROOT = Path(__file__).parent.parent
19+
SRC_DIR = PROJECT_ROOT / "src"
20+
VERSION_FILE = SRC_DIR / "modelscope_mcp_server" / "_version.py"
21+
FILES_TO_COMMIT = "src/modelscope_mcp_server/_version.py"
22+
23+
# PEP 440 version pattern
24+
PEP440_PATTERN = r"^(\d+)\.(\d+)\.(\d+)((a|b|rc)\d+|\.dev\d+|\.post\d+)?$"
25+
26+
BUMP_TYPES = ["major", "minor", "patch"]
27+
1628

1729
def get_current_version():
1830
"""Extract current version by importing the version module."""
19-
# Add the src directory to Python path
20-
src_path = Path(__file__).parent.parent / "src"
21-
if str(src_path) not in sys.path:
22-
sys.path.insert(0, str(src_path))
31+
if str(SRC_DIR) not in sys.path:
32+
sys.path.insert(0, str(SRC_DIR))
2333

2434
try:
25-
# Import the version module
2635
from modelscope_mcp_server._version import __version__
2736

2837
return __version__
2938
except ImportError as e:
3039
raise ValueError(f"Could not import version module: {e}")
3140
finally:
32-
# Clean up the sys.path
33-
if str(src_path) in sys.path:
34-
sys.path.remove(str(src_path))
41+
if str(SRC_DIR) in sys.path:
42+
sys.path.remove(str(SRC_DIR))
3543

3644

3745
def parse_version(version_string):
38-
"""Parse version string, handling legacy alpha suffix."""
39-
# Handle legacy alpha suffix by treating it as the base version
40-
if version_string.endswith(".alpha"):
41-
base_version = version_string[:-6] # Remove '.alpha'
42-
try:
43-
major, minor, patch = map(int, base_version.split("."))
44-
return major, minor, patch
45-
except ValueError:
46-
raise ValueError(f"Invalid version format: {version_string}")
47-
48-
# Regular version without any suffix
46+
"""Parse version string, extracting major.minor.patch from PEP 440 format."""
47+
# Extract base version (major.minor.patch) from PEP 440 format
48+
# Examples: 1.2.3 -> (1,2,3), 1.2.3a1 -> (1,2,3), 1.2.3.dev1 -> (1,2,3)
49+
match = re.match(r"^(\d+)\.(\d+)\.(\d+)", version_string)
50+
if not match:
51+
raise ValueError(f"Invalid version format: {version_string}")
52+
4953
try:
50-
major, minor, patch = map(int, version_string.split("."))
54+
major, minor, patch = map(int, match.groups())
5155
return major, minor, patch
5256
except ValueError:
5357
raise ValueError(f"Invalid version format: {version_string}")
5458

5559

60+
def validate_version_format(version_string):
61+
"""Validate that the version string follows PEP 440 format."""
62+
if not re.match(PEP440_PATTERN, version_string):
63+
raise ValueError(
64+
f"Invalid version format (should follow PEP 440): {version_string}"
65+
)
66+
67+
5668
def bump_version(current_version, bump_type):
5769
"""Bump version based on type (major, minor, patch)."""
5870
major, minor, patch = parse_version(current_version)
@@ -68,52 +80,70 @@ def bump_version(current_version, bump_type):
6880

6981

7082
def update_version(new_version):
71-
"""Update version in _version.py."""
72-
version_path = (
73-
Path(__file__).parent.parent / "src" / "modelscope_mcp_server" / "_version.py"
74-
)
75-
content = version_path.read_text()
76-
77-
# Update version
83+
"""Update version in _version.py and sync dependencies."""
84+
content = VERSION_FILE.read_text()
7885
new_content = re.sub(
7986
r'__version__ = "[^"]+"', f'__version__ = "{new_version}"', content
8087
)
81-
82-
version_path.write_text(new_content)
88+
VERSION_FILE.write_text(new_content)
8389

8490
# Run uv sync to update lock file
8591
try:
86-
subprocess.run(["uv", "sync"], check=True, cwd=Path(__file__).parent.parent)
92+
subprocess.run(["uv", "sync"], check=True, cwd=PROJECT_ROOT)
8793
except subprocess.CalledProcessError as e:
8894
print(f"Warning: Failed to run 'uv sync': {e}")
8995

9096

91-
def main():
92-
if len(sys.argv) != 2 or sys.argv[1] not in ["major", "minor", "patch"]:
93-
print(__doc__)
94-
sys.exit(1)
95-
96-
bump_type = sys.argv[1]
97+
def handle_version_change(action_description, new_version):
98+
"""Common logic for handling version changes."""
99+
current = get_current_version()
100+
print(f"{action_description} from {current} to {new_version}")
101+
update_version(new_version)
102+
print("✓ Updated _version.py")
103+
return new_version
97104

98-
try:
99-
current = get_current_version()
100-
new = bump_version(current, bump_type)
101105

102-
print(f"Bumping version from {current} to {new}")
103-
update_version(new)
104-
print("✓ Updated _version.py")
106+
def print_next_steps(version):
107+
"""Print the next steps after version update."""
108+
print("\nNext steps:")
109+
print(
110+
f"1. Commit the change: git add {FILES_TO_COMMIT} && git commit -m 'chore: bump version to {version}'"
111+
)
112+
print(f"2. Create and push tag: git tag v{version} && git push origin v{version}")
113+
print(
114+
"3. The GitHub Action will automatically create a release and publish to PyPI and Container Registry"
115+
)
105116

106-
files_to_commit = "src/modelscope_mcp_server/_version.py"
107117

108-
print("\nNext steps:")
109-
print(
110-
f"1. Commit the change: git add {files_to_commit} && git commit -m 'chore: bump version to {new}'"
111-
)
112-
print(f"2. Create and push tag: git tag v{new} && git push origin v{new}")
113-
print(
114-
"3. The GitHub Action will automatically create a release and publish to PyPI and Container Registry"
115-
)
118+
def main():
119+
"""Main function to handle version bumping."""
120+
if len(sys.argv) not in [2, 3]:
121+
print(__doc__)
122+
sys.exit(1)
116123

124+
try:
125+
if len(sys.argv) == 2:
126+
# Traditional bump: python bump_version.py major/minor/patch
127+
bump_type = sys.argv[1]
128+
if bump_type not in BUMP_TYPES:
129+
print(__doc__)
130+
sys.exit(1)
131+
132+
current = get_current_version()
133+
new = bump_version(current, bump_type)
134+
final_version = handle_version_change("Bumping version", new)
135+
136+
elif len(sys.argv) == 3:
137+
# Manual set: python bump_version.py set 1.2.3a1
138+
if sys.argv[1] != "set":
139+
print(__doc__)
140+
sys.exit(1)
141+
142+
new = sys.argv[2]
143+
validate_version_format(new)
144+
final_version = handle_version_change("Setting version", new)
145+
146+
print_next_steps(final_version)
117147
except Exception as e:
118148
print(f"Error: {e}")
119149
sys.exit(1)

0 commit comments

Comments
 (0)