Skip to content

Commit 647e791

Browse files
Narwhal-fishLee-W
authored andcommitted
feat: add config option for line length warning
1 parent fefc48c commit 647e791

File tree

9 files changed

+143
-13
lines changed

9 files changed

+143
-13
lines changed

commitizen/cli.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ def __call__(
160160
{
161161
"name": ["-l", "--message-length-limit"],
162162
"type": int,
163-
"default": 0,
164163
"help": "length limit of the commit message; 0 for no limit",
165164
},
166165
{
@@ -499,7 +498,6 @@ def __call__(
499498
{
500499
"name": ["-l", "--message-length-limit"],
501500
"type": int,
502-
"default": 0,
503501
"help": "length limit of the commit message; 0 for no limit",
504502
},
505503
],

commitizen/commands/check.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from commitizen import factory, git, out
88
from commitizen.config import BaseConfig
99
from commitizen.exceptions import (
10+
CommitMessageLengthExceededError,
1011
InvalidCommandArgumentError,
1112
InvalidCommitMessageError,
1213
NoCommitsFoundError,
@@ -18,7 +19,7 @@ class CheckArgs(TypedDict, total=False):
1819
commit_msg: str
1920
rev_range: str
2021
allow_abort: bool
21-
message_length_limit: int
22+
message_length_limit: int | None
2223
allowed_prefixes: list[str]
2324
message: str
2425
use_default_range: bool
@@ -41,8 +42,11 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N
4142
self.allow_abort = bool(
4243
arguments.get("allow_abort", config.settings["allow_abort"])
4344
)
45+
4446
self.use_default_range = bool(arguments.get("use_default_range"))
45-
self.max_msg_length = arguments.get("message_length_limit", 0)
47+
self.max_msg_length = arguments.get(
48+
"message_length_limit", config.settings.get("message_length_limit", None)
49+
)
4650

4751
# we need to distinguish between None and [], which is a valid value
4852
allowed_prefixes = arguments.get("allowed_prefixes")
@@ -88,7 +92,7 @@ def __call__(self) -> None:
8892
invalid_msgs_content = "\n".join(
8993
f'commit "{commit.rev}": "{commit.message}"'
9094
for commit in commits
91-
if not self._validate_commit_message(commit.message, pattern)
95+
if not self._validate_commit_message(commit.message, pattern, commit.rev)
9296
)
9397
if invalid_msgs_content:
9498
# TODO: capitalize the first letter of the error message for consistency in v5
@@ -153,17 +157,22 @@ def _filter_comments(msg: str) -> str:
153157
return "\n".join(lines)
154158

155159
def _validate_commit_message(
156-
self, commit_msg: str, pattern: re.Pattern[str]
160+
self, commit_msg: str, pattern: re.Pattern[str], commit_hash: str
157161
) -> bool:
158162
if not commit_msg:
159163
return self.allow_abort
160164

161165
if any(map(commit_msg.startswith, self.allowed_prefixes)):
162166
return True
163167

164-
if self.max_msg_length:
168+
if self.max_msg_length is not None:
165169
msg_len = len(commit_msg.partition("\n")[0].strip())
166170
if msg_len > self.max_msg_length:
167-
return False
171+
raise CommitMessageLengthExceededError(
172+
f"commit validation: failed!\n"
173+
f"commit message length exceeds the limit.\n"
174+
f'commit "{commit_hash}": "{commit_msg}"\n'
175+
f"message length limit: {self.max_msg_length} (actual: {msg_len})"
176+
)
168177

169178
return bool(pattern.match(commit_msg))

commitizen/commands/commit.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class CommitArgs(TypedDict, total=False):
3333
dry_run: bool
3434
edit: bool
3535
extra_cli_args: str
36-
message_length_limit: int
36+
message_length_limit: int | None
3737
no_retry: bool
3838
signoff: bool
3939
write_message_to_file: Path | None
@@ -80,8 +80,11 @@ def _get_message_by_prompt_commit_questions(self) -> str:
8080
raise NoAnswersError()
8181

8282
message = self.cz.message(answers)
83-
if limit := self.arguments.get("message_length_limit", 0):
83+
if limit := self.arguments.get(
84+
"message_length_limit", self.config.settings.get("message_length_limit", 0)
85+
):
8486
self._validate_subject_length(message=message, length_limit=limit)
87+
8588
return message
8689

8790
def _validate_subject_length(self, *, message: str, length_limit: int) -> None:

commitizen/defaults.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class Settings(TypedDict, total=False):
4646
ignored_tag_formats: Sequence[str]
4747
legacy_tag_formats: Sequence[str]
4848
major_version_zero: bool
49+
message_length_limit: int | None
4950
name: str
5051
post_bump_hooks: list[str] | None
5152
pre_bump_hooks: list[str] | None
@@ -109,6 +110,7 @@ class Settings(TypedDict, total=False):
109110
"always_signoff": False,
110111
"template": None, # default provided by plugin
111112
"extras": {},
113+
"message_length_limit": None, # None for no limit
112114
}
113115

114116
MAJOR = "MAJOR"

poetry.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ dependencies = [
1818
"colorama (>=0.4.1,<1.0)",
1919
"termcolor (>=1.1.0,<4.0.0)",
2020
"packaging>=19",
21-
"tomlkit (>=0.5.3,<1.0.0)",
21+
"tomlkit (>=0.8.0,<1.0.0)",
2222
"jinja2>=2.10.3",
2323
"pyyaml>=3.08",
2424
"argcomplete >=1.12.1,<3.7",

tests/commands/test_check_command.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from commitizen import cli, commands, git
1010
from commitizen.exceptions import (
11+
CommitMessageLengthExceededError,
1112
InvalidCommandArgumentError,
1213
InvalidCommitMessageError,
1314
NoCommitsFoundError,
@@ -449,7 +450,7 @@ def test_check_command_with_message_length_limit_exceeded(config, mocker: MockFi
449450
arguments={"message": message, "message_length_limit": len(message) - 1},
450451
)
451452

452-
with pytest.raises(InvalidCommitMessageError):
453+
with pytest.raises(CommitMessageLengthExceededError):
453454
check_cmd()
454455
error_mock.assert_called_once()
455456

@@ -460,3 +461,59 @@ def test_check_command_with_amend_prefix_default(config, mocker: MockFixture):
460461

461462
check_cmd()
462463
success_mock.assert_called_once()
464+
465+
466+
def test_check_command_with_config_message_length_limit(config, mocker: MockFixture):
467+
success_mock = mocker.patch("commitizen.out.success")
468+
message = "fix(scope): some commit message"
469+
470+
config.settings["message_length_limit"] = len(message) + 1
471+
472+
check_cmd = commands.Check(
473+
config=config,
474+
arguments={"message": message},
475+
)
476+
477+
check_cmd()
478+
success_mock.assert_called_once()
479+
480+
481+
def test_check_command_with_config_message_length_limit_exceeded(
482+
config, mocker: MockFixture
483+
):
484+
error_mock = mocker.patch("commitizen.out.error")
485+
message = "fix(scope): some commit message"
486+
487+
config.settings["message_length_limit"] = len(message) - 1
488+
489+
check_cmd = commands.Check(
490+
config=config,
491+
arguments={"message": message},
492+
)
493+
494+
with pytest.raises(CommitMessageLengthExceededError):
495+
check_cmd()
496+
error_mock.assert_called_once()
497+
498+
499+
def test_check_command_cli_overrides_config_message_length_limit(
500+
config, mocker: MockFixture
501+
):
502+
success_mock = mocker.patch("commitizen.out.success")
503+
message = "fix(scope): some commit message"
504+
505+
config.settings["message_length_limit"] = len(message) - 1
506+
507+
check_cmd = commands.Check(
508+
config=config,
509+
arguments={"message": message, "message_length_limit": len(message) + 1},
510+
)
511+
512+
check_cmd()
513+
success_mock.assert_called_once()
514+
515+
success_mock.reset_mock()
516+
check_cmd = commands.Check(
517+
config=config,
518+
arguments={"message": message, "message_length_limit": None},
519+
)

tests/commands/test_commit_command.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,62 @@ def test_commit_when_nothing_added_to_commit(config, mocker: MockFixture, out):
554554

555555
commit_mock.assert_called_once()
556556
error_mock.assert_called_once_with(out)
557+
558+
559+
@pytest.mark.usefixtures("staging_is_clean")
560+
def test_commit_command_with_config_message_length_limit(config, mocker: MockFixture):
561+
prompt_mock = mocker.patch("questionary.prompt")
562+
prefix = "feat"
563+
subject = "random subject"
564+
message_length = len(prefix) + len(": ") + len(subject)
565+
prompt_mock.return_value = {
566+
"prefix": prefix,
567+
"subject": subject,
568+
"scope": "",
569+
"is_breaking_change": False,
570+
"body": "random body",
571+
"footer": "random footer",
572+
}
573+
574+
commit_mock = mocker.patch("commitizen.git.commit")
575+
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
576+
success_mock = mocker.patch("commitizen.out.success")
577+
578+
config.settings["message_length_limit"] = message_length
579+
commands.Commit(config, {})()
580+
success_mock.assert_called_once()
581+
582+
config.settings["message_length_limit"] = message_length - 1
583+
with pytest.raises(CommitMessageLengthExceededError):
584+
commands.Commit(config, {})()
585+
586+
587+
@pytest.mark.usefixtures("staging_is_clean")
588+
def test_commit_command_cli_overrides_config_message_length_limit(
589+
config, mocker: MockFixture
590+
):
591+
prompt_mock = mocker.patch("questionary.prompt")
592+
prefix = "feat"
593+
subject = "random subject"
594+
message_length = len(prefix) + len(": ") + len(subject)
595+
prompt_mock.return_value = {
596+
"prefix": prefix,
597+
"subject": subject,
598+
"scope": "",
599+
"is_breaking_change": False,
600+
"body": "random body",
601+
"footer": "random footer",
602+
}
603+
604+
commit_mock = mocker.patch("commitizen.git.commit")
605+
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
606+
success_mock = mocker.patch("commitizen.out.success")
607+
608+
config.settings["message_length_limit"] = message_length - 1
609+
610+
commands.Commit(config, {"message_length_limit": message_length})()
611+
success_mock.assert_called_once()
612+
613+
success_mock.reset_mock()
614+
commands.Commit(config, {"message_length_limit": None})()
615+
success_mock.assert_called_once()

tests/test_conf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"always_signoff": False,
106106
"template": None,
107107
"extras": {},
108+
"message_length_limit": None,
108109
}
109110

110111
_new_settings: dict[str, Any] = {
@@ -143,6 +144,7 @@
143144
"always_signoff": False,
144145
"template": None,
145146
"extras": {},
147+
"message_length_limit": None,
146148
}
147149

148150

0 commit comments

Comments
 (0)