From 512f5cfdb3746420bd916e018c1295e96ac3058b Mon Sep 17 00:00:00 2001 From: Xianpeng Shen Date: Tue, 3 Mar 2026 09:55:28 +0000 Subject: [PATCH 1/4] feat: add support for posting comments on pull requests from forked repositories --- main.py | 78 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/main.py b/main.py index 2ec7ea5..73e4b8c 100755 --- a/main.py +++ b/main.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 +import json import os import sys import subprocess import re -from github import Github, Auth # type: ignore +from github import Github, Auth, GithubException # type: ignore # Constants for message titles @@ -96,11 +97,39 @@ def add_job_summary() -> int: return 0 if result_text is None else 1 +def is_fork_pr() -> bool: + """Returns True when the triggering PR originates from a forked repository.""" + event_path = os.getenv("GITHUB_EVENT_PATH") + if not event_path: + return False + try: + with open(event_path, "r") as f: + event = json.load(f) + pr = event.get("pull_request", {}) + head_full_name = pr.get("head", {}).get("repo", {}).get("full_name", "") + base_full_name = pr.get("base", {}).get("repo", {}).get("full_name", "") + return bool(head_full_name and base_full_name and head_full_name != base_full_name) + except Exception: + return False + + def add_pr_comments() -> int: """Posts the commit check result as a comment on the pull request.""" if PR_COMMENTS == "false": return 0 + # Fork PRs triggered by the pull_request event receive a read-only token; + # the GitHub API will always reject comment writes with 403. + if is_fork_pr(): + print( + "::warning::Skipping PR comment: pull requests from forked repositories " + "cannot write comments via the pull_request event (GITHUB_TOKEN is " + "read-only for forks). Use the pull_request_target event or the " + "two-workflow artifact pattern instead. " + "See https://github.com/commit-check/commit-check-action/issues/77" + ) + return 0 + try: token = os.getenv("GITHUB_TOKEN") repo_name = os.getenv("GITHUB_REPOSITORY") @@ -108,20 +137,18 @@ def add_pr_comments() -> int: if pr_number is not None: pr_number = pr_number.split("/")[-2] else: - # Handle the case where GITHUB_REF is not set raise ValueError("GITHUB_REF environment variable is not set") - # Initialize GitHub client - # Use new Auth API to avoid deprecation warning if not token: raise ValueError("GITHUB_TOKEN is not set") + g = Github(auth=Auth.Token(token)) repo = g.get_repo(repo_name) pull_request = repo.get_issue(int(pr_number)) # Prepare comment content result_text = read_result_file() - pr_comments = ( + pr_comment_body = ( SUCCESS_TITLE if result_text is None else f"{FAILURE_TITLE}\n```\n{result_text}\n```" @@ -129,40 +156,41 @@ def add_pr_comments() -> int: # Fetch all existing comments on the PR comments = pull_request.get_comments() + matching_comments = [ + c + for c in comments + if c.body.startswith(SUCCESS_TITLE) or c.body.startswith(FAILURE_TITLE) + ] - # Track if we found a matching comment - matching_comments = [] - last_comment = None - - for comment in comments: - if comment.body.startswith(SUCCESS_TITLE) or comment.body.startswith( - FAILURE_TITLE - ): - matching_comments.append(comment) if matching_comments: last_comment = matching_comments[-1] - - if last_comment.body == pr_comments: + if last_comment.body == pr_comment_body: print(f"PR comment already up-to-date for PR #{pr_number}.") return 0 - else: - # If the last comment doesn't match, update it - print(f"Updating the last comment on PR #{pr_number}.") - last_comment.edit(pr_comments) - - # Delete all older matching comments + print(f"Updating the last comment on PR #{pr_number}.") + last_comment.edit(pr_comment_body) for comment in matching_comments[:-1]: print(f"Deleting an old comment on PR #{pr_number}.") comment.delete() else: - # No matching comments, create a new one print(f"Creating a new comment on PR #{pr_number}.") - pull_request.create_comment(body=pr_comments) + pull_request.create_comment(body=pr_comment_body) return 0 if result_text is None else 1 + except GithubException as e: + if e.status == 403: + print( + "::warning::Unable to post PR comment (403 Forbidden). " + "Ensure your workflow grants 'issues: write' permission. " + f"Error: {e.data.get('message', str(e))}", + file=sys.stderr, + ) + return 0 + print(f"Error posting PR comment: {e}", file=sys.stderr) + return 0 except Exception as e: print(f"Error posting PR comment: {e}", file=sys.stderr) - return 1 + return 0 def log_error_and_exit( From 8e83b622655be5c24ce121f2a5e876c755b66bcd Mon Sep 17 00:00:00 2001 From: Xianpeng Shen Date: Tue, 3 Mar 2026 09:59:40 +0000 Subject: [PATCH 2/4] style: format return statement in is_fork_pr function for better readability --- main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 73e4b8c..7491710 100755 --- a/main.py +++ b/main.py @@ -108,7 +108,9 @@ def is_fork_pr() -> bool: pr = event.get("pull_request", {}) head_full_name = pr.get("head", {}).get("repo", {}).get("full_name", "") base_full_name = pr.get("base", {}).get("repo", {}).get("full_name", "") - return bool(head_full_name and base_full_name and head_full_name != base_full_name) + return bool( + head_full_name and base_full_name and head_full_name != base_full_name + ) except Exception: return False From b6497d52c1b62b3e4af57d5ca835187eaddee806 Mon Sep 17 00:00:00 2001 From: Xianpeng Shen Date: Tue, 3 Mar 2026 12:46:11 +0000 Subject: [PATCH 3/4] docs: update README to clarify PR comment behavior for forked repositories --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6da81e2..f2b84b4 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ jobs: > [!IMPORTANT] > `pr-comments` is an experimental feature. By default, it's disabled. > -> This feature currently doesn’t work with forked repositories. For more details, refer to issue [#77](https://github.com/commit-check/commit-check-action/issues/77). +> For forked repositories skip PR comment. For more details, refer to issue [#77](https://github.com/commit-check/commit-check-action/issues/77). Note: the default rule of above inputs is following [this configuration](https://github.com/commit-check/commit-check-action/blob/main/commit-check.toml). If you want to customize, just add your [`commit-check.toml`](https://commit-check.github.io/commit-check/configuration.html) config file under your repository root directory. From fdae1ef9324e6bf721e6f631622ac87a0ccdcc89 Mon Sep 17 00:00:00 2001 From: Xianpeng Shen Date: Tue, 3 Mar 2026 22:26:03 +0200 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2b84b4..b741447 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ jobs: > [!IMPORTANT] > `pr-comments` is an experimental feature. By default, it's disabled. > -> For forked repositories skip PR comment. For more details, refer to issue [#77](https://github.com/commit-check/commit-check-action/issues/77). +> PR comments are skipped for pull requests from forked repositories. For more details, refer to issue [`#143`](https://github.com/commit-check/commit-check-action/issues/143). Note: the default rule of above inputs is following [this configuration](https://github.com/commit-check/commit-check-action/blob/main/commit-check.toml). If you want to customize, just add your [`commit-check.toml`](https://commit-check.github.io/commit-check/configuration.html) config file under your repository root directory.