|
9 | 9 | import pytest
|
10 | 10 | from pytest_mock import MockerFixture
|
11 | 11 |
|
12 |
| -from commitizen import cmd, defaults |
| 12 | +from commitizen import cmd, defaults, git |
13 | 13 | from commitizen.changelog_formats import (
|
14 | 14 | ChangelogFormat,
|
15 | 15 | get_changelog_format,
|
@@ -231,6 +231,78 @@ def mock_plugin(mocker: MockerFixture, config: BaseConfig) -> BaseCommitizen:
|
231 | 231 | return mock
|
232 | 232 |
|
233 | 233 |
|
| 234 | +class ValidationCz(BaseCommitizen): |
| 235 | + def questions(self): |
| 236 | + return [ |
| 237 | + {"type": "input", "name": "commit", "message": "Initial commit:\n"}, |
| 238 | + {"type": "input", "name": "issue_nb", "message": "ABC-123"}, |
| 239 | + ] |
| 240 | + |
| 241 | + def message(self, answers: dict): |
| 242 | + return f"{answers['issue_nb']}: {answers['commit']}" |
| 243 | + |
| 244 | + def schema(self): |
| 245 | + return "<issue_nb>: <commit>" |
| 246 | + |
| 247 | + def schema_pattern(self): |
| 248 | + return r"^(?P<issue_nb>[A-Z]{3}-\d+): (?P<commit>.*)$" |
| 249 | + |
| 250 | + def validate_commit_message( |
| 251 | + self, |
| 252 | + commit_msg: str, |
| 253 | + pattern: str | None, |
| 254 | + allow_abort: bool, |
| 255 | + allowed_prefixes: list[str], |
| 256 | + max_msg_length: int, |
| 257 | + ) -> tuple[bool, list]: |
| 258 | + """Validate commit message against the pattern.""" |
| 259 | + if not commit_msg: |
| 260 | + return allow_abort, [] if allow_abort else ["commit message is empty"] |
| 261 | + |
| 262 | + if pattern is None: |
| 263 | + return True, [] |
| 264 | + |
| 265 | + if any(map(commit_msg.startswith, allowed_prefixes)): |
| 266 | + return True, [] |
| 267 | + if max_msg_length: |
| 268 | + msg_len = len(commit_msg.partition("\n")[0].strip()) |
| 269 | + if msg_len > max_msg_length: |
| 270 | + return False, [ |
| 271 | + f"commit message is too long. Max length is {max_msg_length}" |
| 272 | + ] |
| 273 | + pattern_match = bool(re.match(pattern, commit_msg)) |
| 274 | + if not pattern_match: |
| 275 | + return False, [f"commit message does not match pattern {pattern}"] |
| 276 | + return True, [] |
| 277 | + |
| 278 | + def format_exception_message( |
| 279 | + self, ill_formated_commits: list[tuple[git.GitCommit, list]] |
| 280 | + ) -> str: |
| 281 | + """Format commit errors.""" |
| 282 | + displayed_msgs_content = "\n".join( |
| 283 | + [ |
| 284 | + ( |
| 285 | + f'commit "{commit.rev}": "{commit.message}"\n' |
| 286 | + f"errors:\n" |
| 287 | + "\n".join(f"- {error}" for error in errors) |
| 288 | + ) |
| 289 | + for (commit, errors) in ill_formated_commits |
| 290 | + ] |
| 291 | + ) |
| 292 | + return ( |
| 293 | + "commit validation: failed!\n" |
| 294 | + "please enter a commit message in the commitizen format.\n" |
| 295 | + f"{displayed_msgs_content}\n" |
| 296 | + f"pattern: {self.schema_pattern}" |
| 297 | + ) |
| 298 | + |
| 299 | + |
| 300 | +@pytest.fixture |
| 301 | +def use_cz_custom_validator(mocker): |
| 302 | + new_cz = {**registry, "cz_custom_validator": ValidationCz} |
| 303 | + mocker.patch.dict("commitizen.cz.registry", new_cz) |
| 304 | + |
| 305 | + |
234 | 306 | SUPPORTED_FORMATS = ("markdown", "textile", "asciidoc", "restructuredtext")
|
235 | 307 |
|
236 | 308 |
|
|
0 commit comments