diff --git a/.gitattributes b/.gitattributes index 6313b56..1fd1481 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ * text=auto eol=lf +scripts/* linguist-detectable=false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e58e742..3ce5ae1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/scripts/bump-version.py b/scripts/bump-version.py new file mode 100644 index 0000000..0780fb3 --- /dev/null +++ b/scripts/bump-version.py @@ -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 +""" + +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 ", 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() diff --git a/scripts/tag-version.py b/scripts/tag-version.py new file mode 100644 index 0000000..6d49671 --- /dev/null +++ b/scripts/tag-version.py @@ -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()