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()