Skip to content

Commit 39f8bc7

Browse files
committed
fix(bump_rule): support flexible bump map
1 parent a4d6eaa commit 39f8bc7

File tree

2 files changed

+108
-10
lines changed

2 files changed

+108
-10
lines changed

commitizen/bump_rule.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ def find_increment_by_callable(
3737
"""
3838
lines = (line for message in commit_messages for line in message.split("\n"))
3939
increments = map(get_increment, lines)
40+
return _find_highest_increment(increments)
41+
42+
43+
def _find_highest_increment(increments: Iterable[Increment | None]) -> Increment | None:
4044
return max(increments, key=lambda x: _VERSION_ORDERING[x], default=None)
4145

4246

@@ -136,17 +140,16 @@ def get_increment(
136140
self.bump_map_major_version_zero if major_version_zero else self.bump_map
137141
)
138142

139-
# TODO: need more flexibility for the pattern
140-
# "refactor!: drop support for Python 2.7" => MAJOR
141-
# bump_pattern = r"^((?P<major>major)|(?P<minor>minor)|(?P<patch>patch))(?P<scope>\(.+\))?(?P<bang>!)?:"
142-
# bump_map = {
143-
# "major": "MAJOR",
144-
# "bang": "MAJOR",
145-
# "minor": "MINOR",
146-
# "patch": "PATCH",
147-
# }
148-
# bump_map_major_version_zero = { ... }
143+
try:
144+
if ret := _find_highest_increment(
145+
(increment for name, increment in bump_map.items() if m.group(name))
146+
):
147+
return ret
148+
except IndexError:
149+
# Fallback to old school bump rule
150+
pass
149151

152+
# Fallback to old school bump rule
150153
found_keyword = m.group(1)
151154
for match_pattern, increment in bump_map.items():
152155
if re.match(match_pattern, found_keyword):

tests/test_bump_rule.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from commitizen.bump_rule import (
44
ConventionalCommitBumpRule,
55
OldSchoolBumpRule,
6+
_find_highest_increment,
67
find_increment_by_callable,
78
)
89
from commitizen.defaults import (
@@ -106,6 +107,38 @@ def test_other_commit_types(self, bump_rule):
106107
assert bump_rule.get_increment("build: update build config", False) is None
107108
assert bump_rule.get_increment("ci: update CI pipeline", False) is None
108109

110+
def test_breaking_change_with_refactor(self, bump_rule):
111+
"""Test breaking changes with refactor type commit messages."""
112+
# Breaking change with refactor type
113+
assert (
114+
bump_rule.get_increment("refactor!: drop support for Python 2.7", False)
115+
== MAJOR
116+
)
117+
assert (
118+
bump_rule.get_increment("refactor!: drop support for Python 2.7", True)
119+
== MINOR
120+
)
121+
122+
# Breaking change with refactor type and scope
123+
assert (
124+
bump_rule.get_increment(
125+
"refactor(api)!: remove deprecated endpoints", False
126+
)
127+
== MAJOR
128+
)
129+
assert (
130+
bump_rule.get_increment("refactor(api)!: remove deprecated endpoints", True)
131+
== MINOR
132+
)
133+
134+
# Regular refactor (should be PATCH)
135+
assert (
136+
bump_rule.get_increment("refactor: improve code structure", False) == PATCH
137+
)
138+
assert (
139+
bump_rule.get_increment("refactor: improve code structure", True) == PATCH
140+
)
141+
109142

110143
class TestFindIncrementByCallable:
111144
@pytest.fixture
@@ -291,6 +324,38 @@ def test_with_find_increment_by_callable(self, old_school_rule):
291324
== MAJOR
292325
)
293326

327+
def test_flexible_bump_map(self, old_school_rule):
328+
"""Test that _find_highest_increment is used correctly in bump map processing."""
329+
# Test with multiple matching patterns
330+
pattern = r"^((?P<major>major)|(?P<minor>minor)|(?P<patch>patch))(?P<scope>\(.+\))?(?P<bang>!)?:"
331+
bump_map = {
332+
"major": MAJOR,
333+
"bang": MAJOR,
334+
"minor": MINOR,
335+
"patch": PATCH,
336+
}
337+
bump_map_major_version_zero = {
338+
"major": MINOR,
339+
"bang": MINOR,
340+
"minor": MINOR,
341+
"patch": PATCH,
342+
}
343+
rule = OldSchoolBumpRule(pattern, bump_map, bump_map_major_version_zero)
344+
345+
# Test with multiple version tags
346+
assert rule.get_increment("major!: drop support for Python 2.7", False) == MAJOR
347+
assert rule.get_increment("major!: drop support for Python 2.7", True) == MINOR
348+
assert rule.get_increment("major: drop support for Python 2.7", False) == MAJOR
349+
assert rule.get_increment("major: drop support for Python 2.7", True) == MINOR
350+
assert rule.get_increment("patch!: drop support for Python 2.7", False) == MAJOR
351+
assert rule.get_increment("patch!: drop support for Python 2.7", True) == MINOR
352+
assert rule.get_increment("patch: drop support for Python 2.7", False) == PATCH
353+
assert rule.get_increment("patch: drop support for Python 2.7", True) == PATCH
354+
assert rule.get_increment("minor: add new feature", False) == MINOR
355+
assert rule.get_increment("minor: add new feature", True) == MINOR
356+
assert rule.get_increment("patch: fix bug", False) == PATCH
357+
assert rule.get_increment("patch: fix bug", True) == PATCH
358+
294359

295360
class TestOldSchoolBumpRuleWithDefault:
296361
@pytest.fixture
@@ -378,3 +443,33 @@ def test_with_find_increment_by_callable(self, old_school_rule):
378443
)
379444
== MAJOR
380445
)
446+
447+
448+
def test_find_highest_increment():
449+
"""Test the _find_highest_increment function."""
450+
# Test with single increment
451+
assert _find_highest_increment([MAJOR]) == MAJOR
452+
assert _find_highest_increment([MINOR]) == MINOR
453+
assert _find_highest_increment([PATCH]) == PATCH
454+
455+
# Test with multiple increments
456+
assert _find_highest_increment([PATCH, MINOR, MAJOR]) == MAJOR
457+
assert _find_highest_increment([PATCH, MINOR]) == MINOR
458+
assert _find_highest_increment([PATCH, PATCH]) == PATCH
459+
460+
# Test with None values
461+
assert _find_highest_increment([None, PATCH]) == PATCH
462+
assert _find_highest_increment([None, None]) is None
463+
assert _find_highest_increment([]) is None
464+
465+
# Test with mixed values
466+
assert _find_highest_increment([None, PATCH, MINOR, MAJOR]) == MAJOR
467+
assert _find_highest_increment([None, PATCH, MINOR]) == MINOR
468+
assert _find_highest_increment([None, PATCH]) == PATCH
469+
470+
# Test with empty iterator
471+
assert _find_highest_increment(iter([])) is None
472+
473+
# Test with generator expression
474+
assert _find_highest_increment(x for x in [PATCH, MINOR, MAJOR]) == MAJOR
475+
assert _find_highest_increment(x for x in [None, PATCH, MINOR]) == MINOR

0 commit comments

Comments
 (0)