Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5011a91

Browse files
committed
feat(commit): implement questions 'filter' support with evaluations
Supported APIs: Common Python, commitizen.cz.utils.* functions Example YAML configurations: --- commitizen: name: cz_customize customize: questions: - ... - type: input name: scope message: 'Scope of the change :' filter: 'lambda text: commitizen.cz.utils.required_validator(text, msg="! Error: Scope is required")' default: '' - type: input name: subject message: 'Title of the commit (starting in lower case and without period) :' filter: 'lambda text: commitizen.cz.utils.required_validator(text.strip(".").strip(), msg="! Error: Title is required")' default: '' - type: input name: body message: 'Additional contextual message (Empty to skip) :' default: 'Issue: #...' filter: 'commitizen.cz.utils.multiple_line_breaker' --- Signed-off-by: Adrian DC <radian.dc@gmail.com>
1 parent 7258073 commit 5011a91

File tree

3 files changed

+95
-5
lines changed

3 files changed

+95
-5
lines changed

‎commitizen/commands/commit.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
from commitizen import factory, git, out
99
from commitizen.config import BaseConfig
1010
from commitizen.cz.exceptions import CzException
11-
from commitizen.cz.utils import get_backup_file_path
11+
from commitizen.cz.utils import (
12+
get_backup_file_path,
13+
multiple_line_breaker,
14+
)
1215
from commitizen.exceptions import (
1316
CommitError,
1417
CommitMessageLengthExceededError,
@@ -52,6 +55,19 @@ def prompt_commit_questions(self) -> str:
5255

5356
for question in filter(lambda q: q["type"] == "list", questions):
5457
question["use_shortcuts"] = self.config.settings["use_shortcuts"]
58+
59+
# Import allowed modules for 'filter'
60+
global commitizen
61+
import commitizen.cz.utils
62+
63+
for question in filter(
64+
lambda q: isinstance(q.get("filter", None), str), questions
65+
):
66+
question_filter = [
67+
multiple_line_breaker(question["filter"].replace("\\n", "\n"))
68+
]
69+
question["filter"] = eval("\n".join(question_filter))
70+
5571
try:
5672
answers = questionary.prompt(questions, style=cz.style)
5773
except ValueError as err:

‎docs/customization.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ commitizen:
175175
| `message` | `str` | `None` | Detail description for the question. |
176176
| `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. |
177177
| `default` | `Any` | `None` | (OPTIONAL) The default value for this question. |
178-
| `filter` | `str` | `None` | (Optional) Validator for user's answer. **(Work in Progress)** |
178+
| `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. The string is evaluated into a Python function, either use `commitizen.cz.utils.*` or lambda functions like `lambda text: text.strip(".").strip()` |
179179
[different-question-types]: https://github.com/tmbo/questionary#different-question-types
180180

181181
#### Shortcut keys

‎tests/test_cz_customize.py‎

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
from types import LambdaType
2+
13
import pytest
4+
from pytest_mock import MockFixture
25

6+
from commitizen import cmd, commands
37
from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig
48
from commitizen.cz.customize import CustomizeCommitsCz
9+
from commitizen.cz.utils import multiple_line_breaker
510
from commitizen.exceptions import MissingCzCustomizeConfigError
611

712
TOML_STR = r"""
@@ -36,10 +41,17 @@
3641
]
3742
message = "Select the type of change you are committing"
3843
44+
[[tool.commitizen.customize.questions]]
45+
type = "input"
46+
name = "subject"
47+
message = "Subject."
48+
filter = "lambda text: commitizen.cz.utils.required_validator(text.strip(\".\").strip(), msg=\"! Error: Subject is required\")"
49+
3950
[[tool.commitizen.customize.questions]]
4051
type = "input"
4152
name = "message"
4253
message = "Body."
54+
filter = "commitizen.cz.utils.multiple_line_breaker"
4355
4456
[[tool.commitizen.customize.questions]]
4557
type = "confirm"
@@ -89,10 +101,17 @@
89101
],
90102
"message": "Select the type of change you are committing"
91103
},
104+
{
105+
"type": "input",
106+
"name": "subject",
107+
"message": "Subject.",
108+
"filter": "lambda text: commitizen.cz.utils.required_validator(text.strip(\".\").strip(), msg=\"! Error: Subject is required\")"
109+
},
92110
{
93111
"type": "input",
94112
"name": "message",
95-
"message": "Body."
113+
"message": "Body.",
114+
"filter": "commitizen.cz.utils.multiple_line_breaker"
96115
},
97116
{
98117
"type": "confirm",
@@ -139,9 +158,14 @@
139158
- value: bug fix
140159
name: 'bug fix: A bug fix.'
141160
message: Select the type of change you are committing
161+
- type: input
162+
name: subject
163+
message: Subject.
164+
filter: 'lambda text: commitizen.cz.utils.required_validator(text.strip(".").strip(), msg="! Error: Subject is required")'
142165
- type: input
143166
name: message
144167
message: Body.
168+
filter: 'commitizen.cz.utils.multiple_line_breaker'
145169
- type: confirm
146170
name: show_message
147171
message: Do you want to add body message in commit?
@@ -330,6 +354,13 @@
330354
"""
331355

332356

357+
@pytest.fixture
358+
def staging_is_clean(mocker: MockFixture, tmp_git_project):
359+
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
360+
is_staging_clean_mock.return_value = False
361+
return tmp_git_project
362+
363+
333364
@pytest.fixture(
334365
params=[
335366
TomlConfig(data=TOML_STR, path="not_exist.toml"),
@@ -437,7 +468,7 @@ def test_change_type_order_unicode(config_with_unicode):
437468
]
438469

439470

440-
def test_questions(config):
471+
def test_questions_default(config):
441472
cz = CustomizeCommitsCz(config)
442473
questions = cz.questions()
443474
expected_questions = [
@@ -450,7 +481,18 @@ def test_questions(config):
450481
],
451482
"message": "Select the type of change you are committing",
452483
},
453-
{"type": "input", "name": "message", "message": "Body."},
484+
{
485+
"type": "input",
486+
"name": "subject",
487+
"message": "Subject.",
488+
"filter": 'lambda text: commitizen.cz.utils.required_validator(text.strip(".").strip(), msg="! Error: Subject is required")',
489+
},
490+
{
491+
"type": "input",
492+
"name": "message",
493+
"message": "Body.",
494+
"filter": "commitizen.cz.utils.multiple_line_breaker",
495+
},
454496
{
455497
"type": "confirm",
456498
"name": "show_message",
@@ -460,6 +502,38 @@ def test_questions(config):
460502
assert list(questions) == expected_questions
461503

462504

505+
@pytest.mark.usefixtures("staging_is_clean")
506+
def test_questions_filter(config, mocker: MockFixture):
507+
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
508+
is_staging_clean_mock.return_value = False
509+
510+
prompt_mock = mocker.patch("questionary.prompt")
511+
prompt_mock.return_value = {
512+
"change_type": "feature",
513+
"subject": "user created",
514+
"message": "body of the commit",
515+
"show_message": True,
516+
}
517+
518+
commit_mock = mocker.patch("commitizen.git.commit")
519+
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
520+
521+
commands.Commit(config, {})()
522+
523+
prompts_questions = prompt_mock.call_args[0][0]
524+
assert prompts_questions[0]["type"] == "list"
525+
assert prompts_questions[0]["name"] == "change_type"
526+
assert prompts_questions[0]["use_shortcuts"] is False
527+
assert prompts_questions[1]["type"] == "input"
528+
assert prompts_questions[1]["name"] == "subject"
529+
assert type(prompts_questions[1]["filter"]) is LambdaType
530+
assert prompts_questions[2]["type"] == "input"
531+
assert prompts_questions[2]["name"] == "message"
532+
assert prompts_questions[2]["filter"] == multiple_line_breaker
533+
assert prompts_questions[3]["type"] == "confirm"
534+
assert prompts_questions[3]["name"] == "show_message"
535+
536+
463537
def test_questions_unicode(config_with_unicode):
464538
cz = CustomizeCommitsCz(config_with_unicode)
465539
questions = cz.questions()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /