Skip to content

Commit be4ccc3

Browse files
authored
Merge branch 'v4-11-0' into feature/single-number-version
2 parents 6ad0bf1 + 2072f8e commit be4ccc3

File tree

18 files changed

+1052
-368
lines changed

18 files changed

+1052
-368
lines changed

.github/workflows/pythonpackage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
python-check:
77
strategy:
88
matrix:
9-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
9+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
1010
platform: [ubuntu-22.04, macos-latest, windows-latest]
1111
runs-on: ${{ matrix.platform }}
1212
steps:

commitizen/changelog_formats/__init__.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
from __future__ import annotations
22

3-
import sys
4-
from typing import TYPE_CHECKING, Callable, ClassVar, Protocol
5-
6-
if sys.version_info >= (3, 10):
7-
from importlib import metadata
8-
else:
9-
import importlib_metadata as metadata
3+
from importlib import metadata
4+
from typing import TYPE_CHECKING, ClassVar, Protocol
105

116
from commitizen.exceptions import ChangelogFormatUnknown
127

138
if TYPE_CHECKING:
9+
from collections.abc import Callable
10+
1411
from commitizen.changelog import Metadata
1512
from commitizen.config.base_config import BaseConfig
1613

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/__init__.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,9 @@
22

33
import importlib
44
import pkgutil
5-
import sys
65
import warnings
7-
8-
if sys.version_info >= (3, 10):
9-
from importlib import metadata
10-
else:
11-
import importlib_metadata as metadata
12-
6+
from collections.abc import Iterable
7+
from importlib import metadata
138
from typing import TYPE_CHECKING
149

1510
if TYPE_CHECKING:

commitizen/cz/base.py

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
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, 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:
10-
from collections.abc import Iterable, Mapping
13+
import re
14+
from collections.abc import Callable, Iterable, Mapping
1115

1216
from commitizen import git
1317
from commitizen.config.base_config import BaseConfig
@@ -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+
)

commitizen/providers/__init__.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
from __future__ import annotations
22

3-
import sys
3+
from importlib import metadata
44
from typing import TYPE_CHECKING, cast
55

6-
if sys.version_info >= (3, 10):
7-
from importlib import metadata
8-
else:
9-
import importlib_metadata as metadata
10-
6+
from commitizen.config.base_config import BaseConfig
117
from commitizen.exceptions import VersionProviderUnknown
128
from commitizen.providers.cargo_provider import CargoProvider
139
from commitizen.providers.commitizen_provider import CommitizenProvider

commitizen/question.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Callable, Literal, TypedDict, Union
1+
from collections.abc import Callable
2+
from typing import Literal, TypedDict
23

34

45
class Choice(TypedDict, total=False):
@@ -29,4 +30,4 @@ class ConfirmQuestion(TypedDict):
2930
default: bool
3031

3132

32-
CzQuestion = Union[ListQuestion, InputQuestion, ConfirmQuestion]
33+
CzQuestion = ListQuestion | InputQuestion | ConfirmQuestion

commitizen/version_schemes.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from __future__ import annotations
22

33
import re
4-
import sys
54
import warnings
5+
from importlib import metadata
66
from itertools import zip_longest
77
from typing import (
88
TYPE_CHECKING,
@@ -14,23 +14,15 @@
1414
runtime_checkable,
1515
)
1616

17-
if sys.version_info >= (3, 10):
18-
from importlib import metadata
19-
else:
20-
import importlib_metadata as metadata
21-
2217
from packaging.version import InvalidVersion # noqa: F401 (expose the common exception)
2318
from packaging.version import Version as _BaseVersion
2419

2520
from commitizen.defaults import MAJOR, MINOR, PATCH, Settings
2621
from commitizen.exceptions import VersionSchemeUnknown
2722

2823
if TYPE_CHECKING:
29-
# TypeAlias is Python 3.10+ but backported in typing-extensions
30-
if sys.version_info >= (3, 10):
31-
from typing import TypeAlias
32-
else:
33-
from typing_extensions import TypeAlias
24+
import sys
25+
from typing import TypeAlias
3426

3527
# Self is Python 3.11+ but backported in typing-extensions
3628
if sys.version_info < (3, 11):

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ This standardization makes your commit history more readable and meaningful, whi
5555

5656
Before installing Commitizen, ensure you have:
5757

58-
- [Python](https://www.python.org/downloads/) `3.9+`
58+
- [Python](https://www.python.org/downloads/) `3.10+`
5959
- [Git][gitscm] `1.8.5.2+`
6060

6161
### Installation

docs/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ If you're a first-time contributor, please check out issues labeled [good first
1515
### Required Tools
1616

1717
1. **Python Environment**
18-
- Python `>=3.9`
18+
- Python `>=3.10`
1919
- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.2.0`
2020
2. **Version Control & Security**
2121
- Git

0 commit comments

Comments
 (0)