diff --git a/commitizen/changelog.py b/commitizen/changelog.py index ca36015ed..7bb9007cd 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -76,6 +76,8 @@ def generate_tree_from_commits( ) -> Iterable[Dict]: pat = re.compile(changelog_pattern) map_pat = re.compile(commit_parser, re.MULTILINE) + body_map_pat = re.compile(commit_parser, re.MULTILINE | re.DOTALL) + # Check if the latest commit is not tagged latest_commit = commits[0] current_tag: Optional[GitTag] = get_commit_tag(latest_commit, tags) @@ -110,8 +112,8 @@ def generate_tree_from_commits( if not matches: continue + # Process subject from commit message message = map_pat.match(commit.message) - message_body = map_pat.search(commit.body) if message: parsed_message: Dict = message.groupdict() # change_type becomes optional by providing None @@ -122,9 +124,18 @@ def generate_tree_from_commits( if changelog_message_builder_hook: parsed_message = changelog_message_builder_hook(parsed_message, commit) changes[change_type].append(parsed_message) - if message_body: + + # Process body from commit message + body_parts = commit.body.split("\n\n") + for body_part in body_parts: + message_body = body_map_pat.match(body_part) + if not message_body: + continue parsed_message_body: Dict = message_body.groupdict() + change_type = parsed_message_body.pop("change_type", None) + if change_type_map: + change_type = change_type_map.get(change_type, change_type) changes[change_type].append(parsed_message_body) yield {"version": current_tag_name, "date": current_tag_date, "changes": changes} diff --git a/setup.cfg b/setup.cfg index de0172c17..d4e21ad5b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,4 +35,4 @@ exclude = build, dist max-line-length = 88 -max-complexity = 11 +max-complexity = 12 diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index e5993dbdf..7448fee32 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -386,6 +386,30 @@ def test_breaking_change_content_v1(mocker, capsys): ) +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_breaking_change_content_v1_multiline(mocker, capsys): + commit_message = ( + "feat(users): email pattern corrected\n\n" + "body content\n\n" + "BREAKING CHANGE: migrate by renaming user to users.\n" + "and then connect the thingy with the other thingy\n\n" + "footer content" + ) + create_file_and_commit(commit_message) + testargs = ["cz", "changelog", "--dry-run"] + mocker.patch.object(sys, "argv", testargs) + with pytest.raises(DryRunExit): + cli.main() + out, _ = capsys.readouterr() + + assert out == ( + "## Unreleased\n\n### Feat\n\n- **users**: email pattern corrected\n\n" + "### BREAKING CHANGE\n\n- migrate by renaming user to users.\n" + "and then connect the thingy with the other thingy" + "\n\n" + ) + + @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_config_flag_increment(mocker, changelog_path, config_path):