Skip to content

Commit 8fc9baa

Browse files
committed
fix: skip the header length check and configure header length
1 parent 02e7ff4 commit 8fc9baa

File tree

5 files changed

+204
-35
lines changed

5 files changed

+204
-35
lines changed

src/commitlint/cli.py

+51-6
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ def get_args() -> argparse.Namespace:
5959
group.add_argument("--from-hash", type=str, help="From commit hash")
6060
# --to-hash is optional
6161
parser.add_argument("--to-hash", type=str, help="To commit hash", default="HEAD")
62-
6362
# feature options
6463
parser.add_argument(
6564
"--skip-detail",
@@ -85,6 +84,14 @@ def get_args() -> argparse.Namespace:
8584
default=False,
8685
)
8786

87+
output_group.add_argument(
88+
"--max-header-length", help="commit message header max length"
89+
)
90+
output_group.add_argument(
91+
"--disable-header-length-check",
92+
action="store_true",
93+
help="disable header max length check",
94+
)
8895
# --verbose option is optional
8996
output_group.add_argument(
9097
"-v",
@@ -152,10 +159,13 @@ def _get_commit_message_from_file(filepath: str) -> str:
152159
return commit_message
153160

154161

162+
# pylint: disable=too-many-arguments
155163
def _handle_commit_message(
156164
commit_message: str,
157165
skip_detail: bool,
158166
hide_input: bool,
167+
max_header_length: int,
168+
disable_max_header_length_check: bool = False,
159169
strip_comments: bool = False,
160170
) -> None:
161171
"""
@@ -171,7 +181,13 @@ def _handle_commit_message(
171181
Raises:
172182
SystemExit: If the commit message is invalid.
173183
"""
174-
success, errors = lint_commit_message(commit_message, skip_detail, strip_comments)
184+
success, errors = lint_commit_message(
185+
commit_message=commit_message,
186+
skip_detail=skip_detail,
187+
strip_comments=strip_comments,
188+
max_header_length=max_header_length,
189+
disable_max_header_length=disable_max_header_length_check,
190+
)
175191

176192
if success:
177193
console.success(VALIDATION_SUCCESSFUL)
@@ -182,7 +198,11 @@ def _handle_commit_message(
182198

183199

184200
def _handle_multiple_commit_messages(
185-
commit_messages: List[str], skip_detail: bool, hide_input: bool
201+
commit_messages: List[str],
202+
skip_detail: bool,
203+
hide_input: bool,
204+
disable_max_header_length_check: bool,
205+
max_header_length: int,
186206
) -> None:
187207
"""
188208
Handles multiple commit messages, checks their validity, and prints the result.
@@ -198,7 +218,12 @@ def _handle_multiple_commit_messages(
198218
has_error = False
199219

200220
for commit_message in commit_messages:
201-
success, errors = lint_commit_message(commit_message, skip_detail)
221+
success, errors = lint_commit_message(
222+
commit_message,
223+
max_header_length=max_header_length,
224+
skip_detail=skip_detail,
225+
disable_max_header_length=disable_max_header_length_check,
226+
)
202227
if success:
203228
console.verbose("lint success")
204229
continue
@@ -224,6 +249,14 @@ def main() -> None:
224249
config.verbose = args.verbose
225250

226251
console.verbose("starting commitlint")
252+
253+
if args.max_header_length and args.disable_header_length_check:
254+
console.error(
255+
"--max-header-length and "
256+
"--disable-header-length-check can't be passed together"
257+
)
258+
raise CommitlintException
259+
227260
try:
228261
if args.file:
229262
console.verbose("commit message source: file")
@@ -232,13 +265,19 @@ def main() -> None:
232265
commit_message,
233266
skip_detail=args.skip_detail,
234267
hide_input=args.hide_input,
268+
disable_max_header_length_check=args.disable_header_length_check,
269+
max_header_length=args.max_header_length,
235270
strip_comments=True,
236271
)
237272
elif args.hash:
238273
console.verbose("commit message source: hash")
239274
commit_message = get_commit_message_of_hash(args.hash)
240275
_handle_commit_message(
241-
commit_message, skip_detail=args.skip_detail, hide_input=args.hide_input
276+
commit_message,
277+
skip_detail=args.skip_detail,
278+
hide_input=args.hide_input,
279+
max_header_length=args.max_header_length,
280+
disable_max_header_length_check=args.disable_header_length_check,
242281
)
243282
elif args.from_hash:
244283
console.verbose("commit message source: hash range")
@@ -249,12 +288,18 @@ def main() -> None:
249288
commit_messages,
250289
skip_detail=args.skip_detail,
251290
hide_input=args.hide_input,
291+
max_header_length=args.max_header_length,
292+
disable_max_header_length_check=args.disable_header_length_check,
252293
)
253294
else:
254295
console.verbose("commit message source: direct message")
255296
commit_message = args.commit_message.strip()
256297
_handle_commit_message(
257-
commit_message, skip_detail=args.skip_detail, hide_input=args.hide_input
298+
commit_message,
299+
skip_detail=args.skip_detail,
300+
hide_input=args.hide_input,
301+
max_header_length=args.max_header_length,
302+
disable_max_header_length_check=args.disable_header_length_check,
258303
)
259304
except CommitlintException as ex:
260305
console.error(f"{ex}")

src/commitlint/linter/_linter.py

+27-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .. import console
99
from .utils import is_ignored, remove_comments
1010
from .validators import (
11+
CommitValidator,
1112
HeaderLengthValidator,
1213
PatternValidator,
1314
SimplePatternValidator,
@@ -16,12 +17,18 @@
1617

1718

1819
def lint_commit_message(
19-
commit_message: str, skip_detail: bool = False, strip_comments: bool = False
20+
commit_message: str,
21+
max_header_length: int,
22+
skip_detail: bool = False,
23+
disable_max_header_length: bool = False,
24+
strip_comments: bool = False,
2025
) -> Tuple[bool, List[str]]:
2126
"""
2227
Lints a commit message.
2328
2429
Args:
30+
disable_max_header_length: flag to disable the max header length check
31+
max_header_length: maximum length of commit message header
2532
commit_message (str): The commit message to be linted.
2633
skip_detail (bool, optional): Whether to skip the detailed error linting
2734
(default is False).
@@ -47,16 +54,30 @@ def lint_commit_message(
4754
console.verbose("commit message ignored, skipping lint")
4855
return True, []
4956

57+
validator_instances: list[CommitValidator] = []
58+
59+
if not disable_max_header_length:
60+
validator_instances.append(
61+
HeaderLengthValidator(
62+
commit_message=commit_message,
63+
**{"max_header_length": max_header_length}, # type: ignore
64+
)
65+
)
66+
5067
# for skip_detail check
5168
if skip_detail:
5269
console.verbose("running simple validators for linting")
70+
71+
validator_instances.append(
72+
SimplePatternValidator(commit_message=commit_message)
73+
)
74+
5375
return run_validators(
54-
commit_message,
55-
validator_classes=[HeaderLengthValidator, SimplePatternValidator],
76+
validator_classes=validator_instances,
5677
fail_fast=True,
5778
)
5879

80+
validator_instances.append(PatternValidator(commit_message=commit_message))
81+
5982
console.verbose("running detailed validators for linting")
60-
return run_validators(
61-
commit_message, validator_classes=[HeaderLengthValidator, PatternValidator]
62-
)
83+
return run_validators(validator_classes=validator_instances)

src/commitlint/linter/validators.py

+26-17
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import re
88
from abc import ABC, abstractmethod
9-
from typing import List, Tuple, Type, Union
9+
from typing import Any, List, Tuple, Union, cast
1010

1111
from .. import console
1212
from ..constants import COMMIT_HEADER_MAX_LENGTH, COMMIT_TYPES
@@ -30,15 +30,15 @@
3030
class CommitValidator(ABC):
3131
"""Abstract Base validator for commit message."""
3232

33-
def __init__(self, commit_message: str) -> None:
33+
def __init__(self, commit_message: str, **kwargs: dict[str, Any]) -> None:
3434
self._commit_message = commit_message
3535
self._errors: List[str] = []
3636

3737
# start validation
38-
self.validate()
38+
self.validate(**kwargs)
3939

4040
@abstractmethod
41-
def validate(self) -> None:
41+
def validate(self, **kwargs: dict[str, Any]) -> None:
4242
"""Performs the validation."""
4343
raise NotImplementedError # pragma: no cover
4444

@@ -50,6 +50,7 @@ def is_valid(self) -> bool:
5050
"""Checks if there are any errors."""
5151
return len(self._errors) == 0
5252

53+
@property
5354
def errors(self) -> List[str]:
5455
"""Get the list of errors."""
5556
return self._errors
@@ -63,15 +64,23 @@ def commit_message(self) -> str:
6364
class HeaderLengthValidator(CommitValidator):
6465
"""Validator for checking commit header length."""
6566

66-
def validate(self) -> None:
67+
def validate(self, **kwargs: dict[str, Any]) -> None:
6768
"""
6869
Validates the length of the commit header.
6970
7071
Returns:
7172
None
7273
"""
74+
max_header_length = kwargs.get("max_header_length")
75+
76+
if max_header_length:
77+
header_length = cast(int, max_header_length)
78+
else:
79+
header_length = COMMIT_HEADER_MAX_LENGTH
80+
7381
header = self.commit_message.split("\n")[0]
74-
if len(header) > COMMIT_HEADER_MAX_LENGTH:
82+
83+
if len(header) > int(header_length):
7584
self.add_error(HEADER_LENGTH_ERROR)
7685

7786

@@ -90,7 +99,7 @@ class SimplePatternValidator(CommitValidator):
9099
r"((\n\n(?P<body>.*))|(\s*))?$"
91100
)
92101

93-
def validate(self) -> None:
102+
def validate(self, **kwargs: dict[str, Any]) -> None:
94103
"""
95104
Validates the commit message using the regex pattern.
96105
@@ -118,7 +127,7 @@ class PatternValidator(CommitValidator):
118127
r"(((?P<body>.*))|(\s*))?$"
119128
)
120129

121-
def validate(self) -> None:
130+
def validate(self, **kwargs: dict[str, Any]) -> None:
122131
"""
123132
Validates the commit message using the regex pattern.
124133
@@ -286,8 +295,7 @@ def validate_description_no_full_stop_at_end(
286295

287296

288297
def run_validators(
289-
commit_message: str,
290-
validator_classes: List[Type[CommitValidator]],
298+
validator_classes: List[CommitValidator],
291299
fail_fast: bool = False,
292300
) -> Tuple[bool, List[str]]:
293301
"""Runs the provided validators for the commit message.
@@ -307,17 +315,18 @@ def run_validators(
307315
success = True
308316
errors: List[str] = []
309317

310-
for validator_class in validator_classes:
311-
console.verbose(f"running validator {validator_class.__name__}")
312-
validator = validator_class(commit_message)
313-
if not validator.is_valid():
314-
console.verbose(f"{validator_class.__name__}: validation failed")
318+
for validator_instance in validator_classes:
319+
console.verbose(f"running validator {validator_instance.__class__.__name__}")
320+
if not validator_instance.is_valid():
321+
console.verbose(
322+
f"{validator_instance.__class__.__name__}: validation failed"
323+
)
315324
if fail_fast:
316325
console.verbose(f"fail_fast: {fail_fast}, skipping further validations")
317326
# returning immediately if any error occurs.
318-
return False, validator.errors()
327+
return False, validator_instance.errors
319328

320329
success = False
321-
errors.extend(validator.errors())
330+
errors.extend(validator_instance.errors)
322331

323332
return success, errors

tests/test_cli.py

+43
Original file line numberDiff line numberDiff line change
@@ -459,3 +459,46 @@ def test__invalid_commit_message_with_hash_range_in_quiet(
459459

460460
mock_stderr_write.assert_not_called()
461461
mock_stdout_write.assert_not_called()
462+
463+
464+
class TestCliHeaderLength:
465+
@patch(
466+
"commitlint.cli.get_args",
467+
return_value=ArgsMock(
468+
commit_message="feat: add method 'formatEnglishDate' \
469+
and 'formatEnglishDateInNepali' (#165)",
470+
quiet=True,
471+
max_header_length=20,
472+
),
473+
)
474+
@patch("sys.stdout.write")
475+
@patch("sys.stderr.write")
476+
def test__main__quiet_option_with_header_max_length(
477+
self, mock_stderr_write, mock_stdout_write, *_
478+
):
479+
with pytest.raises(SystemExit):
480+
main()
481+
482+
mock_stderr_write.assert_not_called()
483+
mock_stdout_write.assert_not_called()
484+
485+
@patch(
486+
"commitlint.cli.get_args",
487+
return_value=ArgsMock(
488+
commit_message="feat: add method 'formatEnglishDate' \
489+
and 'formatEnglishDateInNepali' (#165)",
490+
quiet=True,
491+
max_header_length=20,
492+
disable_header_length_check=True,
493+
),
494+
)
495+
@patch("sys.stdout.write")
496+
@patch("sys.stderr.write")
497+
def test__with_both_header_max_length_and_disabled_max_header_length_check(
498+
self, mock_stderr_write, mock_stdout_write, *_
499+
):
500+
with pytest.raises(CommitlintException):
501+
main()
502+
503+
mock_stderr_write.assert_not_called()
504+
mock_stdout_write.assert_not_called()

0 commit comments

Comments
 (0)