Skip to content

Commit 8b1fad6

Browse files
benediktzieglerLee-W
authored andcommitted
feat: add custom validation
1 parent b78e303 commit 8b1fad6

File tree

4 files changed

+812
-36
lines changed

4 files changed

+812
-36
lines changed

commitizen/commands/check.py

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from commitizen import factory, git, out
88
from commitizen.exceptions import (
9-
CommitMessageLengthExceededError,
109
InvalidCommandArgumentError,
1110
InvalidCommitMessageError,
1211
NoCommitsFoundError,
@@ -83,26 +82,32 @@ def __call__(self) -> None:
8382
"""Validate if commit messages follows the conventional pattern.
8483
8584
Raises:
86-
InvalidCommitMessageError: if the commit provided not follows the conventional pattern
85+
InvalidCommitMessageError: if the commit provided does not follow the conventional pattern
8786
NoCommitsFoundError: if no commit is found with the given range
8887
"""
8988
commits = self._get_commits()
9089
if not commits:
9190
raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'")
9291

9392
pattern = re.compile(self.cz.schema_pattern())
94-
invalid_msgs_content = "\n".join(
95-
f'commit "{commit.rev}": "{commit.message}"'
93+
invalid_commits = [
94+
(commit, check.errors)
9695
for commit in commits
97-
if not self._validate_commit_message(commit.message, pattern, commit.rev)
98-
)
99-
if invalid_msgs_content:
100-
# TODO: capitalize the first letter of the error message for consistency in v5
96+
if not (
97+
check := self.cz.validate_commit_message(
98+
commit_msg=commit.message,
99+
pattern=pattern,
100+
allow_abort=self.allow_abort,
101+
allowed_prefixes=self.allowed_prefixes,
102+
max_msg_length=self.max_msg_length,
103+
commit_hash=commit.rev,
104+
)
105+
).is_valid
106+
]
107+
108+
if invalid_commits:
101109
raise InvalidCommitMessageError(
102-
"commit validation: failed!\n"
103-
"please enter a commit message in the commitizen format.\n"
104-
f"{invalid_msgs_content}\n"
105-
f"pattern: {pattern.pattern}"
110+
self.cz.format_exception_message(invalid_commits)
106111
)
107112
out.success("Commit validation: successful!")
108113

@@ -157,24 +162,3 @@ def _filter_comments(msg: str) -> str:
157162
if not line.startswith("#"):
158163
lines.append(line)
159164
return "\n".join(lines)
160-
161-
def _validate_commit_message(
162-
self, commit_msg: str, pattern: re.Pattern[str], commit_hash: str
163-
) -> bool:
164-
if not commit_msg:
165-
return self.allow_abort
166-
167-
if any(map(commit_msg.startswith, self.allowed_prefixes)):
168-
return True
169-
170-
if self.max_msg_length is not None:
171-
msg_len = len(commit_msg.partition("\n")[0].strip())
172-
if msg_len > self.max_msg_length:
173-
raise CommitMessageLengthExceededError(
174-
f"commit validation: failed!\n"
175-
f"commit message length exceeds the limit.\n"
176-
f'commit "{commit_hash}": "{commit_msg}"\n'
177-
f"message length limit: {self.max_msg_length} (actual: {msg_len})"
178-
)
179-
180-
return bool(pattern.match(commit_msg))

commitizen/cz/base.py

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
from __future__ import annotations
22

33
from abc import ABCMeta, abstractmethod
4-
from typing import TYPE_CHECKING, Any, Callable, Protocol
4+
from collections.abc import Iterable, Mapping
5+
from typing import TYPE_CHECKING, Any, Callable, NamedTuple, Protocol
56

67
from jinja2 import BaseLoader, PackageLoader
78
from prompt_toolkit.styles import Style
89

10+
from commitizen.exceptions import CommitMessageLengthExceededError
11+
912
if TYPE_CHECKING:
13+
import re
1014
from collections.abc import Iterable, Mapping
1115

1216
from commitizen import git
@@ -26,6 +30,11 @@ def __call__(
2630
) -> dict[str, Any]: ...
2731

2832

33+
class ValidationResult(NamedTuple):
34+
is_valid: bool
35+
errors: list
36+
37+
2938
class BaseCommitizen(metaclass=ABCMeta):
3039
bump_pattern: str | None = None
3140
bump_map: dict[str, str] | None = None
@@ -43,7 +52,7 @@ class BaseCommitizen(metaclass=ABCMeta):
4352
("disabled", "fg:#858585 italic"),
4453
]
4554

46-
# The whole subject will be parsed as message by default
55+
# The whole subject will be parsed as a message by default
4756
# This allows supporting changelog for any rule system.
4857
# It can be modified per rule
4958
commit_parser: str | None = r"(?P<message>.*)"
@@ -101,3 +110,55 @@ def schema_pattern(self) -> str:
101110
@abstractmethod
102111
def info(self) -> str:
103112
"""Information about the standardized commit message."""
113+
114+
def validate_commit_message(
115+
self,
116+
*,
117+
commit_msg: str,
118+
pattern: re.Pattern[str],
119+
allow_abort: bool,
120+
allowed_prefixes: list[str],
121+
max_msg_length: int | None,
122+
commit_hash: str,
123+
) -> ValidationResult:
124+
"""Validate commit message against the pattern."""
125+
if not commit_msg:
126+
return ValidationResult(
127+
allow_abort, [] if allow_abort else ["commit message is empty"]
128+
)
129+
130+
if any(map(commit_msg.startswith, allowed_prefixes)):
131+
return ValidationResult(True, [])
132+
133+
if max_msg_length is not None:
134+
msg_len = len(commit_msg.partition("\n")[0].strip())
135+
if msg_len > max_msg_length:
136+
# TODO: capitalize the first letter of the error message for consistency in v5
137+
raise CommitMessageLengthExceededError(
138+
f"commit validation: failed!\n"
139+
f"commit message length exceeds the limit.\n"
140+
f'commit "{commit_hash}": "{commit_msg}"\n'
141+
f"message length limit: {max_msg_length} (actual: {msg_len})"
142+
)
143+
144+
return ValidationResult(
145+
bool(pattern.match(commit_msg)),
146+
[f"pattern: {pattern.pattern}"],
147+
)
148+
149+
def format_exception_message(
150+
self, invalid_commits: list[tuple[git.GitCommit, list]]
151+
) -> str:
152+
"""Format commit errors."""
153+
displayed_msgs_content = "\n".join(
154+
[
155+
f'commit "{commit.rev}": "{commit.message}\n"' + "\n".join(errors)
156+
for commit, errors in invalid_commits
157+
]
158+
)
159+
# TODO: capitalize the first letter of the error message for consistency in v5
160+
return (
161+
"commit validation: failed!\n"
162+
"please enter a commit message in the commitizen format.\n"
163+
f"{displayed_msgs_content}"
164+
)

0 commit comments

Comments
 (0)