Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 86 additions & 18 deletions .github/workflows/automated-review-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ name: Automated review gate

on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
pull_request_review:
types: [submitted, dismissed]
issue_comment:
types: [created, edited]
types: [created, edited, deleted]
status:

permissions:
contents: read
Expand All @@ -20,13 +21,6 @@ permissions:
pull-requests: write
statuses: write

concurrency:
group: automated-review-${{ github.event.pull_request.number || github.event.issue.number }}
# This workflow reconciles one mutable commit status. Canceling an older run
# leaves a canceled Actions check on the pull request SHA even when a later
# run publishes a successful status, so process every event in order.
queue: max

jobs:
review:
name: publish automated review status
Expand All @@ -35,9 +29,13 @@ jobs:
# write a commit status, so skip it there and let the trusted
# pull_request_target and issue_comment runs publish the decision instead.
if: >-
github.event_name != 'status' &&
(github.event_name != 'issue_comment' || github.event.issue.pull_request) &&
(github.event_name != 'pull_request_review' ||
github.event.pull_request.head.repo.full_name == github.repository)
concurrency:
group: automated-review-status-publishers
queue: max
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
Expand All @@ -60,15 +58,16 @@ jobs:
const gateUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/scripts/ci/automated-review-gate.mjs`,
).href;
let pullRequest = context.payload.pull_request;
if (!pullRequest) {
const response = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.issue.number,
});
pullRequest = response.data;
}
const pullNumber = context.payload.pull_request?.number ??
context.payload.issue.number;
const response = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pullNumber,
});
const pullRequest = response.data;
const sameRepository =
`${context.repo.owner}/${context.repo.repo}`;
const headSha = pullRequest.head.sha;
let publishAutomatedReviewStatus;
try {
Expand Down Expand Up @@ -98,6 +97,12 @@ jobs:
headSha,
pullUrl: pullRequest.html_url,
isDraft: pullRequest.draft === true,
// Fork review events cannot publish a status. Ignore review
// objects for forks and accept only proof a trusted pull or
// comment event can reconcile. This safely stays pending until
// a trusted event observes status or comment proof.
allowPullRequestReviews:
pullRequest.head.repo?.full_name === sameRepository,
Comment on lines +104 to +105

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reconcile completed Codex reviews for fork PRs

For a fork PR where Codex submits a COMMENTED or APPROVED findings review and CodeRabbit does not emit a completion status, the pull_request_review job is skipped above and this passes false, causing findAutomatedReview to discard the only exact-head proof. No trusted comment or status event is guaranteed after that review, so the Automated review status remains pending indefinitely and blocks the contribution; add a trusted wake-up path that can reconcile the pinned review or otherwise preserve that evidence for forks.

Useful? React with 👍 / 👎.

});
if (result.state === "pending") {
core.notice(`${result.description}.`);
Expand Down Expand Up @@ -145,3 +150,66 @@ jobs:
? "Skipped an automated review request from a stale synchronize event."
: "An automated review request for this head commit already exists.",
);

status_review:
name: publish completed CodeRabbit review status
# This SHA-scoped path publishes only durable completion success. It shares
# the status publisher queue so it cannot race general PR reconciliation.
# The status payload carries no creator field, so pin the sender instead:
# for a bot-created commit status the sender is the bot itself. The gate
# helper then re-verifies the creator over the REST status history, where
# creator does exist, before treating the wakeup as completion proof.
if: >-
github.event_name == 'status' &&
github.event.context == 'CodeRabbit' &&
github.event.state == 'success' &&
github.event.description == 'Review completed' &&
github.event.sender.login == 'coderabbitai[bot]' &&
github.event.sender.id == 136622811 &&
github.event.sender.type == 'Bot'
concurrency:
group: automated-review-status-publishers
queue: max
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- name: Publish authenticated CodeRabbit completion
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
retries: 3
script: |
const { pathToFileURL } = require("node:url");
const gateUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/scripts/ci/automated-review-gate.mjs`,
).href;
let publishCodeRabbitCompletionStatus;
try {
({ publishCodeRabbitCompletionStatus } = await import(gateUrl));
} catch (error) {
core.setFailed(`Could not load the review gate: ${error}`);
return;
}
const result = await publishCodeRabbitCompletionStatus({
github,
owner: context.repo.owner,
repo: context.repo.repo,
headSha: context.payload.sha,
// The payload claim carries no creator; the helper authenticates
// the completion against the REST status history for the head.
status: {
context: context.payload.context,
state: context.payload.state,
description: context.payload.description,
},
});
if (result.state === "success") {
core.notice(
`Authenticated CodeRabbit completion for ${context.payload.sha.slice(0, 12)}.`,
);
} else if (result.failure) {
core.warning(result.failure.message);
Comment on lines +213 to +214

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail the completion job when reconciliation fails

When the associated-PR lookup, PR refetch, or commit-status publication exhausts its retries, publishCodeRabbitCompletionStatus returns state: "failure" without publishing an Automated review status, but this branch only logs a warning and lets the job succeed. If the previous gate status is pending, the CodeRabbit completion event has already been consumed and no later event may reconcile it, leaving the PR blocked indefinitely behind a misleadingly green workflow run. Publish a failure status or fail the job so the error is visible and retryable.

Useful? React with 👍 / 👎.

}
Loading
Loading