Python commitizen client tool for standardized commit conventions and automated version management
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Extensible plugin system for creating custom commit message rules, version providers, changelog formats, and version schemes. Commitizen's plugin architecture enables teams to define their own commit conventions and integrate with various project types and workflows.
Automatic discovery and registration of commitizen plugins installed via entry points.
def discover_plugins() -> None:
"""
Discover and register all available commitizen plugins.
Searches for plugins in the 'commitizen.plugin' entry point group
and registers them in the global plugin registry.
"""
registry: dict[str, type[BaseCommitizen]]
"""Global plugin registry mapping plugin names to plugin classes."""Factory function for creating commitizen plugin instances based on configuration.
def commiter_factory(config: BaseConfig) -> BaseCommitizen:
"""
Create commitizen plugin instance based on configuration.
Parameters:
- config: Configuration object containing plugin name
Returns:
BaseCommitizen instance for the configured plugin
Raises:
NoCommitizenFoundException: If plugin is not found
"""Abstract base class that all commitizen plugins must inherit from.
class BaseCommitizen:
"""
Abstract base class for all commitizen plugins.
Defines the interface that plugins must implement for commit message
generation, validation, and version bump logic.
"""
def __init__(self, config: BaseConfig):
"""
Initialize plugin with configuration.
Parameters:
- config: Configuration object
"""
def questions(self) -> Questions:
"""
Return interactive questions for commit creation.
Returns:
List of question dictionaries for questionary prompts
"""
def message(self, answers: dict[str, Any]) -> str:
"""
Generate commit message from user answers.
Parameters:
- answers: Dictionary of answers from interactive questions
Returns:
Formatted commit message string
"""
def example(self) -> str:
"""
Return example commit message.
Returns:
Example commit message following plugin rules
"""
def schema(self) -> str:
"""
Return commit message schema description.
Returns:
Human-readable schema description
"""
def schema_pattern(self) -> str:
"""
Return regex pattern for commit message validation.
Returns:
Regex pattern string for validation
"""
def info(self) -> str:
"""
Return plugin information.
Returns:
Plugin description and usage information
"""
def process_commit(self, commit: str) -> str:
"""
Process commit message for changelog generation.
Parameters:
- commit: Raw commit message
Returns:
Processed commit message for changelog
"""
def changelog_pattern(self) -> str:
"""
Return regex pattern for changelog parsing.
Returns:
Regex pattern for extracting changelog information
"""
def change_type_map(self) -> dict[str, str]:
"""
Return mapping of change types to display names.
Returns:
Dictionary mapping change types to changelog section names
"""
def change_type_order(self) -> list[str]:
"""
Return ordered list of change types for changelog.
Returns:
List of change types in display order
"""
def bump_pattern(self) -> str:
"""
Return regex pattern for version bump detection.
Returns:
Regex pattern for determining version increments
"""
def bump_map(self) -> dict[str, str]:
"""
Return mapping of patterns to version increments.
Returns:
Dictionary mapping regex groups to increment types (MAJOR, MINOR, PATCH)
"""Standard plugin implementing the Conventional Commits specification.
class ConventionalCommitsCz(BaseCommitizen):
"""
Plugin implementing the Conventional Commits specification.
Supports standard commit types: feat, fix, docs, style, refactor, test, chore.
Includes BREAKING CHANGE detection for major version bumps.
"""
def questions(self) -> Questions:
"""
Interactive questions for conventional commits.
Prompts for commit type, scope, description, body, and breaking changes.
"""
def message(self, answers: dict[str, Any]) -> str:
"""
Generate conventional commit message.
Format: <type>[optional scope]: <description>
[optional body]
[optional footer(s)]
"""Plugin for integrating with Jira issue tracking.
class JiraSmartCz(BaseCommitizen):
"""
Plugin for Jira integration with smart branch detection.
Automatically detects Jira issue keys from branch names and includes
them in commit messages.
"""
def questions(self) -> Questions:
"""
Interactive questions with Jira issue integration.
Includes Jira issue key detection and linking.
"""Highly configurable plugin for teams with specific commit conventions.
class CustomizeCommitsCz(BaseCommitizen):
"""
Customizable plugin supporting user-defined commit conventions.
Allows complete customization of questions, message format, and validation
through configuration file settings.
"""
def questions(self) -> Questions:
"""
Questions defined in configuration [tool.commitizen.customize.questions].
Supports all questionary question types with full customization.
"""from commitizen.cz.base import BaseCommitizen
from commitizen.defaults import Questions
class MyCustomCz(BaseCommitizen):
"""Custom commitizen plugin for my team's conventions."""
def questions(self) -> Questions:
return [
{
"type": "list",
"name": "type",
"message": "Select the type of change:",
"choices": [
{"value": "feature", "name": "feature: New feature"},
{"value": "bugfix", "name": "bugfix: Bug fix"},
{"value": "docs", "name": "docs: Documentation"},
{"value": "refactor", "name": "refactor: Code refactoring"}
]
},
{
"type": "input",
"name": "scope",
"message": "Scope (optional):"
},
{
"type": "input",
"name": "subject",
"message": "Short description:",
"validate": lambda x: len(x) > 0 and len(x) <= 50
},
{
"type": "input",
"name": "body",
"message": "Longer description (optional):"
}
]
def message(self, answers: dict[str, Any]) -> str:
scope = f"({answers['scope']})" if answers.get('scope') else ""
message = f"{answers['type']}{scope}: {answers['subject']}"
if answers.get('body'):
message += f"\n\n{answers['body']}"
return message
def example(self) -> str:
return "feature(auth): add OAuth2 integration"
def schema(self) -> str:
return "<type>[(scope)]: <subject>\n\n[body]"
def schema_pattern(self) -> str:
return r"^(feature|bugfix|docs|refactor)(\(.+\))?: .{1,50}$"
def info(self) -> str:
return "Custom plugin for MyTeam commit conventions"
def bump_pattern(self) -> str:
return r"^(feature|BREAKING)"
def bump_map(self) -> dict[str, str]:
return {
"BREAKING": "MAJOR",
"feature": "MINOR",
"bugfix": "PATCH"
}
def change_type_map(self) -> dict[str, str]:
return {
"feature": "Features",
"bugfix": "Bug Fixes",
"docs": "Documentation",
"refactor": "Refactoring"
}from setuptools import setup, find_packages
setup(
name="my-commitizen-plugin",
version="1.0.0",
packages=find_packages(),
install_requires=["commitizen>=3.0.0"],
entry_points={
"commitizen.plugin": [
"my_custom_cz = my_plugin:MyCustomCz"
]
}
)[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "my-commitizen-plugin"
version = "1.0.0"
description = "Custom commitizen plugin"
[tool.poetry.dependencies]
python = "^3.8"
commitizen = "^3.0.0"
[tool.poetry.plugins."commitizen.plugin"]
my_custom_cz = "my_plugin:MyCustomCz"Custom version providers for different project types.
class VersionProvider:
"""Abstract base class for version providers."""
def get_version(self) -> str:
"""
Get current version from provider source.
Returns:
Current version string
"""
def set_version(self, version: str) -> None:
"""
Set version in provider source.
Parameters:
- version: New version string
"""
@property
def keyword(self) -> str:
"""
Provider keyword for entry point registration.
Returns:
String identifier for the provider
"""
class CustomProvider(VersionProvider):
"""Example custom version provider."""
@property
def keyword(self) -> str:
return "custom"
def get_version(self) -> str:
# Read version from custom source
with open("VERSION.txt") as f:
return f.read().strip()
def set_version(self, version: str) -> None:
# Write version to custom source
with open("VERSION.txt", "w") as f:
f.write(version)Custom changelog formats for different documentation styles.
class ChangelogFormat(Protocol):
"""Protocol for changelog format implementations."""
def get_changelog(
self,
tree: dict[str, Any],
header_format: str = "",
**kwargs: Any
) -> str:
"""
Generate changelog content from commit tree.
Parameters:
- tree: Parsed commit tree structure
- header_format: Header format template
- kwargs: Additional format-specific options
Returns:
Formatted changelog content
"""
class CustomChangelogFormat:
"""Example custom changelog format."""
def get_changelog(
self,
tree: dict[str, Any],
header_format: str = "",
**kwargs: Any
) -> str:
# Generate custom format changelog
content = []
for version, changes in tree.items():
content.append(f"## Version {version}")
for change_type, items in changes.items():
content.append(f"### {change_type.title()}")
for item in items:
content.append(f"- {item}")
content.append("")
return "\n".join(content)[tool.commitizen]
name = "my_custom_cz" # Plugin entry point name
version = "1.0.0"
# Plugin-specific configuration
[tool.commitizen.customize]
# Custom plugin settings go hereimport pytest
from commitizen.config import BaseConfig
from my_plugin import MyCustomCz
def test_plugin_questions():
config = BaseConfig({"name": "my_custom_cz"})
plugin = MyCustomCz(config)
questions = plugin.questions()
assert len(questions) > 0
assert questions[0]["name"] == "type"
def test_plugin_message():
config = BaseConfig({"name": "my_custom_cz"})
plugin = MyCustomCz(config)
answers = {
"type": "feature",
"subject": "add new feature"
}
message = plugin.message(answers)
assert message == "feature: add new feature"
def test_plugin_validation():
config = BaseConfig({"name": "my_custom_cz"})
plugin = MyCustomCz(config)
pattern = plugin.schema_pattern()
import re
assert re.match(pattern, "feature: valid message")
assert not re.match(pattern, "invalid message format")Install with Tessl CLI
npx tessl i tessl/pypi-commitizen