Skip to content

Commit 6828582

Browse files
committed
feat: implement message length limit for commit messages
1 parent a69d441 commit 6828582

File tree

7 files changed

+144
-6
lines changed

7 files changed

+144
-6
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
{
@@ -492,7 +491,6 @@ def __call__(
492491
{
493492
"name": ["-l", "--message-length-limit"],
494493
"type": int,
495-
"default": 0,
496494
"help": "length limit of the commit message; 0 for no limit",
497495
},
498496
],

commitizen/commands/check.py

Lines changed: 14 additions & 2 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,
@@ -40,7 +41,13 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N
4041
self.allow_abort = bool(
4142
arguments.get("allow_abort", config.settings["allow_abort"])
4243
)
43-
self.max_msg_length = arguments.get("message_length_limit", 0)
44+
45+
# Use command line argument if provided, otherwise use config setting
46+
cmd_length_limit = arguments.get("message_length_limit")
47+
if cmd_length_limit is None:
48+
self.max_msg_length = config.settings.get("message_length_limit", 0)
49+
else:
50+
self.max_msg_length = cmd_length_limit
4451

4552
# we need to distinguish between None and [], which is a valid value
4653
allowed_prefixes = arguments.get("allowed_prefixes")
@@ -154,6 +161,11 @@ def _validate_commit_message(
154161
if self.max_msg_length:
155162
msg_len = len(commit_msg.partition("\n")[0].strip())
156163
if msg_len > self.max_msg_length:
157-
return False
164+
raise CommitMessageLengthExceededError(
165+
f"commit validation: failed!\n"
166+
f"commit message length exceeds the limit.\n"
167+
f'commit "": "{commit_msg}"\n'
168+
f"message length limit: {self.max_msg_length} (actual: {msg_len})"
169+
)
158170

159171
return bool(pattern.match(commit_msg))

commitizen/commands/commit.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ def _prompt_commit_questions(self) -> str:
8181

8282
message = cz.message(answers)
8383
message_len = len(message.partition("\n")[0].strip())
84-
message_length_limit = self.arguments.get("message_length_limit", 0)
84+
85+
86+
message_length_limit = self.arguments.get("message_length_limit")
87+
if message_length_limit is None:
88+
message_length_limit = self.config.settings.get("message_length_limit", 0)
89+
8590
if 0 < message_length_limit < message_len:
8691
raise CommitMessageLengthExceededError(
8792
f"Length of commit message exceeds limit ({message_len}/{message_length_limit})"

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
4950
name: str
5051
post_bump_hooks: list[str] | None
5152
pre_bump_hooks: list[str] | None
@@ -108,6 +109,7 @@ class Settings(TypedDict, total=False):
108109
"always_signoff": False,
109110
"template": None, # default provided by plugin
110111
"extras": {},
112+
"message_length_limit": 0, # 0 for no limit
111113
}
112114

113115
MAJOR = "MAJOR"

tests/commands/test_check_command.py

Lines changed: 61 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,6 +450,65 @@ 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()
456+
457+
458+
def test_check_command_with_config_message_length_limit(config, mocker: MockFixture):
459+
success_mock = mocker.patch("commitizen.out.success")
460+
message = "fix(scope): some commit message"
461+
462+
config.settings["message_length_limit"] = len(message) + 1
463+
464+
check_cmd = commands.Check(
465+
config=config,
466+
arguments={"message": message},
467+
)
468+
469+
check_cmd()
470+
success_mock.assert_called_once()
471+
472+
473+
def test_check_command_with_config_message_length_limit_exceeded(
474+
config, mocker: MockFixture
475+
):
476+
error_mock = mocker.patch("commitizen.out.error")
477+
message = "fix(scope): some commit message"
478+
479+
config.settings["message_length_limit"] = len(message) - 1
480+
481+
check_cmd = commands.Check(
482+
config=config,
483+
arguments={"message": message},
484+
)
485+
486+
with pytest.raises(CommitMessageLengthExceededError):
487+
check_cmd()
488+
error_mock.assert_called_once()
489+
490+
491+
def test_check_command_cli_overrides_config_message_length_limit(
492+
config, mocker: MockFixture
493+
):
494+
success_mock = mocker.patch("commitizen.out.success")
495+
message = "fix(scope): some commit message"
496+
497+
config.settings["message_length_limit"] = len(message) - 1
498+
499+
check_cmd = commands.Check(
500+
config=config,
501+
arguments={"message": message, "message_length_limit": len(message) + 1},
502+
)
503+
504+
check_cmd()
505+
success_mock.assert_called_once()
506+
507+
success_mock.reset_mock()
508+
check_cmd = commands.Check(
509+
config=config,
510+
arguments={"message": message, "message_length_limit": 0},
511+
)
512+
513+
check_cmd()
514+
success_mock.assert_called_once()

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": 0})()
615+
success_mock.assert_called_once()

tests/test_conf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
"always_signoff": False,
9696
"template": None,
9797
"extras": {},
98+
"message_length_limit": 0,
9899
}
99100

100101
_new_settings: dict[str, Any] = {
@@ -126,6 +127,7 @@
126127
"always_signoff": False,
127128
"template": None,
128129
"extras": {},
130+
"message_length_limit": 0,
129131
}
130132

131133

0 commit comments

Comments
 (0)