import json
import re
import sys
import subprocess
from pathlib import Path
def get_repo_root() -> Path:
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
check=True
)
return Path(result.stdout.strip())
def parse_version(version: str) -> tuple[int, int, int]:
match = re.match(r"^(\d+)\.(\d+)\.(\d+)$", version)
if not match:
raise ValueError(f"Invalid version format: {version}")
return int(match.group(1)), int(match.group(2)), int(match.group(3))
def bump_version(current: str, bump_type: str) -> str:
major, minor, patch = parse_version(current)
if bump_type == "major":
return f"{major + 1}.0.0"
elif bump_type == "minor":
return f"{major}.{minor + 1}.0"
elif bump_type == "patch":
return f"{major}.{minor}.{patch + 1}"
else:
parse_version(bump_type) return bump_type
def get_cargo_version(cargo_path: Path) -> str:
content = cargo_path.read_text()
match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
if not match:
raise ValueError("Could not find version in Cargo.toml")
return match.group(1)
def set_cargo_version(cargo_path: Path, version: str) -> None:
content = cargo_path.read_text()
new_content = re.sub(
r'^(version\s*=\s*)"[^"]+"',
f'\\1"{version}"',
content,
count=1,
flags=re.MULTILINE
)
cargo_path.write_text(new_content)
def get_npm_version(package_path: Path) -> str:
data = json.loads(package_path.read_text())
return data.get("version", "0.0.0")
def set_npm_version(package_path: Path, version: str) -> None:
data = json.loads(package_path.read_text())
data["version"] = version
package_path.write_text(json.dumps(data, indent=2) + "\n")
def check_versions(root: Path) -> dict[str, str]:
cargo_path = root / "Cargo.toml"
package_path = root / "package.json"
versions = {
"cargo": get_cargo_version(cargo_path),
"npm": get_npm_version(package_path),
}
return versions
def main():
if len(sys.argv) < 2:
print(__doc__)
sys.exit(1)
arg = sys.argv[1]
root = get_repo_root()
cargo_path = root / "Cargo.toml"
package_path = root / "package.json"
if arg == "--check":
versions = check_versions(root)
print(f"Cargo.toml: {versions['cargo']}")
print(f"package.json: {versions['npm']}")
if versions['cargo'] == versions['npm']:
print("\n✓ Versions are synchronized")
else:
print("\n⚠️ Versions are NOT synchronized!")
sys.exit(1)
return
current = get_cargo_version(cargo_path)
print(f"Current version: {current}")
new_version = bump_version(current, arg)
print(f"New version: {new_version}")
response = input("\nProceed? [y/N] ").strip().lower()
if response != "y":
print("Aborted.")
sys.exit(1)
set_cargo_version(cargo_path, new_version)
print(f"✓ Updated Cargo.toml")
set_npm_version(package_path, new_version)
print(f"✓ Updated package.json")
print(f"\nVersion bumped to {new_version}")
print("\nNext steps:")
print(f" git add Cargo.toml package.json")
print(f" git commit -m 'chore: bump version to {new_version}'")
print(f" git tag v{new_version}")
print(f" git push origin main --tags")
if __name__ == "__main__":
main()