From 6ab2204416a8e958a52dea6f1a47b497e4631e01 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Wed, 4 May 2022 16:40:05 +0100 Subject: [PATCH 1/3] feat(bump): let it respect pre-commit reformats when bumping Fix https://github.com/commitizen-tools/commitizen/issues/502. Just add `--retry` to `cz bump` and it will attempt to commit twice if the 1st commit fails. Useful if your 1st commit runs a formatter that can for example change the contents of CHANGELOG.md. --- commitizen/cli.py | 6 +++ commitizen/commands/bump.py | 14 ++++++- tests/test_bump_create_commit_message.py | 50 +++++++++++++++++++++++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index bf751b44a4..b280e54672 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -165,6 +165,12 @@ "default": False, "help": "Output changelog to the stdout", }, + { + "name": ["--retry"], + "action": "store_true", + "default": False, + "help": "retry commit if it fails the 1st time", + }, ], }, { diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index cd05e8a8f3..e0779cd625 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -1,3 +1,4 @@ +from logging import getLogger from typing import List, Optional import questionary @@ -18,6 +19,8 @@ NoVersionSpecifiedError, ) +logger = getLogger("commitizen") + class Bump: """Show prompt for the user to create a guided commit.""" @@ -49,6 +52,7 @@ def __init__(self, config: BaseConfig, arguments: dict): self.changelog_to_stdout = arguments["changelog_to_stdout"] self.no_verify = arguments["no_verify"] self.check_consistency = arguments["check_consistency"] + self.retry = arguments["retry"] def is_initial_tag(self, current_tag_version: str, is_yes: bool = False) -> bool: """Check if reading the whole git tree up to HEAD is needed.""" @@ -209,7 +213,7 @@ def __call__(self): # noqa: C901 }, ) changelog_cmd() - c = cmd.run(f"git add {changelog_cmd.file_name}") + c = cmd.run(f"git add {changelog_cmd.file_name} {' '.join(version_files)}") self.config.set_key("version", str(new_version)) @@ -217,8 +221,14 @@ def __call__(self): # noqa: C901 raise ExpectedExit() c = git.commit(message, args=self._get_commit_args()) + if self.retry and c.return_code != 0 and self.changelog: + # Maybe pre-commit reformatted some files? Retry once + logger.debug("1st git.commit error: %s", c.err) + logger.info("1st commit attempt failed; retrying once") + cmd.run(f"git add {changelog_cmd.file_name} {' '.join(version_files)}") + c = git.commit(message, args=self._get_commit_args()) if c.return_code != 0: - raise BumpCommitFailedError(f'git.commit error: "{c.err.strip()}"') + raise BumpCommitFailedError(f'2nd git.commit error: "{c.err.strip()}"') c = git.tag( new_tag_version, annotated=self.bump_settings.get("annotated_tag", False) diff --git a/tests/test_bump_create_commit_message.py b/tests/test_bump_create_commit_message.py index 4923d0a66c..937905dd96 100644 --- a/tests/test_bump_create_commit_message.py +++ b/tests/test_bump_create_commit_message.py @@ -1,7 +1,11 @@ +import sys +from pathlib import Path +from textwrap import dedent + import pytest from packaging.version import Version -from commitizen import bump +from commitizen import bump, cli, cmd conversion = [ ( @@ -20,3 +24,47 @@ def test_create_tag(test_input, expected): Version(current_version), Version(new_version), message_template ) assert new_tag == expected + + +@pytest.mark.parametrize("retry", (True, False)) +def test_bump_pre_commit_changelog(tmp_commitizen_project, mocker, freezer, retry): + freezer.move_to("2022-04-01") + testargs = ["cz", "bump", "--changelog", "--yes"] + if retry: + testargs.append("--retry") + else: + pytest.xfail("it will fail because pre-commit will reformat CHANGELOG.md") + mocker.patch.object(sys, "argv", testargs) + with tmp_commitizen_project.as_cwd(): + # Configure prettier as a pre-commit hook + Path(".pre-commit-config.yaml").write_text( + """ + repos: + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.6.2 + hooks: + - id: prettier + stages: [commit] + """ + ) + # Prettier inherits editorconfig + Path(".editorconfig").write_text( + """ + [*] + indent_size = 4 + """ + ) + cmd.run("git add -A") + cmd.run("git commit -m 'fix: _test'") + cmd.run("pre-commit install") + cli.main() + # Pre-commit fixed last line adding extra indent and "\" char + assert Path("CHANGELOG.md").read_text() == dedent( + """\ + ## 0.1.1 (2022-04-01) + + ### Fix + + - \\_test + """ + ) From 889026e3c4ee0210982934c4d3253f2e0d1c4806 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 10 May 2022 17:04:21 +0100 Subject: [PATCH 2/3] docs: document --retry Includes an update to the `cz bump --help` output. --- docs/bump.md | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/bump.md b/docs/bump.md index cbf7fef171..8062044a76 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -54,32 +54,38 @@ Some examples: ```bash $ cz bump --help -usage: cz bump [-h] [--dry-run] [--files-only] [--changelog] [--no-verify] [--local-version] - [--yes] [--tag-format TAG_FORMAT] [--bump-message BUMP_MESSAGE] - [--prerelease {alpha,beta,rc}] - [--increment {MAJOR,MINOR,PATCH}] [--check-consistency] [--annotated-tag] +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}] + [--increment {MAJOR,MINOR,PATCH}] [--check-consistency] + [--annotated-tag] [--changelog-to-stdout] [--retry] -optional arguments: +options: -h, --help show this help message and exit --dry-run show output to stdout, no commit, no modified files --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 - --local-version bump the local portion of the version --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 --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 + --changelog-to-stdout + Output changelog to the stdout + --retry retry commit if it fails the 1st time ``` ### `--files-only` @@ -179,6 +185,13 @@ Example: cz bump --changelog --changelog-to-stdout > body.md ``` +### `--retry` + +If you use tools like [pre-commit](https://pre-commit.com/), add this flag. +It will retry the commit if it fails the 1st time. + +Useful to combine with code formatters, like [Prettier](https://prettier.io/). + ## Avoid raising errors Some situations from commitizen rise an exit code different than 0. From 49f1302011ed4faf23b2f7afa915112851a7f706 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 13 May 2022 11:53:02 +0100 Subject: [PATCH 3/3] test: assert that a failing commit condition will fail always, both with or without --retry --- tests/test_bump_create_commit_message.py | 31 +++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/test_bump_create_commit_message.py b/tests/test_bump_create_commit_message.py index 937905dd96..ee1f1f768c 100644 --- a/tests/test_bump_create_commit_message.py +++ b/tests/test_bump_create_commit_message.py @@ -5,7 +5,7 @@ import pytest from packaging.version import Version -from commitizen import bump, cli, cmd +from commitizen import bump, cli, cmd, exceptions conversion = [ ( @@ -68,3 +68,32 @@ def test_bump_pre_commit_changelog(tmp_commitizen_project, mocker, freezer, retr - \\_test """ ) + + +@pytest.mark.parametrize("retry", (True, False)) +def test_bump_pre_commit_changelog_fails_always( + tmp_commitizen_project, mocker, freezer, retry +): + freezer.move_to("2022-04-01") + testargs = ["cz", "bump", "--changelog", "--yes"] + if retry: + testargs.append("--retry") + mocker.patch.object(sys, "argv", testargs) + with tmp_commitizen_project.as_cwd(): + Path(".pre-commit-config.yaml").write_text( + """ + repos: + - repo: local + hooks: + - id: forbid-changelog + name: changelogs are forbidden + entry: changelogs are forbidden + language: fail + files: CHANGELOG.md + """ + ) + cmd.run("git add -A") + cmd.run("git commit -m 'feat: forbid changelogs'") + cmd.run("pre-commit install") + with pytest.raises(exceptions.BumpCommitFailedError): + cli.main()