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
2 changes: 1 addition & 1 deletion .github/scripts/check_ci_status.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ PR_NUMBER="$1"
REPO="$2"

gh pr checks "$PR_NUMBER" --repo "$REPO" --json name,bucket --jq '
[.[] | select(.name | test("^(validate-triggers|evaluate)$") | not)] |
[.[] | select(.name | test("^(validate-triggers|evaluate|ready-for-review-trigger)$") | not)] |
if length == 0 then "no_checks"
elif all(.bucket == "pass" or .bucket == "skipping") then "all_passed"
elif any(.bucket == "fail") then "has_failures"
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/ready-for-review-trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Ready for Review Trigger

# Fork pull requests run pull_request and pull_request_review events with a
# read-only token and no secrets, so they cannot add or remove labels. This
# workflow does nothing but complete, which fires a workflow_run event that the
# privileged "Ready for Review Label" workflow reacts to with a write token.
on:
pull_request:
types: [ready_for_review, labeled, converted_to_draft]
pull_request_review:
types: [submitted, dismissed]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions: {}

jobs:
relay:
name: ready-for-review-trigger
runs-on: ubuntu-latest
steps:
- name: Relay to applier
run: echo "Relaying pull request event to the ready-for-review applier via workflow_run."
96 changes: 40 additions & 56 deletions .github/workflows/ready-for-review.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
name: Ready for Review Label

# Runs only on workflow_run so it holds a write-capable token even for pull
# requests opened from forks. Events tied to the fork head (pull_request,
# pull_request_review) get a read-only token and no secrets, so they cannot
# mutate labels themselves. The companion "Ready for Review Trigger" workflow
# relays those events here by completing and firing this workflow_run.
on:
pull_request:
types: [ready_for_review, labeled]
pull_request_review:
types: [submitted, dismissed]
workflow_run:
workflows: ["Continuous integration", "Pre-commit Checks", "Sanitizer"]
workflows:
- "Continuous integration"
- "Pre-commit Checks"
- "Sanitizer"
- "Ready for Review Trigger"
types: [completed]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.workflow_run.head_branch }}
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write
checks: read
issues: write
Comment thread
coderabbitai[bot] marked this conversation as resolved.

jobs:
# Ensure all pull_request-triggered workflows are monitored by workflow_run
validate-triggers:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_run'
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -34,10 +40,7 @@ jobs:
SELF=".github/workflows/ready-for-review.yml"

# Extract workflow names from the workflow_run trigger list in this file
MONITORED=$(grep -A1 'workflows:' "$SELF" \
| grep '\[' \
| tr ',' '\n' \
| sed 's/.*"\([^"]*\)".*/\1/')
MONITORED=$(grep -E '^\s+- "' "$SELF" | sed 's/.*"\([^"]*\)".*/\1/')

MISSING=""
for f in .github/workflows/*.yml; do
Expand Down Expand Up @@ -73,14 +76,6 @@ jobs:

evaluate:
runs-on: ubuntu-latest
permissions:
pull-requests: write
checks: read
issues: write
# Skip non-CodeRabbit reviews
if: >-
github.event_name != 'pull_request_review' ||
github.event.review.user.login == 'coderabbitai[bot]'
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -90,54 +85,42 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
EVENT_NAME: ${{ github.event_name }}
EVENT_ACTION: ${{ github.event.action }}
REVIEW_STATE: ${{ github.event.review.state }}
PR_NUMBER_DIRECT: ${{ github.event.pull_request.number }}
LABEL_NAME: ${{ github.event.label.name }}
PULL_REQUESTS_JSON: ${{ toJson(github.event.workflow_run.pull_requests) }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
# On review dismissal or changes requested, remove label and exit
if [ "$EVENT_NAME" = "pull_request_review" ]; then
if [ "$EVENT_ACTION" = "dismissed" ] || [ "$REVIEW_STATE" = "changes_requested" ]; then
echo "CodeRabbit review dismissed or changes requested, removing label."
gh pr edit "$PR_NUMBER_DIRECT" --repo "$REPO" --remove-label "ready-for-review" || true
exit 0
fi
fi

# On merge-conflict label, remove ready-for-review and exit
if [ "$EVENT_NAME" = "pull_request" ] && [ "$EVENT_ACTION" = "labeled" ]; then
if [ "$LABEL_NAME" = "merge-conflict" ]; then
echo "Merge conflict detected, removing ready-for-review label."
gh pr edit "$PR_NUMBER_DIRECT" --repo "$REPO" --remove-label "ready-for-review" || true
fi
exit 0
fi

# Collect PR numbers depending on the event type
if [ "$EVENT_NAME" = "workflow_run" ]; then
PR_NUMBERS=$(echo "$PULL_REQUESTS_JSON" | jq -r '.[].number')
else
PR_NUMBERS="$PR_NUMBER_DIRECT"
fi
# Resolve the PR(s) for the head commit. github.event.workflow_run's
# pull_requests array is empty for fork PRs, and the commit-to-PR
# endpoint does not index fork heads, so match the head sha against
# the list of open PRs, which works regardless of where the branch
# lives. Matching the exact sha also ignores stale runs whose commit
# the PR has already moved past.
PR_NUMBERS=$(gh api --paginate "repos/$REPO/pulls?state=open&per_page=100" \
--jq ".[] | select(.head.sha == \"$HEAD_SHA\") | .number")

if [ -z "$PR_NUMBERS" ]; then
echo "No associated pull requests found, skipping."
echo "No open pull request found for $HEAD_SHA, skipping."
exit 0
fi

for PR_NUMBER in $PR_NUMBERS; do
echo "=== Checking PR #$PR_NUMBER ==="

# Skip draft PRs
IS_DRAFT=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json isDraft --jq '.isDraft')
if [ "$IS_DRAFT" = "true" ]; then
echo "PR is a draft. Skipping."
PR_JSON=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json isDraft,labels)

if [ "$(echo "$PR_JSON" | jq -r '.isDraft')" = "true" ]; then
echo "PR is a draft. Removing ready-for-review label if present."
gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label "ready-for-review" || true
continue
fi

# Check if CodeRabbit has approved
# A merge conflict disqualifies the PR regardless of the other gates.
if [ "$(echo "$PR_JSON" | jq -r '[.labels[].name] | index("merge-conflict") != null')" = "true" ]; then
echo "Merge conflict present. Removing ready-for-review label if present."
gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label "ready-for-review" || true
continue
fi

# Check if CodeRabbit has approved. A dismissed or changes-requested
# review leaves the latest state != APPROVED and removes the label.
CODERABBIT_STATE=$(gh api --paginate "repos/$REPO/pulls/$PR_NUMBER/reviews" | jq -rs '
add
| map(select(.user.login == "coderabbitai[bot]"))
Expand All @@ -148,7 +131,8 @@ jobs:
echo "CodeRabbit review state: $CODERABBIT_STATE"

if [ "$CODERABBIT_STATE" != "APPROVED" ]; then
echo "CodeRabbit has not approved. Skipping."
echo "CodeRabbit has not approved. Removing label if present."
gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label "ready-for-review" || true
continue
fi

Expand Down
Loading