Created
October 20, 2025 10:38
-
-
Save Boomatang/2af4e9ed4f2e0e3e1f9a6b6994aa9588 to your computer and use it in GitHub Desktop.
Bump the version in a cargo.toml file and commit the changes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Bump version to next development version after a release. | |
| This script: | |
| 1. Reads the current version from Cargo.toml | |
| 2. Bumps to the next minor version with -dev suffix | |
| 3. Updates Cargo.toml | |
| 4. Runs cargo check to update Cargo.lock | |
| 5. Commits the changes with proper message format | |
| Requirements: | |
| pip install semver | |
| """ | |
| import re | |
| import subprocess | |
| import sys | |
| import tomllib | |
| from pathlib import Path | |
| try: | |
| import semver | |
| except ImportError: | |
| print("❌ Error: semver package not found") | |
| print(" Install with: pip install semver") | |
| sys.exit(1) | |
| def run_command(cmd, check=True, capture_output=True): | |
| """Run a shell command and return the result.""" | |
| result = subprocess.run( | |
| cmd, | |
| shell=True, | |
| check=check, | |
| capture_output=capture_output, | |
| text=True | |
| ) | |
| return result | |
| def get_git_user(): | |
| """Get git user name and email for Signed-off-by.""" | |
| try: | |
| name = run_command("git config user.name").stdout.strip() | |
| email = run_command("git config user.email").stdout.strip() | |
| return f"{name} <{email}>" | |
| except subprocess.CalledProcessError: | |
| return None | |
| def read_cargo_toml(): | |
| """Read Cargo.toml and return both the parsed data and raw content.""" | |
| cargo_path = Path("Cargo.toml") | |
| if not cargo_path.exists(): | |
| print("❌ Error: Cargo.toml not found in current directory") | |
| sys.exit(1) | |
| raw_content = cargo_path.read_text() | |
| try: | |
| with open(cargo_path, "rb") as f: | |
| parsed_data = tomllib.load(f) | |
| except tomllib.TOMLDecodeError as e: | |
| print(f"❌ Error: Could not parse Cargo.toml: {e}") | |
| sys.exit(1) | |
| return parsed_data, raw_content | |
| def get_version(cargo_data): | |
| """Extract the current version from parsed Cargo.toml data.""" | |
| if "package" not in cargo_data or "version" not in cargo_data["package"]: | |
| print("❌ Error: Could not find version in Cargo.toml [package] section") | |
| sys.exit(1) | |
| return cargo_data["package"]["version"] | |
| def bump_version(current_version): | |
| """ | |
| Bump the minor version and add -dev suffix. | |
| Examples: | |
| 0.11.0-dev -> 0.12.0-dev | |
| 0.11.0 -> 0.12.0-dev | |
| """ | |
| # Remove -dev suffix if present for parsing | |
| version_str = current_version.replace("-dev", "") | |
| try: | |
| # Parse the version | |
| ver = semver.Version.parse(version_str) | |
| # Bump minor version | |
| new_ver = ver.bump_minor() | |
| # Add -dev suffix | |
| new_version = f"{new_ver}-dev" | |
| return new_version | |
| except ValueError as e: | |
| print(f"❌ Error: Invalid version format: {current_version}") | |
| print(f" {e}") | |
| sys.exit(1) | |
| def update_cargo_toml(cargo_content, old_version, new_version): | |
| """Update the version in Cargo.toml content.""" | |
| # Replace the version line in the [package] section | |
| updated = re.sub( | |
| rf'^version\s*=\s*"{re.escape(old_version)}"', | |
| f'version = "{new_version}"', | |
| cargo_content, | |
| count=1, | |
| flags=re.MULTILINE | |
| ) | |
| if updated == cargo_content: | |
| print(f"❌ Error: Could not update version in Cargo.toml") | |
| sys.exit(1) | |
| return updated | |
| def write_cargo_toml(content): | |
| """Write updated content back to Cargo.toml.""" | |
| Path("Cargo.toml").write_text(content) | |
| def main(): | |
| print("🚀 Version Bump Script") | |
| print("=" * 50) | |
| # Step 1: Read Cargo.toml | |
| print("\n📖 Reading Cargo.toml...") | |
| cargo_data, cargo_content = read_cargo_toml() | |
| # Step 2: Parse current version | |
| current_version = get_version(cargo_data) | |
| print(f" Current version: {current_version}") | |
| # Step 3: Calculate next version | |
| new_version = bump_version(current_version) | |
| print(f" Next version: {new_version}") | |
| # Confirm with user | |
| print(f"\n⚠️ About to bump version: {current_version} → {new_version}") | |
| response = input(" Continue? [y/N]: ").strip().lower() | |
| if response != 'y': | |
| print("❌ Aborted by user") | |
| sys.exit(0) | |
| # Step 4: Update Cargo.toml | |
| print("\n✏️ Updating Cargo.toml...") | |
| updated_content = update_cargo_toml(cargo_content, current_version, new_version) | |
| write_cargo_toml(updated_content) | |
| print(" ✅ Cargo.toml updated") | |
| # Step 5: Run cargo check to update Cargo.lock | |
| print("\n🔧 Running cargo check to update Cargo.lock...") | |
| try: | |
| result = run_command("cargo check", capture_output=True) | |
| print(" ✅ Cargo.lock updated") | |
| except subprocess.CalledProcessError as e: | |
| print(f" ❌ cargo check failed:") | |
| print(e.stderr) | |
| sys.exit(1) | |
| # Step 6: Git add files | |
| print("\n📝 Staging changes...") | |
| try: | |
| run_command("git add Cargo.toml Cargo.lock") | |
| print(" ✅ Staged Cargo.toml and Cargo.lock") | |
| except subprocess.CalledProcessError as e: | |
| print(f" ❌ git add failed: {e}") | |
| sys.exit(1) | |
| # Step 7: Create commit message | |
| # Remove -dev suffix for commit message | |
| version_for_commit = new_version.replace("-dev", "") | |
| commit_message = f"On to the next release: {version_for_commit}" | |
| # Add Signed-off-by if git user is configured | |
| git_user = get_git_user() | |
| if git_user: | |
| commit_message += f"\n\nSigned-off-by: {git_user}" | |
| # Commit changes | |
| print("\n💾 Committing changes...") | |
| try: | |
| run_command(f'git commit -m "{commit_message}"') | |
| print(f' ✅ Committed with message: "{commit_message.split(chr(10))[0]}"') | |
| except subprocess.CalledProcessError as e: | |
| print(f" ❌ git commit failed: {e}") | |
| sys.exit(1) | |
| # Step 8: Summary | |
| print("\n" + "=" * 50) | |
| print("✅ SUCCESS!") | |
| print("=" * 50) | |
| print(f"\n📦 Version bumped: {current_version} → {new_version}") | |
| print(f"💬 Commit message: {commit_message.split(chr(10))[0]}") | |
| print("\n📋 Next steps:") | |
| print(" 1. Review the commit: git show") | |
| print(" 2. Push the commit: git push") | |
| print("\n⚠️ Remember: This script does NOT push automatically!") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment