diff --git a/commitizen/cli.py b/commitizen/cli.py index 19d6b1b80a..511f745652 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -243,6 +243,11 @@ def __call__( "help": "create annotated tag instead of lightweight one", "action": "store_true", }, + { + "name": ["--annotated-tag-message", "-atm"], + "help": "create annotated tag message", + "type": str, + }, { "name": ["--gpg-sign", "-s"], "help": "sign tag instead of lightweight one", diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index f1b6813566..af365ecd28 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -50,6 +50,7 @@ def __init__(self, config: BaseConfig, arguments: dict): "bump_message", "gpg_sign", "annotated_tag", + "annotated_tag_message", "major_version_zero", "prerelease_offset", "template", @@ -197,8 +198,7 @@ def __call__(self): # noqa: C901 # If user specified changelog_to_stdout, they probably want the # changelog to be generated as well, this is the most intuitive solution - if not self.changelog and self.changelog_to_stdout: - self.changelog = True + self.changelog = self.changelog or bool(self.changelog_to_stdout) # No commits, there is no need to create an empty tag. # Unless we previously had a prerelease. @@ -365,7 +365,10 @@ def __call__(self): # noqa: C901 signed=self.bump_settings.get("gpg_sign", False) or bool(self.config.settings.get("gpg_sign", False)), annotated=self.bump_settings.get("annotated_tag", False) - or bool(self.config.settings.get("annotated_tag", False)), + or bool(self.config.settings.get("annotated_tag", False)) + or bool(self.bump_settings.get("annotated_tag_message", False)), + msg=self.bump_settings.get("annotated_tag_message", None), + # TODO: also get from self.config.settings? ) if c.return_code != 0: raise BumpTagFailedError(c.err) diff --git a/commitizen/git.py b/commitizen/git.py index 46aa3abffc..4c4dfdb961 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -82,13 +82,19 @@ def from_line(cls, line: str, inner_delimiter: str) -> GitTag: return cls(name=name, rev=obj, date=date) -def tag(tag: str, annotated: bool = False, signed: bool = False) -> cmd.Command: +def tag( + tag: str, annotated: bool = False, signed: bool = False, msg: str | None = None +) -> cmd.Command: _opt = "" if annotated: _opt = f"-a {tag} -m" if signed: _opt = f"-s {tag} -m" - c = cmd.run(f"git tag {_opt} {tag}") + + # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging, + # we're not able to create lightweight tag with message. + # by adding message, we make it a annotated tags + c = cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"') return c @@ -200,6 +206,13 @@ def get_latest_tag_name() -> str | None: return c.out.strip() +def get_tag_message(tag: str) -> str | None: + c = cmd.run(f"git tag -l --format='%(contents:subject)' {tag}") + if c.err: + return None + return c.out.strip() + + def get_tag_names() -> list[str | None]: c = cmd.run("git tag --list") if c.err: diff --git a/docs/bump.md b/docs/bump.md index 8e0980ec82..75d6eb7c11 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -52,13 +52,10 @@ Some examples of pep440: ```bash $ cz bump --help -usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog] - [--no-verify] [--yes] [--tag-format TAG_FORMAT] - [--bump-message BUMP_MESSAGE] [--prerelease {alpha,beta,rc}] - [--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}] - [--check-consistency] [--annotated-tag] [--gpg-sign] - [--changelog-to-stdout] [--git-output-to-stderr] [--retry] [--major-version-zero] - [--template TEMPLATE] [--extra EXTRA] +usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog] [--no-verify] [--yes] [--tag-format TAG_FORMAT] + [--bump-message BUMP_MESSAGE] [--prerelease {alpha,beta,rc}] [--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}] + [--check-consistency] [--annotated-tag] [--gpg-sign] [--changelog-to-stdout] [--git-output-to-stderr] [--retry] [--major-version-zero] + [--prerelease-offset PRERELEASE_OFFSET] [--version-scheme {semver,pep440}] [--version-type {semver,pep440}] [MANUAL_VERSION] positional arguments: @@ -70,15 +67,12 @@ options: --files-only bump version in the files from the config --local-version bump only the local version portion --changelog, -ch generate the changelog for the newest version - --no-verify this option bypasses the pre-commit and commit-msg - hooks + --no-verify this option bypasses the pre-commit and commit-msg hooks --yes accept automatically questions done --tag-format TAG_FORMAT - the format used to tag the commit and read it, use it - in existing projects, wrap around simple quotes + the format used to tag the commit and read it, use it in existing projects, wrap around simple quotes --bump-message BUMP_MESSAGE - template used to create the release commit, useful - when working with CI + template used to create the release commit, useful when working with CI --prerelease {alpha,beta,rc}, -pr {alpha,beta,rc} choose type of prerelease --devrelease DEVRELEASE, -d DEVRELEASE @@ -86,8 +80,7 @@ options: --increment {MAJOR,MINOR,PATCH} manually specify the desired increment --check-consistency, -cc - check consistency among versions defined in commitizen - configuration and version_files + check consistency among versions defined in commitizen configuration and version_files --annotated-tag, -at create annotated tag instead of lightweight one --gpg-sign, -s sign tag instead of lightweight one --changelog-to-stdout @@ -96,14 +89,12 @@ options: Redirect git output to stderr --retry retry commit if it fails the 1st time --major-version-zero keep major version at zero, even for breaking changes - --prerelease-offset start pre-releases with this offset - --version-scheme {pep440,semver} + --prerelease-offset PRERELEASE_OFFSET + start pre-releases with this offset + --version-scheme {semver,pep440} choose version scheme - - --template TEMPLATE, -t TEMPLATE - changelog template file name (relative to the current working directory) - --extra EXTRA, -e EXTRA - a changelog extra variable (in the form 'key=value') + --version-type {semver,pep440} + Deprecated, use --version-scheme ``` ### `--files-only` @@ -184,6 +175,9 @@ If `--local-version` is used, it will bump only the local version `0.1.0` and ke If `--annotated-tag` is used, commitizen will create annotated tags. Also available via configuration, in `pyproject.toml` or `.cz.toml`. +### `--annotated-tag-message` +If `--annotated-tag-message` is used, commitizen will create annotated tags with the given message. + ### `--changelog-to-stdout` If `--changelog-to-stdout` is used, the incremental changelog generated by the bump diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 0ae7d1e509..774ec0a95f 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -673,7 +673,7 @@ def test_bump_with_git_to_stdout_arg(mocker: MockFixture, capsys, changelog_path "bump", "--changelog", "--changelog-to-stdout", - "--annotated", + "--annotated-tag", "--check-consistency", "--yes", ), diff --git a/tests/test_git.py b/tests/test_git.py index 79cd76fca6..4ae11a45dd 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -2,13 +2,14 @@ import inspect import os +import platform import shutil import pytest from commitizen import cmd, exceptions, git from pytest_mock import MockFixture -from tests.utils import FakeCommand, create_file_and_commit +from tests.utils import FakeCommand, create_file_and_commit, create_tag def test_git_object_eq(): @@ -239,3 +240,15 @@ def test_eoltypes_get_eol_for_open(): assert git.EOLTypes.get_eol_for_open(git.EOLTypes.NATIVE) == os.linesep assert git.EOLTypes.get_eol_for_open(git.EOLTypes.LF) == "\n" assert git.EOLTypes.get_eol_for_open(git.EOLTypes.CRLF) == "\r\n" + + +def test_create_tag_with_message(tmp_commitizen_project): + with tmp_commitizen_project.as_cwd(): + create_file_and_commit("feat(test): test") + tag_name = "1.0" + tag_message = "test message" + create_tag(tag_name, tag_message) + assert git.get_latest_tag_name() == tag_name + assert git.get_tag_message(tag_name) == ( + tag_message if platform.system() != "Windows" else f"'{tag_message}'" + ) diff --git a/tests/utils.py b/tests/utils.py index 6eed953270..08d4414ba3 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -55,8 +55,8 @@ def get_current_branch() -> str: return c.out -def create_tag(tag: str): - c = git.tag(tag) +def create_tag(tag: str, message: str | None = None) -> None: + c = git.tag(tag, annotated=(message is not None), msg=message) if c.return_code != 0: raise exceptions.CommitError(c.err)