Skip to content

Commit 388b0b1

Browse files
committed
refactor: clean up PR commit validation flow
1 parent 54dfe61 commit 388b0b1

2 files changed

Lines changed: 40 additions & 31 deletions

File tree

main.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ def is_pr_event() -> bool:
5555
def parse_commit_messages(output: str) -> list[str]:
5656
"""Split git log output into individual commit messages."""
5757
return [
58-
message.rstrip("\n")
58+
message.strip("\n")
5959
for message in output.split(COMMIT_MESSAGE_DELIMITER)
60-
if message.rstrip("\n")
60+
if message.strip("\n")
6161
]
6262

6363

@@ -124,19 +124,26 @@ def get_pr_commit_messages() -> list[str]:
124124

125125

126126
def run_check_command(
127-
args: list[str], result_file: TextIO, input_text: str | None = None
127+
args: list[str],
128+
result_file: TextIO,
129+
input_text: str | None = None,
130+
output_prefix: str | None = None,
128131
) -> int:
129132
"""Run commit-check and write both stdout and stderr to the result file."""
130133
command = ["commit-check"] + args
131134
print(" ".join(command))
132135
result = subprocess.run(
133136
command,
134137
input=input_text,
135-
stdout=result_file,
138+
stdout=subprocess.PIPE,
136139
stderr=subprocess.STDOUT,
137140
text=True,
138141
check=False,
139142
)
143+
if result.stdout:
144+
if output_prefix:
145+
result_file.write(output_prefix)
146+
result_file.write(result.stdout)
140147
return result.returncode
141148

142149

@@ -149,11 +156,12 @@ def run_pr_message_checks(pr_messages: list[str], result_file: TextIO) -> int:
149156
total_messages = len(pr_messages)
150157
for index, msg in enumerate(pr_messages, start=1):
151158
subject = msg.splitlines()[0] if msg else "<empty commit message>"
152-
result_file.write(f"\n--- Commit {index}/{total_messages}: {subject}\n")
153-
has_failure = (
154-
run_check_command(["--message"], result_file, input_text=msg) != 0
155-
or has_failure
156-
)
159+
has_failure = run_check_command(
160+
["--message"],
161+
result_file,
162+
input_text=msg,
163+
output_prefix=f"\n--- Commit {index}/{total_messages}: {subject}\n",
164+
) != 0 or has_failure
157165
return 1 if has_failure else 0
158166

159167

main_test.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,28 +62,28 @@ def test_message_and_branch(self):
6262

6363

6464
class TestParseCommitMessages(unittest.TestCase):
65-
def test_splits_messages_and_trims_trailing_newlines(self):
66-
result = main.parse_commit_messages("fix: first\n\x00feat: second\n\n\x00")
65+
def test_splits_messages_and_trims_surrounding_newlines(self):
66+
result = main.parse_commit_messages("\nfix: first\n\x00\nfeat: second\n\n\x00")
6767
self.assertEqual(result, ["fix: first", "feat: second"])
6868

6969

7070
class TestRunCheckCommand(unittest.TestCase):
7171
def test_with_args_calls_subprocess(self):
72-
mock_result = MagicMock(returncode=0)
72+
mock_result = MagicMock(returncode=0, stdout="")
7373
with patch("main.subprocess.run", return_value=mock_result) as mock_run:
7474
rc = main.run_check_command(["--branch"], io.StringIO())
7575
self.assertEqual(rc, 0)
7676
self.assertEqual(mock_run.call_args[0][0], ["commit-check", "--branch"])
7777

7878
def test_with_input_uses_text_mode(self):
79-
mock_result = MagicMock(returncode=0)
79+
mock_result = MagicMock(returncode=0, stdout="")
8080
with patch("main.subprocess.run", return_value=mock_result) as mock_run:
8181
main.run_check_command(["--message"], io.StringIO(), input_text="fix: demo")
8282
self.assertEqual(mock_run.call_args[1]["input"], "fix: demo")
8383
self.assertTrue(mock_run.call_args[1]["text"])
8484

8585
def test_prints_command(self):
86-
mock_result = MagicMock(returncode=0)
86+
mock_result = MagicMock(returncode=0, stdout="")
8787
with patch("main.subprocess.run", return_value=mock_result):
8888
with patch("builtins.print") as mock_print:
8989
main.run_check_command(["--branch"], io.StringIO())
@@ -92,20 +92,29 @@ def test_prints_command(self):
9292

9393
class TestRunPrMessageChecks(unittest.TestCase):
9494
def test_single_message_pass(self):
95-
mock_result = MagicMock(returncode=0)
95+
mock_result = MagicMock(returncode=0, stdout="")
9696
result_file = io.StringIO()
9797
with patch("main.subprocess.run", return_value=mock_result) as mock_run:
9898
rc = main.run_pr_message_checks(["fix: something"], result_file)
9999
self.assertEqual(rc, 0)
100100
self.assertEqual(mock_run.call_args[0][0], ["commit-check", "--message"])
101101
self.assertEqual(mock_run.call_args[1]["input"], "fix: something")
102+
self.assertEqual(result_file.getvalue(), "")
103+
104+
def test_failed_message_writes_header_and_output(self):
105+
mock_result = MagicMock(returncode=1, stdout="Commit rejected.\n")
106+
result_file = io.StringIO()
107+
with patch("main.subprocess.run", return_value=mock_result):
108+
rc = main.run_pr_message_checks(["fix: something"], result_file)
109+
self.assertEqual(rc, 1)
102110
self.assertIn("--- Commit 1/1: fix: something", result_file.getvalue())
111+
self.assertIn("Commit rejected.", result_file.getvalue())
103112

104113
def test_multiple_messages_partial_failure(self):
105114
results = [
106-
MagicMock(returncode=0),
107-
MagicMock(returncode=1),
108-
MagicMock(returncode=0),
115+
MagicMock(returncode=0, stdout=""),
116+
MagicMock(returncode=1, stdout="Commit rejected.\n"),
117+
MagicMock(returncode=0, stdout=""),
109118
]
110119
with patch("main.subprocess.run", side_effect=results):
111120
rc = main.run_pr_message_checks(["ok", "bad", "ok"], io.StringIO())
@@ -126,7 +135,7 @@ def test_empty_args_returns_zero(self):
126135
mock_run.assert_not_called()
127136

128137
def test_with_args_returns_returncode(self):
129-
mock_result = MagicMock(returncode=1)
138+
mock_result = MagicMock(returncode=1, stdout="branch check failed\n")
130139
with patch("main.subprocess.run", return_value=mock_result):
131140
rc = main.run_other_checks(["--branch", "--author-name"], io.StringIO())
132141
self.assertEqual(rc, 1)
@@ -182,19 +191,15 @@ def test_falls_back_to_base_ref_when_merge_ref_is_unavailable(self):
182191
def test_exception_returns_empty(self):
183192
with (
184193
patch.dict(os.environ, {"GITHUB_EVENT_NAME": "pull_request"}),
185-
patch(
186-
"main.get_messages_from_merge_ref", side_effect=Exception("git failed")
187-
),
194+
patch("main.get_messages_from_merge_ref", side_effect=Exception("git failed")),
188195
):
189196
result = main.get_pr_commit_messages()
190197
self.assertEqual(result, [])
191198

192199

193200
class TestGitMessageReaders(unittest.TestCase):
194201
def test_get_messages_from_merge_ref(self):
195-
mock_result = MagicMock(
196-
returncode=0, stdout="fix: first\n\x00feat: second\n\x00"
197-
)
202+
mock_result = MagicMock(returncode=0, stdout="fix: first\n\x00feat: second\n\x00")
198203
with patch("main.subprocess.run", return_value=mock_result) as mock_run:
199204
result = main.get_messages_from_merge_ref()
200205
self.assertEqual(result, ["fix: first", "feat: second"])
@@ -421,9 +426,7 @@ def test_same_repo_not_fork(self):
421426
"base": {"repo": {"full_name": "owner/repo"}},
422427
}
423428
}
424-
with tempfile.NamedTemporaryFile(
425-
mode="w", suffix=".json", delete=False
426-
) as file_obj:
429+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as file_obj:
427430
json.dump(event, file_obj)
428431
event_path = file_obj.name
429432
with patch.dict(os.environ, {"GITHUB_EVENT_PATH": event_path}):
@@ -440,9 +443,7 @@ def test_different_repo_is_fork(self):
440443
"base": {"repo": {"full_name": "owner/repo"}},
441444
}
442445
}
443-
with tempfile.NamedTemporaryFile(
444-
mode="w", suffix=".json", delete=False
445-
) as file_obj:
446+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as file_obj:
446447
json.dump(event, file_obj)
447448
event_path = file_obj.name
448449
with patch.dict(os.environ, {"GITHUB_EVENT_PATH": event_path}):

0 commit comments

Comments
 (0)