CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-bump-my-version

Version-bump your software with a single command

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

scm-integration.mddocs/

SCM Integration

Source control management integration supporting Git and Mercurial for automated commits, tagging, and repository state management. Provides comprehensive SCM operations with repository status checking, dirty state validation, and automated commit/tag workflows.

Capabilities

SCM Information and Status

Classes for managing source control information and repository state.

class SCMInfo:
    """
    Information about the current source code management system.
    
    Provides comprehensive repository state including current commit,
    branch information, tag distance, and version detection from SCM.
    """
    
    def __init__(
        self,
        tool: Optional[str] = None,
        commit_sha: Optional[str] = None,
        distance_to_latest_tag: int = 0,
        current_version: str = "0.0.0",
        branch_name: Optional[str] = None,
        repository_root: Optional[Path] = None,
        short_branch_name: Optional[str] = None
    ) -> None:
        """
        Initialize SCM information.
        
        Args:
            tool: SCM tool name ('git', 'hg', etc.)
            commit_sha: Current commit SHA
            distance_to_latest_tag: Number of commits since latest tag
            current_version: Current version string
            branch_name: Full branch name
            repository_root: Path to repository root
            short_branch_name: Short branch name
        """
    
    @property
    def is_dirty(self) -> bool:
        """Whether repository has uncommitted changes."""
    
    @property
    def latest_tag_info(self) -> Optional[LatestTagInfo]:
        """Information about the most recent tag."""

class LatestTagInfo:
    """
    Information about the latest repository tag.
    
    Contains tag name, commit SHA, and version information
    extracted from the most recent repository tag.
    """
    
    tag_name: str
    commit_sha: str
    version: Optional[str] = None
    
    @property
    def is_version_tag(self) -> bool:
        """Whether tag represents a version."""

class SCMConfig: 
    """
    Configuration for SCM operations.
    
    Contains settings for commit behavior, tag creation,
    message templates, and repository interaction options.
    """
    
    commit: bool = False
    tag: bool = False
    commit_args: str = ""
    message: str = "Bump version: {current_version} → {new_version}"
    tag_name: str = "v{new_version}"
    tag_message: str = "Bump version: {current_version} → {new_version}"
    sign_tags: bool = False
    allow_dirty: bool = False

Git Implementation

Comprehensive Git integration with full repository management capabilities.

class Git:
    """
    Git implementation for SCM operations.
    
    Provides complete Git integration including repository status checking,
    commit creation, tag management, and branch operations.
    """
    
    def __init__(self, repo_root: Path) -> None:
        """
        Initialize Git integration.
        
        Args:
            repo_root: Path to Git repository root
        """
    
    def is_dirty(self) -> bool:
        """
        Check if repository has uncommitted changes.
        
        Returns:
            True if repository has staged or unstaged changes
        """
    
    def commit_and_tag(
        self,
        message: str,
        tag_name: Optional[str] = None,
        tag_message: Optional[str] = None,
        sign_tags: bool = False,
        commit_args: str = ""
    ) -> None:
        """
        Create commit and optional tag for version changes.
        
        Stages all modified files, creates commit with specified message,
        and optionally creates annotated tag.
        
        Args:
            message: Commit message
            tag_name: Tag name to create (optional)
            tag_message: Tag annotation message
            sign_tags: Whether to sign tags with GPG
            commit_args: Additional arguments for git commit
            
        Raises:
            subprocess.CalledProcessError: Git command failed
        """
    
    def get_current_info(self) -> SCMInfo:
        """
        Get comprehensive repository information.
        
        Returns:
            SCMInfo object with current repository state
        """
    
    def get_latest_tag_info(self) -> Optional[LatestTagInfo]:
        """
        Get information about the most recent tag.
        
        Returns:
            LatestTagInfo object or None if no tags exist
        """
    
    def get_branch_name(self) -> Optional[str]:
        """
        Get current branch name.
        
        Returns:
            Current branch name or None if detached HEAD
        """
    
    def add_path(self, path: Path) -> None:
        """
        Add file or directory to Git staging area.
        
        Args:
            path: Path to add to staging area
        """
    
    def tag_exists(self, tag_name: str) -> bool:
        """
        Check if tag exists in repository.
        
        Args:
            tag_name: Tag name to check
            
        Returns:
            True if tag exists
        """

class Mercurial:
    """
    Mercurial implementation for SCM operations.
    
    Provides Mercurial integration with repository status checking,
    commit creation, and tag management capabilities.
    """
    
    def __init__(self, repo_root: Path) -> None:
        """Initialize Mercurial integration."""
    
    def is_dirty(self) -> bool:
        """Check if repository has uncommitted changes."""
    
    def commit_and_tag(
        self,
        message: str,
        tag_name: Optional[str] = None,
        tag_message: Optional[str] = None
    ) -> None:
        """Create commit and optional tag."""
    
    def get_current_info(self) -> SCMInfo:
        """Get repository information."""

SCM Utility Functions

Helper functions for SCM detection and management.

def get_scm_info(repo_path: Optional[Path] = None) -> Optional[SCMInfo]:
    """
    Detect and get SCM information for repository.
    
    Automatically detects Git or Mercurial repositories and returns
    comprehensive information about repository state.
    
    Args:
        repo_path: Path to repository (defaults to current directory)
        
    Returns:
        SCMInfo object or None if no SCM detected
    """

def detect_scm_type(repo_path: Path) -> Optional[str]:
    """
    Detect SCM type for repository.
    
    Args:
        repo_path: Path to check for SCM
        
    Returns:
        SCM type ('git', 'hg') or None
    """

SCM Configuration Examples

Basic SCM Settings

[tool.bumpversion]
current_version = "1.0.0"
commit = true
tag = true
allow_dirty = false
message = "Bump version: {current_version} → {new_version}"
tag_name = "v{new_version}"
tag_message = "Release {new_version}"
commit_args = "--no-verify"
sign_tags = false

Advanced SCM Configuration

[tool.bumpversion]
# Commit settings
commit = true
commit_args = "--no-verify --gpg-sign"
message = """
Release version {new_version}

Changes:
- Updated version from {current_version} to {new_version}
- Automated by bump-my-version on {now:%Y-%m-%d}
"""

# Tag settings  
tag = true
tag_name = "release-{new_version}"
tag_message = "Release {new_version} ({now:%Y-%m-%d})"
sign_tags = true

# Repository settings
allow_dirty = false

Conditional SCM Operations

from bumpversion.config import get_configuration

# Load configuration
config = get_configuration()

# Only commit in CI environment
import os
if os.getenv("CI"):
    config.commit = True
    config.tag = True
else:
    config.commit = False
    config.tag = False

Usage Examples

Basic SCM Operations

from bumpversion.scm.git import Git
from pathlib import Path

# Initialize Git integration
git = Git(Path.cwd())

# Check repository status
if git.is_dirty():
    print("Repository has uncommitted changes")

# Get repository information
scm_info = git.get_current_info()
print(f"Current branch: {scm_info.branch_name}")
print(f"Current commit: {scm_info.commit_sha}")
print(f"Distance to latest tag: {scm_info.distance_to_latest_tag}")

# Create commit and tag
git.commit_and_tag(
    message="Bump version: 1.0.0 → 1.0.1",
    tag_name="v1.0.1",
    tag_message="Release version 1.0.1"
)

Repository State Checking

from bumpversion.scm.models import SCMInfo
from bumpversion.scm.git import Git

# Get comprehensive repository information
git = Git(Path.cwd())
scm_info = git.get_current_info()

print(f"SCM Tool: {scm_info.tool}")
print(f"Repository Root: {scm_info.repository_root}")
print(f"Current Branch: {scm_info.branch_name}")
print(f"Is Dirty: {scm_info.is_dirty}")

# Check latest tag information
latest_tag = git.get_latest_tag_info()
if latest_tag:
    print(f"Latest Tag: {latest_tag.tag_name}")
    print(f"Tag Commit: {latest_tag.commit_sha}")
    print(f"Is Version Tag: {latest_tag.is_version_tag}")

Automated Commit and Tag Creation

from bumpversion.scm.git import Git
from bumpversion.context import get_context
from bumpversion.config import get_configuration

# Load configuration and create context
config = get_configuration()
context = get_context(config)

# Initialize Git
git = Git(Path.cwd())

# Check if repository is clean (if required)
if not config.allow_dirty and git.is_dirty():
    raise DirtyWorkingDirectoryError("Repository has uncommitted changes")

# Format commit and tag messages
commit_message = config.message.format(**context)
tag_name = config.tag_name.format(**context) if config.tag else None
tag_message = config.tag_message.format(**context) if config.tag else None

# Create commit and tag
git.commit_and_tag(
    message=commit_message,
    tag_name=tag_name,
    tag_message=tag_message,
    sign_tags=config.sign_tags,
    commit_args=config.commit_args
)

Branch and Tag Management

from bumpversion.scm.git import Git

git = Git(Path.cwd())

# Get current branch
branch = git.get_branch_name()
print(f"Current branch: {branch}")

# Check if tag exists
tag_name = "v1.0.1"
if git.tag_exists(tag_name):
    print(f"Tag {tag_name} already exists")
else:
    print(f"Tag {tag_name} is available")

# Add specific files to staging
from pathlib import Path
git.add_path(Path("setup.py"))
git.add_path(Path("src/mypackage/__init__.py"))

SCM Integration with Configuration

from bumpversion.config import get_configuration
from bumpversion.scm.git import Git
from bumpversion.context import get_context

# Load configuration with SCM settings
config = get_configuration(
    commit=True,
    tag=True,
    message="Release {new_version}",
    tag_name="v{new_version}",
    allow_dirty=False
)

# Get SCM instance
git = Git(Path.cwd())

# Validate repository state
if not config.allow_dirty and git.is_dirty():
    print("Repository must be clean for version bump")
    exit(1)

# Get context for templating
context = get_context(config)
context.update({
    "current_version": "1.0.0",
    "new_version": "1.0.1"
})

# Execute SCM operations if configured
if config.commit or config.tag:
    git.commit_and_tag(
        message=config.message.format(**context),
        tag_name=config.tag_name.format(**context) if config.tag else None,
        tag_message=config.tag_message.format(**context) if config.tag else None,
        sign_tags=config.sign_tags,
        commit_args=config.commit_args
    )

Error Handling

from bumpversion.scm.git import Git
from bumpversion.exceptions import DirtyWorkingDirectoryError
import subprocess

try:
    git = Git(Path.cwd())
    
    # Check repository state
    if git.is_dirty() and not allow_dirty:
        raise DirtyWorkingDirectoryError(
            "Repository has uncommitted changes. "
            "Use --allow-dirty to override."
        )
    
    # Attempt SCM operations
    git.commit_and_tag(
        message="Bump version",
        tag_name="v1.0.1",
        tag_message="Release 1.0.1"
    )
    
except subprocess.CalledProcessError as e:
    print(f"Git command failed: {e}")
    print(f"Command: {e.cmd}")
    print(f"Output: {e.output}")
    
except DirtyWorkingDirectoryError as e:
    print(f"Repository state error: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-bump-my-version

docs

cli-commands.md

configuration.md

core-version-management.md

file-operations.md

hooks.md

index.md

scm-integration.md

tile.json