Skip to content

Commit 479cb09

Browse files
committed
feat(changelog): adds a changelog_release_hook called for each release in the changelog
1 parent 579205d commit 479cb09

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
lines changed

Diff for: commitizen/changelog.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
from commitizen import out
4545
from commitizen.bump import normalize_tag
46+
from commitizen.cz.base import ChangelogReleaseHook
4647
from commitizen.exceptions import InvalidConfigurationError, NoCommitsFoundError
4748
from commitizen.git import GitCommit, GitTag
4849
from commitizen.version_schemes import (
@@ -113,6 +114,7 @@ def generate_tree_from_commits(
113114
unreleased_version: str | None = None,
114115
change_type_map: dict[str, str] | None = None,
115116
changelog_message_builder_hook: MessageBuilderHook | None = None,
117+
changelog_release_hook: ChangelogReleaseHook | None = None,
116118
merge_prerelease: bool = False,
117119
scheme: VersionScheme = DEFAULT_SCHEME,
118120
) -> Iterable[dict]:
@@ -143,11 +145,14 @@ def generate_tree_from_commits(
143145
commit_tag, used_tags, merge_prerelease, scheme=scheme
144146
):
145147
used_tags.append(commit_tag)
146-
yield {
148+
release = {
147149
"version": current_tag_name,
148150
"date": current_tag_date,
149151
"changes": changes,
150152
}
153+
if changelog_release_hook:
154+
release = changelog_release_hook(release, commit_tag)
155+
yield release
151156
current_tag_name = commit_tag.name
152157
current_tag_date = commit_tag.date
153158
changes = defaultdict(list)
@@ -178,7 +183,14 @@ def generate_tree_from_commits(
178183
change_type_map,
179184
)
180185

181-
yield {"version": current_tag_name, "date": current_tag_date, "changes": changes}
186+
release = {
187+
"version": current_tag_name,
188+
"date": current_tag_date,
189+
"changes": changes,
190+
}
191+
if changelog_release_hook:
192+
release = changelog_release_hook(release, commit_tag)
193+
yield release
182194

183195

184196
def process_commit_message(

Diff for: commitizen/commands/changelog.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from commitizen import bump, changelog, defaults, factory, git, out
1010

1111
from commitizen.config import BaseConfig
12-
from commitizen.cz.base import MessageBuilderHook
12+
from commitizen.cz.base import MessageBuilderHook, ChangelogReleaseHook
1313
from commitizen.exceptions import (
1414
DryRunExit,
1515
NoCommitsFoundError,
@@ -154,6 +154,9 @@ def __call__(self):
154154
changelog_message_builder_hook: MessageBuilderHook | None = (
155155
self.cz.changelog_message_builder_hook
156156
)
157+
changelog_release_hook: ChangelogReleaseHook | None = (
158+
self.cz.changelog_release_hook
159+
)
157160
merge_prerelease = self.merge_prerelease
158161

159162
if self.export_template_to:
@@ -207,6 +210,7 @@ def __call__(self):
207210
unreleased_version,
208211
change_type_map=change_type_map,
209212
changelog_message_builder_hook=changelog_message_builder_hook,
213+
changelog_release_hook=changelog_release_hook,
210214
merge_prerelease=merge_prerelease,
211215
scheme=self.scheme,
212216
)

Diff for: commitizen/cz/base.py

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ def __call__(
1717
) -> dict[str, Any] | Iterable[dict[str, Any]] | None: ...
1818

1919

20+
class ChangelogReleaseHook(Protocol):
21+
def __call__(
22+
self, release: dict[str, Any], tag: git.GitTag | None
23+
) -> dict[str, Any]: ...
24+
25+
2026
class BaseCommitizen(metaclass=ABCMeta):
2127
bump_pattern: str | None = None
2228
bump_map: dict[str, str] | None = None
@@ -48,6 +54,9 @@ class BaseCommitizen(metaclass=ABCMeta):
4854
# Executed only at the end of the changelog generation
4955
changelog_hook: Callable[[str, str | None], str] | None = None
5056

57+
# Executed for each release in the changelog
58+
changelog_release_hook: ChangelogReleaseHook | None = None
59+
5160
# Plugins can override templates and provide extra template data
5261
template_loader: BaseLoader = PackageLoader("commitizen", "templates")
5362
template_extras: dict[str, Any] = {}

Diff for: docs/customization.md

+5
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ You can customize it of course, and this are the variables you need to add to yo
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 |
321321
| `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 |
323+
| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich a releases before they are rendered. Must return the update release
323324

324325
```python
325326
from commitizen.cz.base import BaseCommitizen
@@ -347,6 +348,10 @@ class StrangeCommitizen(BaseCommitizen):
347348
] = f"{m} {rev} [{commit.author}]({commit.author_email})"
348349
return parsed_message
349350
351+
def changelog_release_hook(self, release: dict, tag: git.GitTag) -> dict:
352+
release["author"] = tag.author
353+
return release
354+
350355
def changelog_hook(
351356
self, full_changelog: str, partial_changelog: Optional[str]
352357
) -> str:

Diff for: tests/commands/test_changelog_command.py

+22
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,28 @@ def test_changelog_hook_customize(mocker: MockFixture, config_customize):
323323
changelog_hook_mock.assert_called_with(full_changelog, full_changelog)
324324

325325

326+
@pytest.mark.usefixtures("tmp_commitizen_project")
327+
def test_changelog_release_hook(mocker: MockFixture, config):
328+
def changelog_release_hook(release: dict, tag: git.GitTag) -> dict:
329+
return release
330+
331+
for i in range(3):
332+
create_file_and_commit("feat: new file")
333+
create_file_and_commit("refactor: is in changelog")
334+
create_file_and_commit("Merge into master")
335+
git.tag(f"0.{i + 1}.0")
336+
337+
# changelog = Changelog(config, {})
338+
changelog = Changelog(
339+
config, {"unreleased_version": None, "incremental": True, "dry_run": False}
340+
)
341+
mocker.patch.object(changelog.cz, "changelog_release_hook", changelog_release_hook)
342+
spy = mocker.spy(changelog.cz, "changelog_release_hook")
343+
changelog()
344+
345+
assert spy.call_count == 3
346+
347+
326348
@pytest.mark.usefixtures("tmp_commitizen_project")
327349
def test_changelog_with_non_linear_merges_commit_order(
328350
mocker: MockFixture, config_customize

Diff for: tests/test_changelog.py

+20
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,26 @@ def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
14041404
), f"Line {no}: type {change_type} should have been overridden"
14051405

14061406

1407+
def test_render_changelog_with_changelog_release_hook(
1408+
gitcommits, tags, any_changelog_format: ChangelogFormat
1409+
):
1410+
def changelog_release_hook(release: dict, tag: git.GitTag | None) -> dict:
1411+
release["extra"] = "whatever"
1412+
return release
1413+
1414+
parser = ConventionalCommitsCz.commit_parser
1415+
changelog_pattern = ConventionalCommitsCz.changelog_pattern
1416+
tree = changelog.generate_tree_from_commits(
1417+
gitcommits,
1418+
tags,
1419+
parser,
1420+
changelog_pattern,
1421+
changelog_release_hook=changelog_release_hook,
1422+
)
1423+
for release in tree:
1424+
assert release["extra"] == "whatever"
1425+
1426+
14071427
def test_get_smart_tag_range_returns_an_extra_for_a_range(tags):
14081428
start, end = (
14091429
tags[0],

0 commit comments

Comments
 (0)