-
-
Notifications
You must be signed in to change notification settings - Fork 328
Expand file tree
/
Copy pathbase.py
More file actions
164 lines (133 loc) · 5.41 KB
/
base.py
File metadata and controls
164 lines (133 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from __future__ import annotations
from abc import ABCMeta, abstractmethod
from collections.abc import Iterable, Mapping
from typing import TYPE_CHECKING, Any, NamedTuple, Protocol
from jinja2 import BaseLoader, PackageLoader
from prompt_toolkit.styles import Style
from commitizen.exceptions import CommitMessageLengthExceededError
if TYPE_CHECKING:
import re
from collections.abc import Callable, Iterable, Mapping
from commitizen import git
from commitizen.config.base_config import BaseConfig
from commitizen.question import CzQuestion
class MessageBuilderHook(Protocol):
def __call__(
self, message: dict[str, Any], commit: git.GitCommit
) -> dict[str, Any] | Iterable[dict[str, Any]] | None: ...
class ChangelogReleaseHook(Protocol):
def __call__(
self, release: dict[str, Any], tag: git.GitTag | None
) -> dict[str, Any]: ...
class ValidationResult(NamedTuple):
is_valid: bool
errors: list
class BaseCommitizen(metaclass=ABCMeta):
bump_pattern: str | None = None
bump_map: dict[str, str] | None = None
bump_map_major_version_zero: dict[str, str] | None = None
default_style_config: list[tuple[str, str]] = [
("qmark", "fg:#ff9d00 bold"),
("question", "bold"),
("answer", "fg:#ff9d00 bold"),
("pointer", "fg:#ff9d00 bold"),
("highlighted", "fg:#ff9d00 bold"),
("selected", "fg:#cc5454"),
("separator", "fg:#cc5454"),
("instruction", ""),
("text", ""),
("disabled", "fg:#858585 italic"),
]
# The whole subject will be parsed as a message by default
# This allows supporting changelog for any rule system.
# It can be modified per rule
commit_parser: str | None = r"(?P<message>.*)"
changelog_pattern: str | None = r".*"
change_type_map: dict[str, str] | None = None
change_type_order: list[str] | None = None
# Executed per message parsed by the commitizen
changelog_message_builder_hook: MessageBuilderHook | None = None
# Executed only at the end of the changelog generation
changelog_hook: Callable[[str, str | None], str] | None = None
# Executed for each release in the changelog
changelog_release_hook: ChangelogReleaseHook | None = None
# Plugins can override templates and provide extra template data
template_loader: BaseLoader = PackageLoader("commitizen", "templates")
template_extras: dict[str, Any] = {}
def __init__(self, config: BaseConfig) -> None:
self.config = config
if not self.config.settings.get("style"):
self.config.settings.update({"style": BaseCommitizen.default_style_config})
@abstractmethod
def questions(self) -> list[CzQuestion]:
"""Questions regarding the commit message."""
@abstractmethod
def message(self, answers: Mapping[str, Any]) -> str:
"""Format your git message."""
@property
def style(self) -> Style:
return Style(
[
*BaseCommitizen.default_style_config,
*self.config.settings["style"],
]
)
@abstractmethod
def example(self) -> str:
"""Example of the commit message."""
@abstractmethod
def schema(self) -> str:
"""Schema definition of the commit message."""
@abstractmethod
def schema_pattern(self) -> str:
"""Regex matching the schema used for message validation."""
@abstractmethod
def info(self) -> str:
"""Information about the standardized commit message."""
def validate_commit_message(
self,
*,
commit_msg: str,
pattern: re.Pattern[str],
allow_abort: bool,
allowed_prefixes: list[str],
max_msg_length: int | None,
commit_hash: str,
) -> ValidationResult:
"""Validate commit message against the pattern."""
if not commit_msg:
return ValidationResult(
allow_abort, [] if allow_abort else ["commit message is empty"]
)
if any(map(commit_msg.startswith, allowed_prefixes)):
return ValidationResult(True, [])
if max_msg_length is not None and max_msg_length > 0:
msg_len = len(commit_msg.partition("\n")[0].strip())
if msg_len > max_msg_length:
# TODO: capitalize the first letter of the error message for consistency in v5
raise CommitMessageLengthExceededError(
f"commit validation: failed!\n"
f"commit message length exceeds the limit.\n"
f'commit "{commit_hash}": "{commit_msg}"\n'
f"message length limit: {max_msg_length} (actual: {msg_len})"
)
return ValidationResult(
bool(pattern.match(commit_msg)),
[f"pattern: {pattern.pattern}"],
)
def format_exception_message(
self, invalid_commits: list[tuple[git.GitCommit, list]]
) -> str:
"""Format commit errors."""
displayed_msgs_content = "\n".join(
[
f'commit "{commit.rev}": "{commit.message}\n"' + "\n".join(errors)
for commit, errors in invalid_commits
]
)
# TODO: capitalize the first letter of the error message for consistency in v5
return (
"commit validation: failed!\n"
"please enter a commit message in the commitizen format.\n"
f"{displayed_msgs_content}"
)