Skip to content

Commit 4dea3fa

Browse files
committed
refactor(bump_rule): add find_increment_by_callable
1 parent 03aa596 commit 4dea3fa

File tree

3 files changed

+106
-2
lines changed

3 files changed

+106
-2
lines changed

commitizen/bump.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
logger = getLogger("commitizen")
1919

2020

21+
# TODO: replace this with find_increment_by_callable?
2122
def find_increment(
2223
commits: list[GitCommit], regex: str, increments_map: dict | OrderedDict
2324
) -> Increment | None:

commitizen/bump_rule.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,41 @@
22

33
import re
44
from functools import cached_property
5-
from typing import Protocol
5+
from typing import Callable, Protocol
66

77
from commitizen.version_schemes import Increment
88

9+
_VERSION_ORDERING = dict(zip((None, "PATCH", "MINOR", "MAJOR"), range(4)))
10+
11+
12+
def find_increment_by_callable(
13+
commit_messages: list[str], get_increment: Callable[[str], Increment | None]
14+
) -> Increment | None:
15+
"""Find the highest version increment from a list of messages.
16+
17+
This function processes a list of messages and determines the highest version
18+
increment needed based on the commit messages. It splits multi-line commit messages
19+
and evaluates each line using the provided get_increment callable.
20+
21+
Args:
22+
commit_messages: A list of messages to analyze.
23+
get_increment: A callable that takes a commit message string and returns an
24+
Increment value (MAJOR, MINOR, PATCH) or None if no increment is needed.
25+
26+
Returns:
27+
The highest version increment needed (MAJOR, MINOR, PATCH) or None if no
28+
increment is needed. The order of precedence is MAJOR > MINOR > PATCH.
29+
30+
Example:
31+
>>> commit_messages = ["feat: new feature", "fix: bug fix"]
32+
>>> rule = DefaultBumpRule()
33+
>>> find_increment_by_callable(commit_messages, lambda x: rule.get_increment(x, False))
34+
'MINOR'
35+
"""
36+
lines = (line for message in commit_messages for line in message.split("\n"))
37+
increments = map(get_increment, lines)
38+
return max(increments, key=lambda x: _VERSION_ORDERING[x], default=None)
39+
940

1041
class BumpRule(Protocol):
1142
def get_increment(

tests/test_bump_rule.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from commitizen.bump_rule import DefaultBumpRule
3+
from commitizen.bump_rule import DefaultBumpRule, find_increment_by_callable
44
from commitizen.defaults import MAJOR, MINOR, PATCH
55

66

@@ -93,3 +93,75 @@ def test_other_commit_types(self, bump_rule):
9393
assert bump_rule.get_increment("test: add unit tests", False) is None
9494
assert bump_rule.get_increment("build: update build config", False) is None
9595
assert bump_rule.get_increment("ci: update CI pipeline", False) is None
96+
97+
98+
class TestFindIncrementByCallable:
99+
@pytest.fixture
100+
def get_increment(self, bump_rule):
101+
return lambda x: bump_rule.get_increment(x, False)
102+
103+
def test_single_commit(self, get_increment):
104+
commit_messages = ["feat: add new feature"]
105+
assert find_increment_by_callable(commit_messages, get_increment) == MINOR
106+
107+
def test_multiple_commits(self, get_increment):
108+
commit_messages = [
109+
"feat: new feature",
110+
"fix: bug fix",
111+
"docs: update readme",
112+
]
113+
assert find_increment_by_callable(commit_messages, get_increment) == MINOR
114+
115+
def test_breaking_change(self, get_increment):
116+
commit_messages = [
117+
"feat: new feature",
118+
"feat!: breaking change",
119+
]
120+
assert find_increment_by_callable(commit_messages, get_increment) == MINOR
121+
122+
def test_multi_line_commit(self, get_increment):
123+
commit_messages = [
124+
"feat: new feature\n\nBREAKING CHANGE: major change",
125+
]
126+
assert find_increment_by_callable(commit_messages, get_increment) == MINOR
127+
128+
def test_no_increment_needed(self, get_increment):
129+
commit_messages = [
130+
"docs: update documentation",
131+
"style: format code",
132+
]
133+
assert find_increment_by_callable(commit_messages, get_increment) is None
134+
135+
def test_empty_commits(self, get_increment):
136+
commit_messages = []
137+
assert find_increment_by_callable(commit_messages, get_increment) is None
138+
139+
def test_major_version_zero(self):
140+
bump_rule = DefaultBumpRule()
141+
142+
commit_messages = [
143+
"feat!: breaking change",
144+
"BREAKING CHANGE: major change",
145+
]
146+
assert (
147+
find_increment_by_callable(
148+
commit_messages, lambda x: bump_rule.get_increment(x, True)
149+
)
150+
== MAJOR
151+
)
152+
153+
def test_mixed_commit_types(self, get_increment):
154+
commit_messages = [
155+
"feat: new feature",
156+
"fix: bug fix",
157+
"perf: improve performance",
158+
"refactor: restructure code",
159+
]
160+
assert find_increment_by_callable(commit_messages, get_increment) == MINOR
161+
162+
def test_commit_with_scope(self, get_increment):
163+
commit_messages = [
164+
"feat(api): add new endpoint",
165+
"fix(ui): fix button alignment",
166+
]
167+
assert find_increment_by_callable(commit_messages, get_increment) == MINOR

0 commit comments

Comments
 (0)