33Version bumping script for ModelScope MCP Server releases.
44
55Usage:
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
1112import re
1213import subprocess
1314import sys
1415from 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
1729def 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
3745def 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+
5668def 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
7082def 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 ("\n Next 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 ("\n Next 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