From c01c25f852aad796e0dd55098e05a5ec53661c3a Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Sat, 14 Mar 2026 01:18:19 +0200 Subject: [PATCH 1/2] fix: check actual PR commits instead of merge commit HEAD When actions/checkout runs on a pull_request event, it checks out an auto-generated merge commit at refs/pull/{N}/merge as HEAD. The commit message of this merge commit is 'Merge {sha} into {base}', which: - Matches the (Merge).* alternative in the conventional commits regex - Is allowed by allow_merge_commits = true This caused the action to always pass on PRs regardless of the actual commit messages, since only the merge commit HEAD was ever checked. Fix by adding get_pr_commit_messages() which, when GITHUB_BASE_REF is set (indicating a PR context), retrieves the real PR commits via: git log --no-merges origin/{base_ref}..HEAD --format=%B%x00 --reverse Each commit message is then piped individually to commit-check --message via stdin so the actual PR commits are validated. In non-PR contexts (push, manual dispatch, etc.) the existing behaviour is preserved: commit-check determines what to check from git itself. Also fixes stderr being silently discarded (subprocess.PIPE) by using subprocess.STDOUT so errors are captured in result.txt and surfaced in the job summary and PR comments. Fixes #184 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- main.py | 105 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 23 deletions(-) diff --git a/main.py b/main.py index 7491710..aa9efcd 100755 --- a/main.py +++ b/main.py @@ -36,35 +36,94 @@ def log_env_vars(): print(f"PR_COMMENTS = {PR_COMMENTS}\n") -def run_commit_check() -> int: - """Runs the commit-check command and logs the result.""" - args = [ - "--message", - "--branch", - "--author-name", - "--author-email", - ] - args = [ - arg - for arg, value in zip( - args, +def get_pr_commit_messages() -> list[str] | None: + """Get commit messages for all commits in a PR, excluding merge commits. + + In a GitHub Actions PR context, HEAD points to an auto-generated merge commit + (refs/pull/{N}/merge), not the actual PR commits. This function retrieves the + real commit messages so they can be validated individually. + + Returns None if not in a PR context or if no commits are found. + """ + base_ref = os.getenv("GITHUB_BASE_REF", "") + if not base_ref: + return None + + try: + result = subprocess.run( [ - MESSAGE, - BRANCH, - AUTHOR_NAME, - AUTHOR_EMAIL, + "git", + "log", + "--no-merges", + f"origin/{base_ref}..HEAD", + "--format=%B%x00", + "--reverse", ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=False, ) - if value == "true" + if result.returncode != 0 or not result.stdout.strip(): + return None + messages = [m.strip() for m in result.stdout.split("\x00") if m.strip()] + return messages if messages else None + except Exception: + return None + + +def run_commit_check() -> int: + """Runs the commit-check command and logs the result.""" + other_check_flags = [ + ("--branch", BRANCH), + ("--author-name", AUTHOR_NAME), + ("--author-email", AUTHOR_EMAIL), ] + other_args = [arg for arg, value in other_check_flags if value == "true"] - command = ["commit-check"] + args - print(" ".join(command)) + ret_code = 0 with open("result.txt", "w") as result_file: - result = subprocess.run( - command, stdout=result_file, stderr=subprocess.PIPE, check=False - ) - return result.returncode + if MESSAGE == "true": + commit_messages = get_pr_commit_messages() + if commit_messages: + # PR context: check each commit message individually to avoid + # only checking the auto-generated merge commit at HEAD. + for msg in commit_messages: + command = ["commit-check", "--message"] + print(" ".join(command)) + result = subprocess.run( + command, + input=msg, + text=True, + stdout=result_file, + stderr=subprocess.STDOUT, + check=False, + ) + ret_code += result.returncode + else: + # Non-PR context: let commit-check determine what to check from git. + command = ["commit-check", "--message"] + print(" ".join(command)) + result = subprocess.run( + command, + stdout=result_file, + stderr=subprocess.STDOUT, + check=False, + ) + ret_code += result.returncode + + if other_args: + command = ["commit-check"] + other_args + print(" ".join(command)) + result = subprocess.run( + command, + stdout=result_file, + stderr=subprocess.STDOUT, + check=False, + ) + ret_code += result.returncode + + return ret_code def read_result_file() -> str | None: From 2552150cc0d0667e611fb24f4210d3e7bc69ae9b Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Sat, 14 Mar 2026 01:54:54 +0200 Subject: [PATCH 2/2] fix: update main.py to fix mypy --- main.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index aa9efcd..14cb5e7 100755 --- a/main.py +++ b/main.py @@ -104,24 +104,24 @@ def run_commit_check() -> int: # Non-PR context: let commit-check determine what to check from git. command = ["commit-check", "--message"] print(" ".join(command)) - result = subprocess.run( + ret_code += subprocess.run( command, stdout=result_file, stderr=subprocess.STDOUT, + text=True, check=False, - ) - ret_code += result.returncode + ).returncode if other_args: command = ["commit-check"] + other_args print(" ".join(command)) - result = subprocess.run( + ret_code += subprocess.run( command, stdout=result_file, stderr=subprocess.STDOUT, + text=True, check=False, - ) - ret_code += result.returncode + ).returncode return ret_code