Skip to content

Commit

Permalink
fix: skip the header length check and configure header length
Browse files Browse the repository at this point in the history
  • Loading branch information
golesuman committed Dec 4, 2024
1 parent 02e7ff4 commit 8fc9baa
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 35 deletions.
57 changes: 51 additions & 6 deletions src/commitlint/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ def get_args() -> argparse.Namespace:
group.add_argument("--from-hash", type=str, help="From commit hash")
# --to-hash is optional
parser.add_argument("--to-hash", type=str, help="To commit hash", default="HEAD")

# feature options
parser.add_argument(
"--skip-detail",
Expand All @@ -85,6 +84,14 @@ def get_args() -> argparse.Namespace:
default=False,
)

output_group.add_argument(
"--max-header-length", help="commit message header max length"
)
output_group.add_argument(
"--disable-header-length-check",
action="store_true",
help="disable header max length check",
)
# --verbose option is optional
output_group.add_argument(
"-v",
Expand Down Expand Up @@ -152,10 +159,13 @@ def _get_commit_message_from_file(filepath: str) -> str:
return commit_message


# pylint: disable=too-many-arguments
def _handle_commit_message(
commit_message: str,
skip_detail: bool,
hide_input: bool,
max_header_length: int,
disable_max_header_length_check: bool = False,
strip_comments: bool = False,
) -> None:
"""
Expand All @@ -171,7 +181,13 @@ def _handle_commit_message(
Raises:
SystemExit: If the commit message is invalid.
"""
success, errors = lint_commit_message(commit_message, skip_detail, strip_comments)
success, errors = lint_commit_message(
commit_message=commit_message,
skip_detail=skip_detail,
strip_comments=strip_comments,
max_header_length=max_header_length,
disable_max_header_length=disable_max_header_length_check,
)

if success:
console.success(VALIDATION_SUCCESSFUL)
Expand All @@ -182,7 +198,11 @@ def _handle_commit_message(


def _handle_multiple_commit_messages(
commit_messages: List[str], skip_detail: bool, hide_input: bool
commit_messages: List[str],
skip_detail: bool,
hide_input: bool,
disable_max_header_length_check: bool,
max_header_length: int,
) -> None:
"""
Handles multiple commit messages, checks their validity, and prints the result.
Expand All @@ -198,7 +218,12 @@ def _handle_multiple_commit_messages(
has_error = False

for commit_message in commit_messages:
success, errors = lint_commit_message(commit_message, skip_detail)
success, errors = lint_commit_message(
commit_message,
max_header_length=max_header_length,
skip_detail=skip_detail,
disable_max_header_length=disable_max_header_length_check,
)
if success:
console.verbose("lint success")
continue
Expand All @@ -224,6 +249,14 @@ def main() -> None:
config.verbose = args.verbose

console.verbose("starting commitlint")

if args.max_header_length and args.disable_header_length_check:
console.error(
"--max-header-length and "
"--disable-header-length-check can't be passed together"
)
raise CommitlintException

try:
if args.file:
console.verbose("commit message source: file")
Expand All @@ -232,13 +265,19 @@ def main() -> None:
commit_message,
skip_detail=args.skip_detail,
hide_input=args.hide_input,
disable_max_header_length_check=args.disable_header_length_check,
max_header_length=args.max_header_length,
strip_comments=True,
)
elif args.hash:
console.verbose("commit message source: hash")
commit_message = get_commit_message_of_hash(args.hash)
_handle_commit_message(
commit_message, skip_detail=args.skip_detail, hide_input=args.hide_input
commit_message,
skip_detail=args.skip_detail,
hide_input=args.hide_input,
max_header_length=args.max_header_length,
disable_max_header_length_check=args.disable_header_length_check,
)
elif args.from_hash:
console.verbose("commit message source: hash range")
Expand All @@ -249,12 +288,18 @@ def main() -> None:
commit_messages,
skip_detail=args.skip_detail,
hide_input=args.hide_input,
max_header_length=args.max_header_length,
disable_max_header_length_check=args.disable_header_length_check,
)
else:
console.verbose("commit message source: direct message")
commit_message = args.commit_message.strip()
_handle_commit_message(
commit_message, skip_detail=args.skip_detail, hide_input=args.hide_input
commit_message,
skip_detail=args.skip_detail,
hide_input=args.hide_input,
max_header_length=args.max_header_length,
disable_max_header_length_check=args.disable_header_length_check,
)
except CommitlintException as ex:
console.error(f"{ex}")
Expand Down
33 changes: 27 additions & 6 deletions src/commitlint/linter/_linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .. import console
from .utils import is_ignored, remove_comments
from .validators import (
CommitValidator,
HeaderLengthValidator,
PatternValidator,
SimplePatternValidator,
Expand All @@ -16,12 +17,18 @@


def lint_commit_message(
commit_message: str, skip_detail: bool = False, strip_comments: bool = False
commit_message: str,
max_header_length: int,
skip_detail: bool = False,
disable_max_header_length: bool = False,
strip_comments: bool = False,
) -> Tuple[bool, List[str]]:
"""
Lints a commit message.
Args:
disable_max_header_length: flag to disable the max header length check
max_header_length: maximum length of commit message header
commit_message (str): The commit message to be linted.
skip_detail (bool, optional): Whether to skip the detailed error linting
(default is False).
Expand All @@ -47,16 +54,30 @@ def lint_commit_message(
console.verbose("commit message ignored, skipping lint")
return True, []

validator_instances: list[CommitValidator] = []

Check failure on line 57 in src/commitlint/linter/_linter.py

View workflow job for this annotation

GitHub Actions / linting

"list" is not subscriptable, use "typing.List" instead

if not disable_max_header_length:
validator_instances.append(
HeaderLengthValidator(
commit_message=commit_message,
**{"max_header_length": max_header_length}, # type: ignore
)
)

# for skip_detail check
if skip_detail:
console.verbose("running simple validators for linting")

validator_instances.append(
SimplePatternValidator(commit_message=commit_message)
)

return run_validators(
commit_message,
validator_classes=[HeaderLengthValidator, SimplePatternValidator],
validator_classes=validator_instances,
fail_fast=True,
)

validator_instances.append(PatternValidator(commit_message=commit_message))

console.verbose("running detailed validators for linting")
return run_validators(
commit_message, validator_classes=[HeaderLengthValidator, PatternValidator]
)
return run_validators(validator_classes=validator_instances)
43 changes: 26 additions & 17 deletions src/commitlint/linter/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import re
from abc import ABC, abstractmethod
from typing import List, Tuple, Type, Union
from typing import Any, List, Tuple, Union, cast

from .. import console
from ..constants import COMMIT_HEADER_MAX_LENGTH, COMMIT_TYPES
Expand All @@ -30,15 +30,15 @@
class CommitValidator(ABC):
"""Abstract Base validator for commit message."""

def __init__(self, commit_message: str) -> None:
def __init__(self, commit_message: str, **kwargs: dict[str, Any]) -> None:

Check failure on line 33 in src/commitlint/linter/validators.py

View workflow job for this annotation

GitHub Actions / linting

"dict" is not subscriptable, use "typing.Dict" instead
self._commit_message = commit_message
self._errors: List[str] = []

# start validation
self.validate()
self.validate(**kwargs)

@abstractmethod
def validate(self) -> None:
def validate(self, **kwargs: dict[str, Any]) -> None:

Check failure on line 41 in src/commitlint/linter/validators.py

View workflow job for this annotation

GitHub Actions / linting

"dict" is not subscriptable, use "typing.Dict" instead
"""Performs the validation."""
raise NotImplementedError # pragma: no cover

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

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

def validate(self) -> None:
def validate(self, **kwargs: dict[str, Any]) -> None:

Check failure on line 67 in src/commitlint/linter/validators.py

View workflow job for this annotation

GitHub Actions / linting

"dict" is not subscriptable, use "typing.Dict" instead
"""
Validates the length of the commit header.
Returns:
None
"""
max_header_length = kwargs.get("max_header_length")

if max_header_length:
header_length = cast(int, max_header_length)
else:
header_length = COMMIT_HEADER_MAX_LENGTH

header = self.commit_message.split("\n")[0]
if len(header) > COMMIT_HEADER_MAX_LENGTH:

if len(header) > int(header_length):
self.add_error(HEADER_LENGTH_ERROR)


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

def validate(self) -> None:
def validate(self, **kwargs: dict[str, Any]) -> None:

Check failure on line 102 in src/commitlint/linter/validators.py

View workflow job for this annotation

GitHub Actions / linting

"dict" is not subscriptable, use "typing.Dict" instead
"""
Validates the commit message using the regex pattern.
Expand Down Expand Up @@ -118,7 +127,7 @@ class PatternValidator(CommitValidator):
r"(((?P<body>.*))|(\s*))?$"
)

def validate(self) -> None:
def validate(self, **kwargs: dict[str, Any]) -> None:

Check failure on line 130 in src/commitlint/linter/validators.py

View workflow job for this annotation

GitHub Actions / linting

"dict" is not subscriptable, use "typing.Dict" instead
"""
Validates the commit message using the regex pattern.
Expand Down Expand Up @@ -286,8 +295,7 @@ def validate_description_no_full_stop_at_end(


def run_validators(
commit_message: str,
validator_classes: List[Type[CommitValidator]],
validator_classes: List[CommitValidator],
fail_fast: bool = False,
) -> Tuple[bool, List[str]]:
"""Runs the provided validators for the commit message.
Expand All @@ -307,17 +315,18 @@ def run_validators(
success = True
errors: List[str] = []

for validator_class in validator_classes:
console.verbose(f"running validator {validator_class.__name__}")
validator = validator_class(commit_message)
if not validator.is_valid():
console.verbose(f"{validator_class.__name__}: validation failed")
for validator_instance in validator_classes:
console.verbose(f"running validator {validator_instance.__class__.__name__}")
if not validator_instance.is_valid():
console.verbose(
f"{validator_instance.__class__.__name__}: validation failed"
)
if fail_fast:
console.verbose(f"fail_fast: {fail_fast}, skipping further validations")
# returning immediately if any error occurs.
return False, validator.errors()
return False, validator_instance.errors

success = False
errors.extend(validator.errors())
errors.extend(validator_instance.errors)

return success, errors
43 changes: 43 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,46 @@ def test__invalid_commit_message_with_hash_range_in_quiet(

mock_stderr_write.assert_not_called()
mock_stdout_write.assert_not_called()


class TestCliHeaderLength:
@patch(
"commitlint.cli.get_args",
return_value=ArgsMock(
commit_message="feat: add method 'formatEnglishDate' \
and 'formatEnglishDateInNepali' (#165)",
quiet=True,
max_header_length=20,
),
)
@patch("sys.stdout.write")
@patch("sys.stderr.write")
def test__main__quiet_option_with_header_max_length(
self, mock_stderr_write, mock_stdout_write, *_
):
with pytest.raises(SystemExit):
main()

mock_stderr_write.assert_not_called()
mock_stdout_write.assert_not_called()

@patch(
"commitlint.cli.get_args",
return_value=ArgsMock(
commit_message="feat: add method 'formatEnglishDate' \
and 'formatEnglishDateInNepali' (#165)",
quiet=True,
max_header_length=20,
disable_header_length_check=True,
),
)
@patch("sys.stdout.write")
@patch("sys.stderr.write")
def test__with_both_header_max_length_and_disabled_max_header_length_check(
self, mock_stderr_write, mock_stdout_write, *_
):
with pytest.raises(CommitlintException):
main()

mock_stderr_write.assert_not_called()
mock_stdout_write.assert_not_called()
Loading

0 comments on commit 8fc9baa

Please sign in to comment.