import os import re from typing import Any, Dict, List from commitizen import defaults from commitizen.cz.base import BaseCommitizen from commitizen.cz.utils import multiple_line_breaker, required_validator def parse_scope(text): if not text: return "" scope = text.strip().split() if len(scope) == 1: return scope[0] return "-".join(scope) def parse_subject(text): if isinstance(text, str): text = text.strip(".").strip() return required_validator(text, msg="Subject is required.") class MyCustomCommitizenCz(BaseCommitizen): bump_pattern = defaults.bump_pattern bump_map = defaults.bump_map commit_parser = defaults.commit_parser changelog_pattern = defaults.bump_pattern change_type_map = { "feat": "Features", "fix": "Bug Fixes", "refactor": "Refactor", "perf": "Performance", } def questions(self) -> List[Dict[str, Any]]: questions: List[Dict[str, Any]] = [ { "type": "list", "name": "prefix", "message": "Select the type of change you are committing", "choices": [ { "value": "checkpoint", "name": ( "checkpoint: A simple commit and push to make a backup" ), }, { "value": "fix", "name": "fix: A bug fix. Correlates with PATCH in SemVer", }, { "value": "feat", "name": "feat: A new feature. Correlates with MINOR in SemVer", }, { "value": "docs", "name": "docs: Documentation only changes"}, { "value": "style", "name": ( "style: Changes that do not affect the meaning of " "the code (white-space, formatting, missing " "semi-colons, etc)" ), }, { "value": "refactor", "name": ( "refactor: A code change that neither fixes " "a bug nor adds a feature" ), }, { "value": "perf", "name": "perf: A code change that improves performance", }, { "value": "test", "name": ( "test: Adding missing or correcting " "existing tests" ), }, { "value": "build", "name": ( "build: Changes that affect the build system or " "external dependencies (example scopes: pip, docker, npm)" ), }, ], }, { "type": "input", "name": "scope", "message": ( "What is the scope of this change? (class or file name): (press [enter] to skip)\n" ), "filter": parse_scope, }, { "type": "input", "name": "subject", "filter": parse_subject, "message": ( "Write a short and imperative summary of the code changes: (lower case and no period)\n" ), }, { "type": "input", "name": "body", "message": ( "Provide additional contextual information about the code changes: (press [enter] to skip)\n" ), "filter": multiple_line_breaker, }, { "type": "confirm", "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer", "name": "is_breaking_change", "default": False, }, { "type": "input", "name": "footer", "message": ( "Footer. Information about Breaking Changes and " "reference JIRA issues that this commit closes: (press [enter] to skip)\n" ), }, ] return questions def message(self, answers: dict) -> str: prefix = answers["prefix"] scope = answers["scope"] subject = answers["subject"] body = answers["body"] footer = answers["footer"] is_breaking_change = answers["is_breaking_change"] if scope: scope = f"({scope})" if body: body = f"\n\n{body}" if is_breaking_change: footer = f"BREAKING CHANGE: {footer}" if footer: footer = f"\n\n{footer}" message = f"************************** {prefix}{scope}: {subject}{body}{footer}" return message def example(self) -> str: return ( "fix: correct minor typos in code\n" "\n" "see the issue for details on the typos fixed\n" "\n" "closes issue JIRA-12 JIRA-34 JIRA-35" ) def schema(self) -> str: return ( "(): \n" "\n" "\n" "\n" "(BREAKING CHANGE: )