Version-bump your software with a single command
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pre-commit, post-commit, and setup hook system for extending bump-my-version functionality with custom automation workflows. Provides shell command execution at key points in the version bump process with full context access and error handling.
Functions for executing hooks at different stages of the version bump workflow.
def run_setup_hooks(
config: Config,
current_version: Version,
dry_run: bool = False
) -> None:
"""
Execute setup hooks before version processing begins.
Runs configured setup hooks in sequence before any version analysis
or file modifications. Useful for environment preparation or validation.
Args:
config: Configuration containing setup_hooks list
current_version: Current Version object for context
dry_run: Preview operations without executing
Raises:
HookError: Hook execution failed
"""
def run_pre_commit_hooks(
config: Config,
current_version: Version,
new_version: Version,
dry_run: bool = False
) -> None:
"""
Execute pre-commit hooks before SCM operations.
Runs configured pre-commit hooks after file modifications but before
creating commits or tags. Ideal for formatting, linting, or testing.
Args:
config: Configuration containing pre_commit_hooks list
current_version: Current Version object
new_version: New Version object
dry_run: Preview operations without executing
Raises:
HookError: Hook execution failed
"""
def run_post_commit_hooks(
config: Config,
current_version: Version,
new_version: Version,
dry_run: bool = False
) -> None:
"""
Execute post-commit hooks after SCM operations complete.
Runs configured post-commit hooks after all version bump operations
including commits and tags. Perfect for deployment, notifications, or cleanup.
Args:
config: Configuration containing post_commit_hooks list
current_version: Current Version object
new_version: New Version object
dry_run: Preview operations without executing
Raises:
HookError: Hook execution failed
"""Hook settings are configured in the main bump-my-version configuration:
class Config(BaseSettings):
# Hook configuration lists
setup_hooks: List[str] = []
pre_commit_hooks: List[str] = []
post_commit_hooks: List[str] = []Execute before any version processing begins:
[tool.bumpversion]
setup_hooks = [
"echo 'Starting version bump process'",
"uv sync --upgrade",
"python -m pytest --no-cov -x",
"pre-commit run --all-files"
]Use Cases:
Execute after file modifications but before SCM operations:
[tool.bumpversion]
pre_commit_hooks = [
"python setup.py check",
"uv build",
"twine check dist/*",
"git add dist/",
"git add CHANGELOG.md"
]Use Cases:
Execute after all SCM operations complete:
[tool.bumpversion]
post_commit_hooks = [
"git push origin main",
"git push --tags",
"gh release create {new_version} --generate-notes",
"uv publish",
"slack-notify 'Released {new_version}'"
]Use Cases:
Hooks have access to the full template context including version information, timestamps, and SCM details:
context = {
# Version information
"current_version": "1.0.0",
"new_version": "1.0.1",
"major": "1",
"minor": "0",
"patch": "1",
# SCM information
"commit_sha": "abc123...",
"branch_name": "main",
"tag_name": "v1.0.1",
# Timestamps
"now": datetime.now(),
"utcnow": datetime.utcnow(),
# Environment variables (BMP_* prefixed)
"BMP_CUSTOM_VAR": "value"
}[tool.bumpversion]
post_commit_hooks = [
"echo 'Released version {new_version} on {now:%Y-%m-%d}'",
"git tag -a archive-{current_version} -m 'Archive old version {current_version}'",
"curl -X POST https://api.example.com/releases -d version={new_version}",
"docker build -t myapp:{new_version} .",
"docker push myapp:{new_version}"
][tool.bumpversion]
current_version = "1.0.0"
commit = true
tag = true
# Simple hooks
setup_hooks = ["python -m pytest"]
pre_commit_hooks = ["python setup.py check"]
post_commit_hooks = ["git push --follow-tags"][tool.bumpversion]
current_version = "1.0.0"
commit = true
tag = true
# Comprehensive development workflow
setup_hooks = [
"echo 'Starting release process for {current_version} → {new_version}'",
"uv sync --upgrade",
"python -m pytest --cov=src --cov-report=html",
"pre-commit run --all-files",
"python -c 'import sys; sys.exit(0 if \"{new_version}\" > \"{current_version}\" else 1)'"
]
pre_commit_hooks = [
"uv build",
"twine check dist/*",
"python scripts/update_changelog.py {current_version} {new_version}",
"git add CHANGELOG.md",
"python scripts/update_docs.py {new_version}",
"git add docs/"
]
post_commit_hooks = [
"git push origin {branch_name}",
"git push --tags",
"gh release create {new_version} dist/* --generate-notes",
"uv publish --token $PYPI_TOKEN",
"python scripts/notify_slack.py 'Released {new_version}'",
"python scripts/update_dependencies.py {new_version}"
][tool.bumpversion]
# Hooks for CI/CD pipeline integration
setup_hooks = [
"echo 'CI Release Process Started'",
"echo 'RELEASE_VERSION={new_version}' >> $GITHUB_ENV"
]
pre_commit_hooks = [
"docker build -t myapp:{new_version} .",
"docker run --rm myapp:{new_version} python -m pytest",
"echo 'DOCKER_IMAGE=myapp:{new_version}' >> $GITHUB_ENV"
]
post_commit_hooks = [
"docker push myapp:{new_version}",
"docker tag myapp:{new_version} myapp:latest",
"docker push myapp:latest",
"kubectl set image deployment/myapp myapp=myapp:{new_version}"
]from bumpversion.hooks import run_setup_hooks, run_pre_commit_hooks, run_post_commit_hooks
from bumpversion.config import get_configuration
from bumpversion.context import get_context
# Load configuration
config = get_configuration()
# Create context
context = get_context(config)
context.update({
"current_version": "1.0.0",
"new_version": "1.0.1"
})
# Create version objects
from bumpversion.versioning.models import Version
current_version = Version({"major": "1", "minor": "0", "patch": "0"}, None)
new_version = Version({"major": "1", "minor": "0", "patch": "1"}, None)
try:
# Execute hooks in sequence
run_setup_hooks(config, current_version, dry_run=False)
print("Setup hooks completed successfully")
# ... perform version bump operations ...
run_pre_commit_hooks(config, current_version, new_version, dry_run=False)
print("Pre-commit hooks completed successfully")
# ... perform SCM operations ...
run_post_commit_hooks(config, current_version, new_version, dry_run=False)
print("Post-commit hooks completed successfully")
except HookError as e:
print(f"Hook execution failed: {e}")
print(f"Failed command: {e.command}")
print(f"Exit code: {e.exit_code}")
print(f"Output: {e.output}")import subprocess
from typing import List, Dict, Any
from bumpversion.exceptions import HookError
def run_custom_hooks(
hooks: List[str],
context: Dict[str, Any],
cwd: Optional[Path] = None
) -> None:
"""
Run custom hooks with enhanced error handling.
Args:
hooks: List of shell commands to execute
context: Template context for command formatting
cwd: Working directory for command execution
"""
for hook in hooks:
try:
# Format command with context
formatted_command = hook.format(**context)
print(f"Executing hook: {formatted_command}")
# Execute command
result = subprocess.run(
formatted_command,
shell=True,
cwd=cwd,
capture_output=True,
text=True,
check=True
)
print(f"Hook output: {result.stdout}")
except subprocess.CalledProcessError as e:
raise HookError(
f"Hook failed: {formatted_command}",
command=formatted_command,
exit_code=e.returncode,
output=e.stderr
)import os
from bumpversion.hooks import run_post_commit_hooks
# Only run deployment hooks in production environment
if os.getenv("ENVIRONMENT") == "production":
config.post_commit_hooks.extend([
"python scripts/deploy_production.py {new_version}",
"python scripts/notify_team.py 'Production deployment of {new_version} complete'"
])
# Run different hooks based on version type
if context.get("patch") != "0":
# Patch release
config.post_commit_hooks.append("python scripts/hotfix_notification.py")
elif context.get("minor") != "0":
# Minor release
config.post_commit_hooks.append("python scripts/feature_announcement.py")
else:
# Major release
config.post_commit_hooks.extend([
"python scripts/major_release_blog.py {new_version}",
"python scripts/social_media_announcement.py {new_version}"
])from bumpversion.hooks import run_pre_commit_hooks
from bumpversion.exceptions import HookError
try:
run_pre_commit_hooks(config, current_version, new_version)
except HookError as e:
print(f"Pre-commit hook failed: {e}")
# Check if it's a specific type of failure
if "pytest" in e.command:
print("Tests failed - aborting release")
sys.exit(1)
elif "build" in e.command:
print("Build failed - checking for dependency issues")
# Could implement recovery logic here
else:
print(f"Unknown hook failure: {e.command}")
print(f"Exit code: {e.exit_code}")
print(f"Error output: {e.output}")[tool.bumpversion]
# Base configuration
current_version = "1.0.0"
# Development environment hooks
setup_hooks = [
"python -c 'import os; print(f\"Environment: {os.getenv(\"ENVIRONMENT\", \"development\")}\")'",
"if [ \"$ENVIRONMENT\" = \"development\" ]; then echo 'Dev mode - skipping some checks'; fi"
]
post_commit_hooks = [
# Always push to origin
"git push origin {branch_name}",
"git push --tags",
# Conditional publishing
"if [ \"$ENVIRONMENT\" = \"production\" ]; then uv publish; fi",
"if [ \"$ENVIRONMENT\" = \"staging\" ]; then uv publish --repository testpypi; fi"
]Install with Tessl CLI
npx tessl i tessl/pypi-bump-my-version