chore: setup auto tag & bump scripts with pre-commit

This commit is contained in:
Ryan Walters
2025-09-07 15:12:19 -05:00
parent fff44faa05
commit 6ddc6d1181
4 changed files with 292 additions and 0 deletions

1
.gitattributes vendored
View File

@@ -1 +1,2 @@
* text=auto eol=lf
scripts/* linguist-detectable=false

View File

@@ -12,6 +12,13 @@ repos:
- id: forbid-submodules
- id: mixed-line-ending
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v4.2.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
args: []
- repo: local
hooks:
- id: cargo-fmt
@@ -20,15 +27,31 @@ repos:
language: system
types: [rust]
pass_filenames: false
- id: cargo-check
name: cargo check
entry: cargo check --all-targets
language: system
types_or: [rust, cargo, cargo-lock]
pass_filenames: false
- id: cargo-check-wasm
name: cargo check for wasm32-unknown-emscripten
entry: cargo check --all-targets --target=wasm32-unknown-emscripten
language: system
types_or: [rust, cargo, cargo-lock]
pass_filenames: false
- id: bump-version
name: bump version based on commit message
entry: python scripts/bump-version.py
language: system
stages: [commit-msg]
always_run: true
- id: tag-version
name: tag version based on commit message
entry: python scripts/tag-version.py
language: system
stages: [post-commit]
always_run: true

143
scripts/bump-version.py Normal file
View File

@@ -0,0 +1,143 @@
#!/usr/bin/env python3
"""
Pre-commit hook script to automatically bump Cargo.toml version based on commit message.
This script parses the commit message for version bump keywords and uses cargo set-version
to update the version in Cargo.toml accordingly.
Supported keywords:
- "major" or "breaking": Bump major version (1.0.0 -> 2.0.0)
- "minor" or "feature": Bump minor version (1.0.0 -> 1.1.0)
- "patch" or "fix" or "bugfix": Bump patch version (1.0.0 -> 1.0.1)
Usage: python scripts/bump-version.py <commit_message_file>
"""
import sys
import re
import subprocess
import os
from pathlib import Path
def get_current_version():
"""Get the current version from Cargo.toml."""
try:
result = subprocess.run(
["cargo", "metadata", "--format-version", "1", "--no-deps"],
capture_output=True,
text=True,
check=True
)
# Parse the JSON output to get version
import json
metadata = json.loads(result.stdout)
return metadata["packages"][0]["version"]
except (subprocess.CalledProcessError, json.JSONDecodeError, KeyError) as e:
print(f"Error getting current version: {e}", file=sys.stderr)
return None
def bump_version(current_version, bump_type):
"""Calculate the new version based on bump type."""
try:
major, minor, patch = map(int, current_version.split('.'))
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:
return None
except ValueError:
print(f"Invalid version format: {current_version}", file=sys.stderr)
return None
def set_version(new_version):
"""Set the new version using cargo set-version."""
try:
result = subprocess.run(
["cargo", "set-version", new_version],
capture_output=True,
text=True,
check=True
)
print(f"Successfully bumped version to {new_version}")
return True
except subprocess.CalledProcessError as e:
print(f"Error setting version: {e}", file=sys.stderr)
print(f"stdout: {e.stdout}", file=sys.stderr)
print(f"stderr: {e.stderr}", file=sys.stderr)
return False
def parse_commit_message(commit_message_file):
"""Parse the commit message file for version bump keywords."""
try:
with open(commit_message_file, 'r', encoding='utf-8') as f:
message = f.read().lower()
except FileNotFoundError:
print(f"Commit message file not found: {commit_message_file}", file=sys.stderr)
return None
except Exception as e:
print(f"Error reading commit message: {e}", file=sys.stderr)
return None
# Check for version bump keywords
if re.search(r'\b(major|breaking)\b', message):
return "major"
elif re.search(r'\b(minor|feature)\b', message):
return "minor"
elif re.search(r'\b(patch|fix|bugfix)\b', message):
return "patch"
return None
def main():
if len(sys.argv) != 2:
print("Usage: python scripts/bump-version.py <commit_message_file>", file=sys.stderr)
sys.exit(1)
commit_message_file = sys.argv[1]
# Parse commit message for version bump type
bump_type = parse_commit_message(commit_message_file)
if not bump_type:
print("No version bump keywords found in commit message")
sys.exit(0)
print(f"Found version bump type: {bump_type}")
# Get current version
current_version = get_current_version()
if not current_version:
print("Failed to get current version", file=sys.stderr)
sys.exit(1)
print(f"Current version: {current_version}")
# Calculate new version
new_version = bump_version(current_version, bump_type)
if not new_version:
print("Failed to calculate new version", file=sys.stderr)
sys.exit(1)
print(f"New version: {new_version}")
# Set the new version
if set_version(new_version):
print("Version bump completed successfully")
sys.exit(0)
else:
print("Version bump failed", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

125
scripts/tag-version.py Normal file
View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""
Post-commit hook script to automatically create git tags based on the version in Cargo.toml.
This script reads the current version from Cargo.toml and creates a git tag with that version.
It's designed to run after the version has been bumped by the bump-version.py script.
Usage: python scripts/tag-version.py
"""
import sys
import subprocess
import re
from pathlib import Path
def get_version_from_cargo_toml():
"""Get the current version from Cargo.toml."""
cargo_toml_path = Path("Cargo.toml")
if not cargo_toml_path.exists():
print("Cargo.toml not found", file=sys.stderr)
return None
try:
with open(cargo_toml_path, 'r', encoding='utf-8') as f:
content = f.read()
# Look for version = "x.y.z" pattern
version_match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content)
if version_match:
return version_match.group(1)
else:
print("Could not find version in Cargo.toml", file=sys.stderr)
return None
except Exception as e:
print(f"Error reading Cargo.toml: {e}", file=sys.stderr)
return None
def get_existing_tags():
"""Get list of existing git tags."""
try:
result = subprocess.run(
["git", "tag", "--list"],
capture_output=True,
text=True,
check=True
)
return result.stdout.strip().split('\n') if result.stdout.strip() else []
except subprocess.CalledProcessError as e:
print(f"Error getting git tags: {e}", file=sys.stderr)
return []
def create_git_tag(version):
"""Create a git tag with the specified version."""
tag_name = f"v{version}"
try:
# Check if tag already exists
existing_tags = get_existing_tags()
if tag_name in existing_tags:
print(f"Tag {tag_name} already exists, skipping")
return True
# Create the tag
result = subprocess.run(
["git", "tag", tag_name],
capture_output=True,
text=True,
check=True
)
print(f"Successfully created tag: {tag_name}")
return True
except subprocess.CalledProcessError as e:
print(f"Error creating git tag: {e}", file=sys.stderr)
print(f"stdout: {e.stdout}", file=sys.stderr)
print(f"stderr: {e.stderr}", file=sys.stderr)
return False
def is_git_repository():
"""Check if we're in a git repository."""
try:
subprocess.run(
["git", "rev-parse", "--git-dir"],
capture_output=True,
check=True
)
return True
except subprocess.CalledProcessError:
return False
def main():
# Check if we're in a git repository
if not is_git_repository():
print("Not in a git repository, skipping tag creation")
sys.exit(0)
# Get the current version from Cargo.toml
version = get_version_from_cargo_toml()
if not version:
print("Could not determine version, skipping tag creation")
sys.exit(0)
print(f"Current version: {version}")
# Create the git tag
if create_git_tag(version):
print("Tag creation completed successfully")
sys.exit(0)
else:
print("Tag creation failed", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()