Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions commitizen/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from collections import OrderedDict, defaultdict
from dataclasses import dataclass
from datetime import date
from enum import Enum
from itertools import chain
from typing import TYPE_CHECKING, Any

Expand Down Expand Up @@ -85,6 +86,13 @@ class IncrementalMergeInfo:
index: int | None = None


class AuthorSource(str, Enum):
"""Source of the author shown in each changelog entry (`--add-author`)"""

AUTHOR = "author"
SIGNED_OFF_BY = "signed-off-by"


def get_commit_tag(commit: GitCommit, tags: list[GitTag]) -> GitTag | None:
return next((tag for tag in tags if tag.rev == commit.rev), None)

Expand All @@ -100,6 +108,7 @@ def generate_tree_from_commits(
changelog_release_hook: ChangelogReleaseHook | None = None,
rules: TagRules | None = None,
during_version_bump: bool = False,
author_source: AuthorSource | None = None,
) -> Generator[dict[str, Any], None, None]:
pat = re.compile(changelog_pattern)
map_pat = re.compile(commit_parser, re.MULTILINE)
Expand Down Expand Up @@ -159,6 +168,7 @@ def generate_tree_from_commits(
commit,
changes,
change_type_map,
author_source,
)

release = {
Expand All @@ -171,12 +181,33 @@ def generate_tree_from_commits(
yield release


def resolve_changelog_author(
commit: GitCommit, author_source: AuthorSource
) -> tuple[str, str]:
"""Get the author name and email to show in a changelog entry.

Args:
commit: The commit of the changelog entry.
author_source: Where to read the author from.

Returns:
A `(name, email)` tuple. For `SIGNED_OFF_BY`, it is the first
`Signed-off-by` trailer, or the commit author when there is no trailer.
"""
if author_source is AuthorSource.SIGNED_OFF_BY and (
signed_off_by := commit.signed_off_by
):
return signed_off_by
return commit.author, commit.author_email


def process_commit_message(
hook: MessageBuilderHook | None,
parsed: re.Match[str],
commit: GitCommit,
ref_changes: MutableMapping[str | None, list],
change_type_map: Mapping[str, str] | None = None,
author_source: AuthorSource | None = None,
) -> None:
message: dict[str, Any] = {
"sha1": commit.rev,
Expand All @@ -185,6 +216,10 @@ def process_commit_message(
"author_email": commit.author_email,
**parsed.groupdict(),
}
if author_source is not None:
name, email = resolve_changelog_author(commit, author_source)
message["changelog_author"] = name
message["changelog_author_email"] = email

processed_msg = hook(message, commit) if hook else message
if not processed_msg:
Expand Down
13 changes: 13 additions & 0 deletions commitizen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ def __call__(
"metavar": "EXTRA",
"help": "Changelog extra variables (in the form 'key=value').",
},
{
"name": ["--add-author"],
"nargs": "?",
"const": "author",
"default": None,
"choices": ["author", "signed-off-by"],
"help": (
"Add the author to each changelog entry. "
"'author' (default) uses the commit author. "
"'signed-off-by' uses the first Signed-off-by trailer, "
"or the commit author if the commit has no trailer."
),
},
)

data = {
Expand Down
2 changes: 2 additions & 0 deletions commitizen/commands/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@


class BumpArgs(Settings, total=False):
add_author: str | None
allow_no_commit: bool | None
annotated_tag_message: str | None
build_metadata: str | None
Expand Down Expand Up @@ -330,6 +331,7 @@ def __call__(self) -> None:
"dry_run": dry_run,
# governs logic for merge_prerelease
"during_version_bump": self.arguments["prerelease"] is None,
"add_author": self.arguments.get("add_author"),
}
if self.changelog_to_stdout:
try:
Expand Down
4 changes: 4 additions & 0 deletions commitizen/commands/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ChangelogArgs(TypedDict, total=False):
export_template: str
during_version_bump: bool | None
allow_no_commit: bool | None # Internal-only when invoked by bump.
add_author: str | None


class Changelog:
Expand Down Expand Up @@ -127,6 +128,8 @@ def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None:
self.during_version_bump: bool = arguments.get("during_version_bump") or False
# Internal flag used when changelog is invoked from `cz bump --allow-no-commit`.
self.allow_no_commit: bool = bool(arguments.get("allow_no_commit"))
add_author = arguments.get("add_author")
self.author_source = changelog.AuthorSource(add_author) if add_author else None

def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str:
"""Try to find the 'start_rev'.
Expand Down Expand Up @@ -277,6 +280,7 @@ def __call__(self) -> None:
changelog_release_hook=self.cz.changelog_release_hook,
rules=self.tag_rules,
during_version_bump=self.during_version_bump,
author_source=self.author_source,
)
if self.change_type_order:
tree = changelog.generate_ordered_changelog_tree(
Expand Down
19 changes: 19 additions & 0 deletions commitizen/git.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import os
import re
from enum import Enum
from functools import lru_cache
from pathlib import Path
Expand All @@ -14,6 +15,12 @@
from collections.abc import Sequence


_SIGNED_OFF_BY_PATTERN = re.compile(
r"^Signed-off-by:\s*(?P<name>.+?)\s*<(?P<email>[^>]+)>\s*$",
re.MULTILINE | re.IGNORECASE,
)


class EOLType(Enum):
"""The EOL type from `git config core.eol`."""

Expand Down Expand Up @@ -78,6 +85,18 @@ def __init__(
def message(self) -> str:
return f"{self.title}\n\n{self.body}".strip()

@property
def signed_off_by(self) -> tuple[str, str] | None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be added to commitizen, the rest belong on a separate package

"""Get the first `Signed-off-by` trailer of the commit body.

Returns:
`(name, email)` tuple, or `None` when the body has no trailer.
"""
match = _SIGNED_OFF_BY_PATTERN.search(self.body)
if match is None:
return None
return match.group("name"), match.group("email")

@classmethod
def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit:
"""Create a GitCommit instance from a formatted commit string.
Expand Down
4 changes: 2 additions & 2 deletions commitizen/templates/CHANGELOG.adoc.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

{% for change in changes %}
{% if change.scope %}
* *{{ change.scope }}*: {{ change.message }}
* *{{ change.scope }}*: {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% elif change.message %}
* {{ change.message }}
* {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% endif %}
{% endfor %}
{% endfor %}
Expand Down
4 changes: 2 additions & 2 deletions commitizen/templates/CHANGELOG.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

{% for change in changes %}
{% if change.scope %}
- **{{ change.scope }}**: {{ change.message }}
- **{{ change.scope }}**: {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% elif change.message %}
- {{ change.message }}
- {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% endif %}
{% endfor %}
{% endfor %}
Expand Down
4 changes: 2 additions & 2 deletions commitizen/templates/CHANGELOG.rst.j2
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

{% for change in changes %}
{% if change.scope %}
- **{{ change.scope }}**: {{ change.message }}
- **{{ change.scope }}**: {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% elif change.message %}
- {{ change.message }}
- {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% endif %}
{% endfor %}
{% endfor %}
Expand Down
4 changes: 2 additions & 2 deletions commitizen/templates/CHANGELOG.textile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ h3. {{ change_key }}

{% for change in changes %}
{% if change.scope %}
- *{{ change.scope }}*: {{ change.message }}
- *{{ change.scope }}*: {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% elif change.message %}
- {{ change.message }}
- {{ change.message }}{{ " (" ~ change.changelog_author ~ " <" ~ change.changelog_author_email ~ ">)" if change.changelog_author }}
{% endif %}
{% endfor %}
{% endfor %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ usage: cz bump [-h] [--dry-run] [--files-only] [--version-files-only]
[--annotated-tag-message ANNOTATED_TAG_MESSAGE] [--gpg-sign]
[--changelog-to-stdout] [--git-output-to-stderr] [--retry]
[--major-version-zero] [--template TEMPLATE] [--extra EXTRA]
[--file-name FILE_NAME] [--prerelease-offset PRERELEASE_OFFSET]
[--add-author [{author,signed-off-by}]] [--file-name FILE_NAME]
[--prerelease-offset PRERELEASE_OFFSET]
[--version-scheme {pep440,semver,semver2}]
[--version-type {pep440,semver,semver2}]
[--build-metadata BUILD_METADATA] [--get-next]
Expand Down Expand Up @@ -73,6 +74,11 @@ options:
working directory).
--extra EXTRA, -e EXTRA
Changelog extra variables (in the form 'key=value').
--add-author [{author,signed-off-by}]
Add the author to each changelog entry. 'author'
(default) uses the commit author. 'signed-off-by' uses
the first Signed-off-by trailer, or the commit author
if the commit has no trailer.
--file-name FILE_NAME
File name of changelog (default: 'CHANGELOG.md').
--prerelease-offset PRERELEASE_OFFSET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME]
[--start-rev START_REV] [--merge-prerelease]
[--version-scheme {pep440,semver,semver2}]
[--export-template EXPORT_TEMPLATE] [--template TEMPLATE]
[--extra EXTRA] [--tag-format TAG_FORMAT]
[--extra EXTRA] [--add-author [{author,signed-off-by}]]
[--tag-format TAG_FORMAT]
[rev_range]

Generate changelog (note that it will overwrite existing files)
Expand Down Expand Up @@ -38,5 +39,10 @@ options:
working directory).
--extra EXTRA, -e EXTRA
Changelog extra variables (in the form 'key=value').
--add-author [{author,signed-off-by}]
Add the author to each changelog entry. 'author'
(default) uses the commit author. 'signed-off-by' uses
the first Signed-off-by trailer, or the commit author
if the commit has no trailer.
--tag-format TAG_FORMAT
The format of the tag, wrap around simple quotes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ usage: cz bump [-h] [--dry-run] [--files-only] [--version-files-only]
[--annotated-tag-message ANNOTATED_TAG_MESSAGE] [--gpg-sign]
[--changelog-to-stdout] [--git-output-to-stderr] [--retry]
[--major-version-zero] [--template TEMPLATE] [--extra EXTRA]
[--file-name FILE_NAME] [--prerelease-offset PRERELEASE_OFFSET]
[--add-author [{author,signed-off-by}]] [--file-name FILE_NAME]
[--prerelease-offset PRERELEASE_OFFSET]
[--version-scheme {pep440,semver,semver2}]
[--version-type {pep440,semver,semver2}]
[--build-metadata BUILD_METADATA] [--get-next]
Expand Down Expand Up @@ -73,6 +74,11 @@ options:
working directory).
--extra EXTRA, -e EXTRA
Changelog extra variables (in the form 'key=value').
--add-author [{author,signed-off-by}]
Add the author to each changelog entry. 'author'
(default) uses the commit author. 'signed-off-by' uses
the first Signed-off-by trailer, or the commit author
if the commit has no trailer.
--file-name FILE_NAME
File name of changelog (default: 'CHANGELOG.md').
--prerelease-offset PRERELEASE_OFFSET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME]
[--start-rev START_REV] [--merge-prerelease]
[--version-scheme {pep440,semver,semver2}]
[--export-template EXPORT_TEMPLATE] [--template TEMPLATE]
[--extra EXTRA] [--tag-format TAG_FORMAT]
[--extra EXTRA] [--add-author [{author,signed-off-by}]]
[--tag-format TAG_FORMAT]
[rev_range]

Generate changelog (note that it will overwrite existing files)
Expand Down Expand Up @@ -38,5 +39,10 @@ options:
working directory).
--extra EXTRA, -e EXTRA
Changelog extra variables (in the form 'key=value').
--add-author [{author,signed-off-by}]
Add the author to each changelog entry. 'author'
(default) uses the commit author. 'signed-off-by' uses
the first Signed-off-by trailer, or the commit author
if the commit has no trailer.
--tag-format TAG_FORMAT
The format of the tag, wrap around simple quotes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ usage: cz bump [-h] [--dry-run] [--files-only] [--version-files-only]
[--annotated-tag-message ANNOTATED_TAG_MESSAGE] [--gpg-sign]
[--changelog-to-stdout] [--git-output-to-stderr] [--retry]
[--major-version-zero] [--template TEMPLATE] [--extra EXTRA]
[--file-name FILE_NAME] [--prerelease-offset PRERELEASE_OFFSET]
[--add-author [{author,signed-off-by}]] [--file-name FILE_NAME]
[--prerelease-offset PRERELEASE_OFFSET]
[--version-scheme {pep440,semver,semver2}]
[--version-type {pep440,semver,semver2}]
[--build-metadata BUILD_METADATA] [--get-next]
Expand Down Expand Up @@ -73,6 +74,11 @@ options:
working directory).
--extra EXTRA, -e EXTRA
Changelog extra variables (in the form 'key=value').
--add-author [{author,signed-off-by}]
Add the author to each changelog entry. 'author'
(default) uses the commit author. 'signed-off-by' uses
the first Signed-off-by trailer, or the commit author
if the commit has no trailer.
--file-name FILE_NAME
File name of changelog (default: 'CHANGELOG.md').
--prerelease-offset PRERELEASE_OFFSET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME]
[--start-rev START_REV] [--merge-prerelease]
[--version-scheme {pep440,semver,semver2}]
[--export-template EXPORT_TEMPLATE] [--template TEMPLATE]
[--extra EXTRA] [--tag-format TAG_FORMAT]
[--extra EXTRA] [--add-author [{author,signed-off-by}]]
[--tag-format TAG_FORMAT]
[rev_range]

Generate changelog (note that it will overwrite existing files)
Expand Down Expand Up @@ -38,5 +39,10 @@ options:
working directory).
--extra EXTRA, -e EXTRA
Changelog extra variables (in the form 'key=value').
--add-author [{author,signed-off-by}]
Add the author to each changelog entry. 'author'
(default) uses the commit author. 'signed-off-by' uses
the first Signed-off-by trailer, or the commit author
if the commit has no trailer.
--tag-format TAG_FORMAT
The format of the tag, wrap around simple quotes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ usage: cz bump [-h] [--dry-run] [--files-only] [--version-files-only]
[--annotated-tag-message ANNOTATED_TAG_MESSAGE] [--gpg-sign]
[--changelog-to-stdout] [--git-output-to-stderr] [--retry]
[--major-version-zero] [--template TEMPLATE] [--extra EXTRA]
[--file-name FILE_NAME] [--prerelease-offset PRERELEASE_OFFSET]
[--add-author [{author,signed-off-by}]] [--file-name FILE_NAME]
[--prerelease-offset PRERELEASE_OFFSET]
[--version-scheme {pep440,semver,semver2}]
[--version-type {pep440,semver,semver2}]
[--build-metadata BUILD_METADATA] [--get-next]
Expand Down Expand Up @@ -72,6 +73,11 @@ options:
Changelog template file name (relative to the current
working directory).
--extra, -e EXTRA Changelog extra variables (in the form 'key=value').
--add-author [{author,signed-off-by}]
Add the author to each changelog entry. 'author'
(default) uses the commit author. 'signed-off-by' uses
the first Signed-off-by trailer, or the commit author
if the commit has no trailer.
--file-name FILE_NAME
File name of changelog (default: 'CHANGELOG.md').
--prerelease-offset PRERELEASE_OFFSET
Expand Down
Loading
Loading