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 f72828a

Browse files
feat(changelog): adds a changelog_release_hook called for each release in the changelog (#1018)
1 parent 5d64224 commit f72828a

File tree

6 files changed

+76
-3
lines changed

6 files changed

+76
-3
lines changed

‎commitizen/changelog.py‎

Lines changed: 14 additions & 2 deletions
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(

‎commitizen/commands/changelog.py‎

Lines changed: 5 additions & 1 deletion
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,
@@ -150,6 +150,9 @@ def __call__(self):
150150
changelog_message_builder_hook: MessageBuilderHook | None = (
151151
self.cz.changelog_message_builder_hook
152152
)
153+
changelog_release_hook: ChangelogReleaseHook | None = (
154+
self.cz.changelog_release_hook
155+
)
153156
merge_prerelease = self.merge_prerelease
154157

155158
if self.export_template_to:
@@ -203,6 +206,7 @@ def __call__(self):
203206
unreleased_version,
204207
change_type_map=change_type_map,
205208
changelog_message_builder_hook=changelog_message_builder_hook,
209+
changelog_release_hook=changelog_release_hook,
206210
merge_prerelease=merge_prerelease,
207211
scheme=self.scheme,
208212
)

‎commitizen/cz/base.py‎

Lines changed: 9 additions & 0 deletions
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] = {}

‎docs/customization.md‎

Lines changed: 5 additions & 0 deletions
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:

‎tests/commands/test_changelog_command.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,28 @@ def test_changelog_hook_customize(mocker: MockFixture, config_customize):
327327
changelog_hook_mock.assert_called_with(full_changelog, full_changelog)
328328

329329

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

‎tests/test_changelog.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22

33
from pathlib import Path
4+
from typing import Optional
45

56
import pytest
67
from jinja2 import FileSystemLoader
@@ -1404,6 +1405,26 @@ def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
14041405
), f"Line {no}: type {change_type} should have been overridden"
14051406

14061407

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

0 commit comments

Comments
(0)

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