Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ec2da06

Browse files
feat(changelog): changelog_message_build_hook can now generate multiple changelog entries from a single commit (#1003)
1 parent 41f9b82 commit ec2da06

File tree

4 files changed

+66
-30
lines changed

4 files changed

+66
-30
lines changed

‎commitizen/changelog.py‎

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from collections import OrderedDict, defaultdict
3232
from dataclasses import dataclass
3333
from datetime import date
34-
from typing import TYPE_CHECKING, Callable, Iterable
34+
from typing import TYPE_CHECKING, Iterable
3535

3636
from jinja2 import (
3737
BaseLoader,
@@ -52,6 +52,7 @@
5252
)
5353

5454
if TYPE_CHECKING:
55+
from commitizen.cz.base import MessageBuilderHook
5556
from commitizen.version_schemes import VersionScheme
5657

5758

@@ -111,7 +112,7 @@ def generate_tree_from_commits(
111112
changelog_pattern: str,
112113
unreleased_version: str | None = None,
113114
change_type_map: dict[str, str] | None = None,
114-
changelog_message_builder_hook: Callable | None = None,
115+
changelog_message_builder_hook: MessageBuilderHook | None = None,
115116
merge_prerelease: bool = False,
116117
scheme: VersionScheme = DEFAULT_SCHEME,
117118
) -> Iterable[dict]:
@@ -156,39 +157,48 @@ def generate_tree_from_commits(
156157
continue
157158

158159
# Process subject from commit message
159-
message = map_pat.match(commit.message)
160-
if message:
161-
parsed_message: dict = message.groupdict()
162-
163-
if changelog_message_builder_hook:
164-
parsed_message = changelog_message_builder_hook(parsed_message, commit)
165-
if parsed_message:
166-
change_type = parsed_message.pop("change_type", None)
167-
if change_type_map:
168-
change_type = change_type_map.get(change_type, change_type)
169-
changes[change_type].append(parsed_message)
160+
if message := map_pat.match(commit.message):
161+
process_commit_message(
162+
changelog_message_builder_hook,
163+
message,
164+
commit,
165+
changes,
166+
change_type_map,
167+
)
170168

171169
# Process body from commit message
172170
body_parts = commit.body.split("\n\n")
173171
for body_part in body_parts:
174-
message_body = body_map_pat.match(body_part)
175-
if not message_body:
176-
continue
177-
parsed_message_body: dict = message_body.groupdict()
178-
179-
if changelog_message_builder_hook:
180-
parsed_message_body = changelog_message_builder_hook(
181-
parsed_message_body, commit
172+
if message := body_map_pat.match(body_part):
173+
process_commit_message(
174+
changelog_message_builder_hook,
175+
message,
176+
commit,
177+
changes,
178+
change_type_map,
182179
)
183-
if parsed_message_body:
184-
change_type = parsed_message_body.pop("change_type", None)
185-
if change_type_map:
186-
change_type = change_type_map.get(change_type, change_type)
187-
changes[change_type].append(parsed_message_body)
188180

189181
yield {"version": current_tag_name, "date": current_tag_date, "changes": changes}
190182

191183

184+
def process_commit_message(
185+
hook: MessageBuilderHook | None,
186+
parsed: re.Match[str],
187+
commit: GitCommit,
188+
changes: dict[str | None, list],
189+
change_type_map: dict[str, str] | None = None,
190+
):
191+
message: dict = parsed.groupdict()
192+
193+
if processed := hook(message, commit) if hook else message:
194+
messages = [processed] if isinstance(processed, dict) else processed
195+
for msg in messages:
196+
change_type = msg.pop("change_type", None)
197+
if change_type_map:
198+
change_type = change_type_map.get(change_type, change_type)
199+
changes[change_type].append(msg)
200+
201+
192202
def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable:
193203
if len(set(change_type_order)) != len(change_type_order):
194204
raise InvalidConfigurationError(

‎commitizen/cz/base.py‎

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

33
from abc import ABCMeta, abstractmethod
4-
from typing import Any, Callable, Protocol
4+
from typing import Any, Callable, Iterable, Protocol
55

66
from jinja2 import BaseLoader, PackageLoader
77
from prompt_toolkit.styles import Style, merge_styles
@@ -14,7 +14,7 @@
1414
class MessageBuilderHook(Protocol):
1515
def __call__(
1616
self, message: dict[str, Any], commit: git.GitCommit
17-
) -> dict[str, Any] | None: ...
17+
) -> dict[str, Any] | Iterable[dict[str, Any]] |None: ...
1818

1919

2020
class BaseCommitizen(metaclass=ABCMeta):

‎docs/customization.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ You can customize it of course, and this are the variables you need to add to yo
318318
| `commit_parser` | `str` | NO | Regex which should provide the variables explained in the [changelog description][changelog-des] |
319319
| `changelog_pattern` | `str` | NO | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern |
320320
| `change_type_map` | `dict` | NO | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided |
321-
| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. |
321+
| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. |
322322
| `changelog_hook` | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog |
323323

324324
```python
@@ -339,7 +339,7 @@ class StrangeCommitizen(BaseCommitizen):
339339
340340
def changelog_message_builder_hook(
341341
self, parsed_message: dict, commit: git.GitCommit
342-
) -> dict | None:
342+
) -> dict | list | None:
343343
rev = commit.rev
344344
m = parsed_message["message"]
345345
parsed_message[

‎tests/test_changelog.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,32 @@ def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
13471347
assert RE_HEADER.match(line), f"Line {no} should not be there: {line}"
13481348

13491349

1350+
def test_render_changelog_with_changelog_message_builder_hook_multiple_entries(
1351+
gitcommits, tags, any_changelog_format: ChangelogFormat
1352+
):
1353+
def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
1354+
messages = [message.copy(), message.copy(), message.copy()]
1355+
for idx, msg in enumerate(messages):
1356+
msg["message"] = "Message #{idx}"
1357+
return messages
1358+
1359+
parser = ConventionalCommitsCz.commit_parser
1360+
changelog_pattern = ConventionalCommitsCz.changelog_pattern
1361+
loader = ConventionalCommitsCz.template_loader
1362+
template = any_changelog_format.template
1363+
tree = changelog.generate_tree_from_commits(
1364+
gitcommits,
1365+
tags,
1366+
parser,
1367+
changelog_pattern,
1368+
changelog_message_builder_hook=changelog_message_builder_hook,
1369+
)
1370+
result = changelog.render_changelog(tree, loader, template)
1371+
1372+
for idx in range(3):
1373+
assert "Message #{idx}" in result
1374+
1375+
13501376
def test_changelog_message_builder_hook_can_access_and_modify_change_type(
13511377
gitcommits, tags, any_changelog_format: ChangelogFormat
13521378
):

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /