From d84ded9fc567599b1555aa99148acf17935e4b94 Mon Sep 17 00:00:00 2001 From: Sumit Nainani Date: Thu, 14 May 2026 16:49:40 +0530 Subject: [PATCH] Improved line length check in commit message. Enhanced validation logic for commit message line length. Allow long words such as URLs or identifiers without spaces. If the last word starts after the 72 character limit It reports an error.Otherwise allow lines where a single word extends beyond the limit. Signed-off-by: Sumit Nainani --- check_commits.py | 32 ++++++++--- tests/test_check_commits.py | 104 ++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 12 deletions(-) diff --git a/check_commits.py b/check_commits.py index b1bf147..11b3642 100644 --- a/check_commits.py +++ b/check_commits.py @@ -29,6 +29,7 @@ def parse_arguments(): parser.add_argument("--body-limit", type=int, default=72) parser.add_argument("--sub-limit", type=int, default=50) parser.add_argument("--check-blank-line", type=str, default="true") + parser.add_argument("--strict-line-length-check", type=str, default="true") return parser.parse_args() @@ -79,7 +80,9 @@ def validate_subject(subject, sub_char_limit): return errors -def validate_body(lines, n, body_char_limit, check_blank_line): +def validate_body( + lines, n, body_char_limit, check_blank_line, strict_line_length_check +): """Validate the commit body.""" errors = [] @@ -98,7 +101,12 @@ def validate_body(lines, n, body_char_limit, check_blank_line): errors.append("Commit message is missing a body!") for line in body: if len(line) > body_char_limit: - errors.append(f"Line exceeds {body_char_limit} characters: {line}") + if strict_line_length_check.lower() == "true": + errors.append(f"Line exceeds {body_char_limit} characters: {line}") + else: + index_of_last_space = line.rfind(" ") + if index_of_last_space >= body_char_limit: + errors.append(f"Line exceeds {body_char_limit} characters: {line}") return errors, body @@ -121,7 +129,9 @@ def validate_trailers(lines, body, check_blank_line): return errors -def validate_commit_message(commit, sub_char_limit, body_char_limit, check_blank_line): +def validate_commit_message( + commit, sub_char_limit, body_char_limit, check_blank_line, strict_line_length_check +): sha = commit["sha"] message = commit["message"] lines = message.splitlines() @@ -130,7 +140,9 @@ def validate_commit_message(commit, sub_char_limit, body_char_limit, check_blank errors = [] subject_errors = validate_subject(subject, sub_char_limit) - body_errors, body = validate_body(lines, n, body_char_limit, check_blank_line) + body_errors, body = validate_body( + lines, n, body_char_limit, check_blank_line, strict_line_length_check + ) trailer_errors = validate_trailers(lines, body, check_blank_line) errors.extend(subject_errors + body_errors + trailer_errors) @@ -138,11 +150,13 @@ def validate_commit_message(commit, sub_char_limit, body_char_limit, check_blank return sha, errors -def process_commits(commits, sub_limit, body_limit, check_blank_line): +def process_commits( + commits, sub_limit, body_limit, check_blank_line, strict_line_length_check +): failed_count = 0 for commit in commits: sha, errors = validate_commit_message( - commit, sub_limit, body_limit, check_blank_line + commit, sub_limit, body_limit, check_blank_line, strict_line_length_check ) if errors: print(f"::group:: ❌ Errors in commit {sha}") @@ -159,7 +173,11 @@ def main(): args = parse_arguments() commits = fetch_commits(args.base, args.head) failed_count = process_commits( - commits, args.sub_limit, args.body_limit, args.check_blank_line + commits, + args.sub_limit, + args.body_limit, + args.check_blank_line, + args.strict_line_length_check, ) summary_path = os.getenv("GITHUB_STEP_SUMMARY") diff --git a/tests/test_check_commits.py b/tests/test_check_commits.py index 7476b13..873e8c9 100644 --- a/tests/test_check_commits.py +++ b/tests/test_check_commits.py @@ -61,6 +61,7 @@ def test_validate_commit_message_valid(self): sub_char_limit=50, body_char_limit=72, check_blank_line="true", + strict_line_length_check="true", ) self.assertEqual(sha, "abc123") self.assertEqual(errors, []) @@ -75,7 +76,11 @@ def test_validate_commit_message_subject_too_long_no_blank_check(self): ), } _sha, errors = check_commits.validate_commit_message( - commit, sub_char_limit=50, body_char_limit=72, check_blank_line="false" + commit, + sub_char_limit=50, + body_char_limit=72, + check_blank_line="false", + strict_line_length_check="true", ) self.assertIn("Subject exceeds 50 characters!", errors) self.assertTrue( @@ -95,7 +100,11 @@ def test_validate_commit_message_subject_too_long_with_blank_check(self): ), } _sha, errors = check_commits.validate_commit_message( - commit, sub_char_limit=50, body_char_limit=72, check_blank_line="true" + commit, + sub_char_limit=50, + body_char_limit=72, + check_blank_line="true", + strict_line_length_check="true", ) self.assertIn("Subject exceeds 50 characters!", errors) self.assertIn("Subject and body must be separated by a blank line", errors) @@ -105,7 +114,11 @@ def test_process_commits_all_valid_prints_and_returns_zero(self): buf = StringIO() with redirect_stdout(buf): failed = check_commits.process_commits( - commits, sub_limit=50, body_limit=72, check_blank_line="true" + commits, + sub_limit=50, + body_limit=72, + check_blank_line="true", + strict_line_length_check="true", ) out = buf.getvalue() self.assertEqual(failed, 0) @@ -123,7 +136,11 @@ def test_process_commits_mixed_failures(self): buf = StringIO() with redirect_stdout(buf): failed = check_commits.process_commits( - commits, sub_limit=50, body_limit=72, check_blank_line="true" + commits, + sub_limit=50, + body_limit=72, + check_blank_line="true", + strict_line_length_check="true", ) out = buf.getvalue() self.assertEqual(failed, 1) @@ -135,7 +152,11 @@ def test_commits_failures(self): buf = StringIO() with redirect_stdout(buf): failed = check_commits.process_commits( - commits, sub_limit=50, body_limit=72, check_blank_line="true" + commits, + sub_limit=50, + body_limit=72, + check_blank_line="true", + strict_line_length_check="true", ) out = buf.getvalue() self.assertEqual(failed, 1) @@ -143,6 +164,79 @@ def test_commits_failures(self): self.assertIn("Subject exceeds 50 characters!", out) self.assertIn("Subject and body must be separated by a blank line", out) + def test_body_strict_true_long_line_fails(self): + commit = { + "sha": "long1", + "message": ("Valid subject\n\n" + "a" * 80 + "\n"), + } + _sha, errors = check_commits.validate_commit_message( + commit, + sub_char_limit=50, + body_char_limit=72, + check_blank_line="true", + strict_line_length_check="true", + ) + self.assertIn("Line exceeds 72 characters", "".join(errors)) + + def test_body_strict_false_single_long_word_passes(self): + commit = { + "sha": "long2", + "message": ("Valid subject\n\n" + "a" * 80 + "\n"), + } + _sha, errors = check_commits.validate_commit_message( + commit, + sub_char_limit=50, + body_char_limit=72, + check_blank_line="true", + strict_line_length_check="false", + ) + self.assertEqual(errors, []) + + def test_body_strict_false_new_word_after_limit_fails(self): + line = "a" * 72 + " newword" + commit = { + "sha": "long3", + "message": ("Valid subject\n\n" + line + "\n"), + } + _sha, errors = check_commits.validate_commit_message( + commit, + sub_char_limit=50, + body_char_limit=72, + check_blank_line="true", + strict_line_length_check="false", + ) + self.assertIn("Line exceeds 72 characters", "".join(errors)) + + def test_body_strict_false_long_url_passes(self): + url = "https://example.com/" + "a" * 100 + commit = { + "sha": "long4", + "message": ("Valid subject\n\n" + url + "\n"), + } + _sha, errors = check_commits.validate_commit_message( + commit, + sub_char_limit=50, + body_char_limit=72, + check_blank_line="true", + strict_line_length_check="false", + ) + self.assertEqual(errors, []) + + def test_body_strict_true_long_url_fails(self): + url = "https://www.example.com/" + "a" * 100 + commit = { + "sha": "long5", + "message": ("Valid subject\n\n" + url + "\n"), + } + _sha, errors = check_commits.validate_commit_message( + commit, + sub_char_limit=50, + body_char_limit=72, + check_blank_line="true", + strict_line_length_check="true", + ) + self.assertIn("Line exceeds 72 characters", "".join(errors)) + if __name__ == "__main__": unittest.main()