From a442d7021badcfe779eb52884e6be5f9d836fff4 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sun, 25 Aug 2024 03:12:42 +0200 Subject: [PATCH 01/13] feat(commit): allow '-- --allow-empty' to create empty commits Signed-off-by: Adrian DC --- commitizen/commands/commit.py | 8 +++-- tests/commands/test_commit_command.py | 49 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 9b13a020b6..abecb3b3ca 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -93,6 +93,10 @@ def manual_edit(self, message: str) -> str: return message def __call__(self): + extra_args: str = self.arguments.get("extra_cli_args", "") + + allow_empty: bool = "--allow-empty" in extra_args + dry_run: bool = self.arguments.get("dry_run") write_message_to_file: bool = self.arguments.get("write_message_to_file") manual_edit: bool = self.arguments.get("edit") @@ -101,7 +105,7 @@ def __call__(self): if is_all: c = git.add("-u") - if git.is_staging_clean() and not dry_run: + if git.is_staging_clean() and not (dry_run or allow_empty): raise NothingToCommitError("No files added to staging!") if write_message_to_file is not None and write_message_to_file.is_dir(): @@ -137,8 +141,6 @@ def __call__(self): always_signoff: bool = self.config.settings["always_signoff"] signoff: bool = self.arguments.get("signoff") - extra_args = self.arguments.get("extra_cli_args", "") - if signoff: out.warn( "signoff mechanic is deprecated, please use `cz commit -- -s` instead." diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 85959abe33..55751f6902 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -324,6 +324,55 @@ def test_commit_when_nothing_to_commit(config, mocker: MockFixture): assert "No files added to staging!" in str(excinfo.value) +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_with_allow_empty(config, mocker: MockFixture): + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "closes #21", + "footer": "", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) + success_mock = mocker.patch("commitizen.out.success") + + commands.Commit(config, {"extra_cli_args": "--allow-empty"})() + + commit_mock.assert_called_with( + "feat: user created\n\ncloses #21", args="--allow-empty" + ) + success_mock.assert_called_once() + + +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_with_signoff_and_allow_empty(config, mocker: MockFixture): + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "closes #21", + "footer": "", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) + success_mock = mocker.patch("commitizen.out.success") + + config.settings["always_signoff"] = True + commands.Commit(config, {"extra_cli_args": "--allow-empty"})() + + commit_mock.assert_called_with( + "feat: user created\n\ncloses #21", args="--allow-empty -s" + ) + success_mock.assert_called_once() + + @pytest.mark.usefixtures("staging_is_clean") def test_commit_when_customized_expected_raised(config, mocker: MockFixture, capsys): _err = ValueError() From 753c9053e971a79276ed4bb5b23d099c91033738 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Tue, 13 Aug 2024 17:35:37 +0200 Subject: [PATCH 02/13] test(cz_customize): add missing YAML configuration file tests Signed-off-by: Adrian DC --- tests/test_cz_customize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 210c8b6774..cc1037d761 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -321,6 +321,7 @@ params=[ TomlConfig(data=TOML_STR, path="not_exist.toml"), JsonConfig(data=JSON_STR, path="not_exist.json"), + YAMLConfig(data=YAML_STR, path="not_exist.yaml"), ] ) def config(request): From 735b4fe2b08b1fd130d2ae1bc4d5106c271c63d0 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Tue, 13 Aug 2024 18:53:52 +0200 Subject: [PATCH 03/13] test(cz_customize): fix YAML test and docs configurations quotes > commitizen.exceptions.InvalidConfigurationError: Failed to parse not_exist.yaml: while scanning a double-quoted scalar > found unknown escape character 's' Signed-off-by: Adrian DC --- docs/customization.md | 14 +++++++------- tests/test_cz_customize.py | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/customization.md b/docs/customization.md index 16ba588f10..0561e7e239 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -110,13 +110,13 @@ And the correspondent example for a yaml json file: commitizen: name: cz_customize customize: - message_template: "{{change_type}}:{% if show_message %} {{message}}{% endif %}" + message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' example: 'feature: this feature enable customize through config file' - schema: ": " - schema_pattern: "(feature|bug fix):(\\s.*)" - bump_pattern: "^(break|new|fix|hotfix)" - commit_parser: "^(?Pfeature|bug fix):\\s(?P.*)?", - changelog_pattern: "^(feature|bug fix)?(!)?", + schema: ': ' + schema_pattern: '(feature|bug fix):(\\s.*)' + bump_pattern: '^(break|new|fix|hotfix)' + commit_parser: '^(?Pfeature|bug fix):\\s(?P.*)?' + changelog_pattern: '^(feature|bug fix)?(!)?' change_type_map: feature: Feat bug fix: Fix @@ -125,7 +125,7 @@ commitizen: new: MINOR fix: PATCH hotfix: PATCH - change_type_order: ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] + change_type_order: ['BREAKING CHANGE', 'feat', 'fix', 'refactor', 'perf'] info_path: cz_customize_info.txt info: This is customized info questions: diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index cc1037d761..47b6d7fc49 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -105,17 +105,17 @@ - commitizen/__version__.py - pyproject.toml customize: - message_template: "{{change_type}}:{% if show_message %} {{message}}{% endif %}" + message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' example: 'feature: this feature enables customization through a config file' - schema: ": " - schema_pattern: "(feature|bug fix):(\\s.*)" - bump_pattern: "^(break|new|fix|hotfix)" + schema: ': ' + schema_pattern: '(feature|bug fix):(\\s.*)' + bump_pattern: '^(break|new|fix|hotfix)' bump_map: break: MAJOR new: MINOR fix: PATCH hotfix: PATCH - change_type_order: ["perf", "BREAKING CHANGE", "feat", "fix", "refactor"] + change_type_order: ['perf', 'BREAKING CHANGE', 'feat', 'fix', 'refactor'] info: This is a customized cz. questions: - type: list From e41b7e4982accb5d3860626f7241fef8d0012aba Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Tue, 13 Aug 2024 18:54:21 +0200 Subject: [PATCH 04/13] test(cz_customize): fix missing YAML test keys against JSON/TOML > test_commit_parser[config2] - AssertionError: > assert '(?P.*)' == '^(?P.*)?' > test_changelog_pattern[config2] - AssertionError: > assert '.*' == '^(feature|bug fix)?(!)?' > test_change_type_map[config2] - AssertionError: > assert None == {'bug fix': 'Fix', 'feature': 'Feat'} Signed-off-by: Adrian DC --- tests/test_cz_customize.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 47b6d7fc49..594bb2edea 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -110,6 +110,11 @@ schema: ': ' schema_pattern: '(feature|bug fix):(\\s.*)' bump_pattern: '^(break|new|fix|hotfix)' + commit_parser: '^(?Pfeature|bug fix):\\s(?P.*)?' + changelog_pattern: '^(feature|bug fix)?(!)?' + change_type_map: + feature: Feat + bug fix: Fix bump_map: break: MAJOR new: MINOR From db9e56576ea208afff5f38b8a489cf8c15ef366f Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Fri, 16 Aug 2024 23:08:02 +0200 Subject: [PATCH 05/13] chore(scripts): allow passing 'pytest' arguments to './script/test' Example: ./scripts/test -k 'test_commit_when_' --no-cov --- Signed-off-by: Adrian DC --- scripts/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test b/scripts/test index 894228b41f..f21578552d 100755 --- a/scripts/test +++ b/scripts/test @@ -4,7 +4,7 @@ set -e export PREFIX='poetry run python -m ' export REGEX='^(?![.]|venv).*' -${PREFIX}pytest -n 3 --dist=loadfile --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen tests/ +${PREFIX}pytest -n 3 --dist=loadfile --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen "${@}" tests/ ${PREFIX}ruff check commitizen/ tests/ --fix ${PREFIX}mypy commitizen/ tests/ ${PREFIX}commitizen -nr 3 check --rev-range origin/master.. From 5501b08961e223905fd00d7b6590b077180476df Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Fri, 16 Aug 2024 23:52:42 +0200 Subject: [PATCH 06/13] test(command): cover 'nothing added' and 'no changes added to commit' Signed-off-by: Adrian DC --- tests/commands/test_commit_command.py | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 55751f6902..659186a44f 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -324,6 +324,68 @@ def test_commit_when_nothing_to_commit(config, mocker: MockFixture): assert "No files added to staging!" in str(excinfo.value) +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_when_nothing_added_to_commit(config, mocker: MockFixture): + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "", + "footer": "", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command( + 'nothing added to commit but untracked files present (use "git add" to track)', + "", + b"", + b"", + 0, + ) + + error_mock = mocker.patch("commitizen.out.error") + + commands.Commit(config, {"all": False})() + + prompt_mock.assert_called_once() + error_mock.assert_called_once() + + assert "nothing added" in error_mock.call_args[0][0] + + +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_when_no_changes_added_to_commit(config, mocker: MockFixture): + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "", + "footer": "", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command( + 'no changes added to commit (use "git add" and/or "git commit -a")', + "", + b"", + b"", + 0, + ) + + error_mock = mocker.patch("commitizen.out.error") + + commands.Commit(config, {"all": False})() + + prompt_mock.assert_called_once() + error_mock.assert_called_once() + + assert "no changes added to commit" in error_mock.call_args[0][0] + + @pytest.mark.usefixtures("staging_is_clean") def test_commit_with_allow_empty(config, mocker: MockFixture): prompt_mock = mocker.patch("questionary.prompt") From 742e4f99e154884e6d547a40c2f45cbb0e6600fc Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sat, 17 Aug 2024 02:24:22 +0200 Subject: [PATCH 07/13] fix(commit): ensure 'questions' is a Python dictionary and not TOML Details: If using a TOML configuration, the type was 'tomlkit.items.AoT' --- Signed-off-by: Adrian DC --- commitizen/commands/commit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index abecb3b3ca..f1714711a0 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -51,7 +51,8 @@ def read_backup_message(self) -> str | None: def prompt_commit_questions(self) -> str: # Prompt user for the commit message cz = self.cz - questions = cz.questions() + questions = [dict(question) for question in cz.questions()] + for question in filter(lambda q: q["type"] == "list", questions): question["use_shortcuts"] = self.config.settings["use_shortcuts"] try: From c75de9a070e0d784482b6fd4f8297ca43074e5bc Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sat, 17 Aug 2024 02:30:44 +0200 Subject: [PATCH 08/13] test(cz_customize): use 'cz_customize' configurations in its tests Signed-off-by: Adrian DC --- tests/test_cz_customize.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 594bb2edea..418c475fec 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -5,6 +5,14 @@ from commitizen.exceptions import MissingCzCustomizeConfigError TOML_STR = r""" + [tool.commitizen] + name = "cz_customize" + version = "1.0.0" + version_files = [ + "commitizen/__version__.py", + "pyproject.toml" + ] + [tool.commitizen.customize] message_template = "{{change_type}}:{% if show_message %} {{message}}{% endif %}" example = "feature: this feature enables customization through a config file" @@ -42,7 +50,7 @@ JSON_STR = r""" { "commitizen": { - "name": "cz_jira", + "name": "cz_customize", "version": "1.0.0", "version_files": [ "commitizen/__version__.py", @@ -99,7 +107,7 @@ YAML_STR = """ commitizen: - name: cz_jira + name: cz_customize version: 1.0.0 version_files: - commitizen/__version__.py From 33bbf0ff68cc5fcf3a6b917918fd6e5c91b215ac Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Tue, 13 Aug 2024 16:01:56 +0200 Subject: [PATCH 09/13] feat(commit): implement questions 'filter' support with handlers Supported APIs: - multiple_line_breaker - required_validator - required_validator_scope - required_validator_subject_strip - required_validator_title_strip Example YAML configurations: --- commitizen: name: cz_customize customize: questions: - ... - type: input name: scope message: 'Scope of the change :' filter: 'required_validator_scope' default: '' - type: input name: subject message: 'Title of the commit (starting in lower case and without period) :' filter: 'required_validator_subject_strip' default: '' - type: input name: body message: 'Additional contextual message (Empty to skip) :' default: 'Issue: #...' filter: 'multiple_line_breaker' --- Signed-off-by: Adrian DC --- commitizen/commands/commit.py | 26 ++++++- commitizen/cz/utils.py | 25 +++++- docs/customization.md | 3 +- tests/test_cz_customize.py | 140 +++++++++++++++++++++++++++++++++- 4 files changed, 186 insertions(+), 8 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index f1714711a0..ceb394185c 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -11,7 +11,14 @@ from commitizen import factory, git, out from commitizen.config import BaseConfig from commitizen.cz.exceptions import CzException -from commitizen.cz.utils import get_backup_file_path +from commitizen.cz.utils import ( + get_backup_file_path, + multiple_line_breaker, + required_validator, + required_validator_scope, + required_validator_subject_strip, + required_validator_title_strip, +) from commitizen.exceptions import ( CommitError, CommitMessageLengthExceededError, @@ -55,6 +62,23 @@ def prompt_commit_questions(self) -> str: for question in filter(lambda q: q["type"] == "list", questions): question["use_shortcuts"] = self.config.settings["use_shortcuts"] + + for question in filter( + lambda q: isinstance(q.get("filter", None), str), questions + ): + if question["filter"] == "multiple_line_breaker": + question["filter"] = multiple_line_breaker + elif question["filter"] == "required_validator": + question["filter"] = required_validator + elif question["filter"] == "required_validator_scope": + question["filter"] = required_validator_scope + elif question["filter"] == "required_validator_subject_strip": + question["filter"] = required_validator_subject_strip + elif question["filter"] == "required_validator_title_strip": + question["filter"] = required_validator_title_strip + else: + raise NotAllowed(f"Unknown value filter: {question['filter']}") + try: answers = questionary.prompt(questions, style=cz.style) except ValueError as err: diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py index 7bc89673c6..e0e7bd8ddf 100644 --- a/commitizen/cz/utils.py +++ b/commitizen/cz/utils.py @@ -6,13 +6,34 @@ from commitizen.cz import exceptions -def required_validator(answer, msg=None): +def required_validator(answer: str, msg=None) -> str: if not answer: raise exceptions.AnswerRequiredError(msg) return answer -def multiple_line_breaker(answer, sep="|"): +def required_validator_scope( + answer: str, + msg: str = "! Error: Scope is required", +) -> str: + return required_validator(answer, msg) + + +def required_validator_subject_strip( + answer: str, + msg: str = "! Error: Subject is required", +) -> str: + return required_validator(answer.strip(".").strip(), msg) + + +def required_validator_title_strip( + answer: str, + msg: str = "! Error: Title is required", +) -> str: + return required_validator(answer.strip(".").strip(), msg) + + +def multiple_line_breaker(answer: str, sep: str = "|") -> str: return "\n".join(line.strip() for line in answer.split(sep) if line) diff --git a/docs/customization.md b/docs/customization.md index 0561e7e239..c5ac282c18 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -176,7 +176,8 @@ commitizen: | `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. | | `default` | `Any` | `None` | (OPTIONAL) The default value for this question. | | `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. **(Work in Progress)** | -| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`. | +| `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. The string is the name of a `commitizen.cz.utils.NAME(answer...)` function like `multiple_line_breaker` | +| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`. | [different-question-types]: https://github.com/tmbo/questionary#different-question-types #### Shortcut keys diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 418c475fec..74643fa909 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -1,8 +1,17 @@ import pytest +from pytest_mock import MockFixture +from commitizen import cmd, commands from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig from commitizen.cz.customize import CustomizeCommitsCz -from commitizen.exceptions import MissingCzCustomizeConfigError +from commitizen.cz.utils import ( + multiple_line_breaker, + required_validator, + required_validator_scope, + required_validator_subject_strip, + required_validator_title_strip, +) +from commitizen.exceptions import MissingCzCustomizeConfigError, NotAllowed TOML_STR = r""" [tool.commitizen] @@ -36,10 +45,17 @@ ] message = "Select the type of change you are committing" + [[tool.commitizen.customize.questions]] + type = "input" + name = "subject" + message = "Subject." + filter = "required_validator_subject_strip" + [[tool.commitizen.customize.questions]] type = "input" name = "message" message = "Body." + filter = "multiple_line_breaker" [[tool.commitizen.customize.questions]] type = "confirm" @@ -89,10 +105,17 @@ ], "message": "Select the type of change you are committing" }, + { + "type": "input", + "name": "subject", + "message": "Subject.", + "filter": "required_validator_subject_strip" + }, { "type": "input", "name": "message", - "message": "Body." + "message": "Body.", + "filter": "multiple_line_breaker" }, { "type": "confirm", @@ -139,9 +162,14 @@ - value: bug fix name: 'bug fix: A bug fix.' message: Select the type of change you are committing + - type: input + name: subject + message: Subject. + filter: required_validator_subject_strip - type: input name: message message: Body. + filter: multiple_line_breaker - type: confirm name: show_message message: Do you want to add body message in commit? @@ -330,6 +358,13 @@ """ +@pytest.fixture +def staging_is_clean(mocker: MockFixture, tmp_git_project): + is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean") + is_staging_clean_mock.return_value = False + return tmp_git_project + + @pytest.fixture( params=[ TomlConfig(data=TOML_STR, path="not_exist.toml"), @@ -346,6 +381,15 @@ def config(request): return request.param +@pytest.fixture( + params=[ + YAMLConfig(data=YAML_STR, path="not_exist.yaml"), + ] +) +def config_filters(request): + return request.param + + @pytest.fixture( params=[ TomlConfig(data=TOML_STR_INFO_PATH, path="not_exist.toml"), @@ -437,7 +481,7 @@ def test_change_type_order_unicode(config_with_unicode): ] -def test_questions(config): +def test_questions_default(config): cz = CustomizeCommitsCz(config) questions = cz.questions() expected_questions = [ @@ -450,7 +494,18 @@ def test_questions(config): ], "message": "Select the type of change you are committing", }, - {"type": "input", "name": "message", "message": "Body."}, + { + "type": "input", + "name": "subject", + "message": "Subject.", + "filter": "required_validator_subject_strip", + }, + { + "type": "input", + "name": "message", + "message": "Body.", + "filter": "multiple_line_breaker", + }, { "type": "confirm", "name": "show_message", @@ -460,6 +515,83 @@ def test_questions(config): assert list(questions) == expected_questions +@pytest.mark.usefixtures("staging_is_clean") +def test_questions_filter_default(config, mocker: MockFixture): + is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean") + is_staging_clean_mock.return_value = False + + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "change_type": "feature", + "subject": "user created", + "message": "body of the commit", + "show_message": True, + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) + + commands.Commit(config, {})() + + prompts_questions = prompt_mock.call_args[0][0] + assert prompts_questions[0]["type"] == "list" + assert prompts_questions[0]["name"] == "change_type" + assert prompts_questions[0]["use_shortcuts"] is False + assert prompts_questions[1]["type"] == "input" + assert prompts_questions[1]["name"] == "subject" + assert prompts_questions[1]["filter"] == required_validator_subject_strip + assert prompts_questions[2]["type"] == "input" + assert prompts_questions[2]["name"] == "message" + assert prompts_questions[2]["filter"] == multiple_line_breaker + assert prompts_questions[3]["type"] == "confirm" + assert prompts_questions[3]["name"] == "show_message" + + +@pytest.mark.usefixtures("staging_is_clean") +def test_questions_filter_values(config_filters, mocker: MockFixture): + is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean") + is_staging_clean_mock.return_value = False + + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "change_type": "feature", + "subject": "user created", + "message": "body of the commit", + "show_message": True, + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) + + commit_cmd = commands.Commit(config_filters, {}) + + assert isinstance(commit_cmd.cz, CustomizeCommitsCz) + + for filter_desc in [ + ("multiple_line_breaker", multiple_line_breaker), + ("required_validator", required_validator), + ("required_validator_scope", required_validator_scope), + ("required_validator_subject_strip", required_validator_subject_strip), + ("required_validator_title_strip", required_validator_title_strip), + ]: + commit_cmd.cz.custom_settings["questions"][1]["filter"] = filter_desc[0] # type: ignore[index] + commit_cmd() + + assert filter_desc[1]("input") + + prompts_questions = prompt_mock.call_args[0][0] + assert prompts_questions[1]["filter"] == filter_desc[1] + + for filter_name in [ + "", + "faulty_value", + ]: + commit_cmd.cz.custom_settings["questions"][1]["filter"] = filter_name # type: ignore[index] + + with pytest.raises(NotAllowed): + commit_cmd() + + def test_questions_unicode(config_with_unicode): cz = CustomizeCommitsCz(config_with_unicode) questions = cz.questions() From 2b00aff08d85faf9bc210789dfd391ecf0a25b41 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Thu, 15 Aug 2024 22:18:57 +0200 Subject: [PATCH 10/13] fix(pre-commit-hooks)!: move 'check' to 'args' for global options Details: 'args: [...]' is not able to access '-nr NO_RAISE' global arguments for example from a arguments configuration --- Signed-off-by: Adrian DC --- .pre-commit-hooks.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 2a3a088484..af5a80b976 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -4,8 +4,8 @@ Check whether the current commit message follows committing rules. Allow empty commit messages by default, because they typically indicate to Git that the commit should be aborted. - entry: cz check - args: [--allow-abort, --commit-msg-file] + entry: cz + args: [check, --allow-abort, --commit-msg-file] stages: [commit-msg] language: python language_version: python3 @@ -18,8 +18,8 @@ default branch on the origin repository. Useful for checking messages after the fact (e.g., pre-push or in CI) without an expensive check of the entire repository history. - entry: cz check - args: [--rev-range, origin/HEAD..HEAD] + entry: cz + args: [check, --rev-range, origin/HEAD..HEAD] always_run: true pass_filenames: false language: python From a58275f0b2db027763f28f5fde2f3609ab09cb32 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Thu, 15 Aug 2024 22:21:25 +0200 Subject: [PATCH 11/13] fix(pre-commit-hooks)!: allow empty ranges in 'commitizen-branch' Details: Pushing the same history from 'develop' to 'staging' for example fails because range 'origin/HEAD..HEAD' is empty > No commit found with range: 'origin/HEAD..HEAD' --- Signed-off-by: Adrian DC --- .pre-commit-hooks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index af5a80b976..d143aa3bdf 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -19,7 +19,7 @@ the fact (e.g., pre-push or in CI) without an expensive check of the entire repository history. entry: cz - args: [check, --rev-range, origin/HEAD..HEAD] + args: [--no-raise, '3', check, --rev-range, origin/HEAD..HEAD] always_run: true pass_filenames: false language: python From c4be47fc58a143106defd3201025e8a001c54d18 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Tue, 13 Aug 2024 19:11:18 +0200 Subject: [PATCH 12/13] ci(gitlab-ci): create 'codestyle' and 'test' job for 'gcil' usage Signed-off-by: Adrian DC --- .gitlab-ci.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..3ef86402ee --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,31 @@ +stages: + - prepare + - test + +codestyle: + stage: prepare + image: python:3.12 + variables: + POETRY_VIRTUALENVS_CREATE: false + before_script: + # Install dependencies + - pip install poetry + - poetry install + script: + # Format sources + - ./scripts/format + +tests: + stage: test + image: python:3.12 + variables: + POETRY_VIRTUALENVS_CREATE: false + before_script: + # Install dependencies + - pip install poetry + - poetry install + script: + # Format sources + - ./scripts/format + # Run tests + - ./scripts/test From d3f601d3a96056babf2a78ad60408854380af884 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Tue, 13 Aug 2024 12:21:23 +0200 Subject: [PATCH 13/13] build(commitizen): isolate version to '3.31.0+adriandc.20241116' Signed-off-by: Adrian DC --- commitizen/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 0cba0fb82a..cfddc4579c 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "3.31.0" +__version__ = "3.31.0+adriandc.20241116"