-
Notifications
You must be signed in to change notification settings - Fork 10
feat: add CodeRabbit label sync workflow for PR reviews #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a GitHub Actions workflow that automatically synchronizes a label ( Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In @.github/workflows/coderabbit-label-sync.yml:
- Around line 38-50: The GraphQL query limits reviewThreads and comments
(reviewThreads(first: 100) and comments(first: 10)) so PRs with >100 threads or
>10 comments per thread can be truncated; update the query-handling to check
reviewThreads.pageInfo.hasNextPage and comments.pageInfo.hasNextPage and at
minimum log a warning when either is true, and ideally implement cursor-based
pagination to fetch additional pages (using endCursor and subsequent queries) to
fully iterate all reviewThreads and their comments before deciding label
changes.
- Around line 110-132: Wrap the GitHub Issues API calls
github.rest.issues.addLabels and github.rest.issues.removeLabel (the blocks
guarded by hasUnresolved/existingLabel) in try/catch so API failures don't abort
the workflow: on failure log a descriptive message including prNumber, the
action attempted and the caught error (use console.error or processLogger), and
do not rethrow; for removeLabel consider suppressing 404 errors (label not
found) while still logging other errors. This keeps label sync best-effort while
preserving the existing hasUnresolved/existingLabel logic.
- Around line 84-86: The workflow currently writes intermediate outputs with
core.setOutput ('has-unresolved', 'unresolved-count', 'pr-number') to pass data
to a subsequent step; instead, merge the producer and consumer into one
github-script step so you can directly call the filtering/labeling logic without
core.setOutput/steps.<...>.outputs: move the unresolvedCodeRabbitThreads
filtering and label management into the same github-script invocation (use the
existing unresolvedCodeRabbitThreads and prNumber variables directly), remove
the separate step that reads those outputs, and delete the now-unneeded
core.setOutput calls to eliminate the output-passing and injection surface.
- Around line 3-5: Add an inline comment above the "pull_request_target" trigger
documenting that using pull_request_target is intentional because the workflow
only queries PR metadata and manages labels (no checkout of PR code), note the
security risk if a checkout or other untrusted-code steps are added later
(especially for forked PRs), and optionally document any permission restrictions
(e.g., no write/contents permissions or safe token usage) to make the rationale
and constraints explicit next to the pull_request_target declaration.
- Around line 92-95: The script currently interpolates GitHub Actions outputs
directly into JS strings (prNumber, hasUnresolved, unresolvedCount), which is
insecure; instead, expose those outputs as environment variables on the step
(e.g., PR_NUMBER, HAS_UNRESOLVED, UNRESOLVED_COUNT) and read them via
process.env inside the script (parseInt(process.env.PR_NUMBER),
process.env.HAS_UNRESOLVED === 'true', parseInt(process.env.UNRESOLVED_COUNT'));
set the env either at the step level or as a sibling of script under with when
using actions/github-script@v7.
- Around line 10-11: The workflow is using the unsupported trigger name
pull_request_review_thread; replace it with supported triggers so the workflow
can run—remove pull_request_review_thread and instead rely on
pull_request_review (already present) combined with issue_comment for
comment-based thread updates, or add a schedule trigger to poll thread
resolution, or add workflow_dispatch for manual runs; update the triggers block
to use one of these supported events and ensure any logic that relied on
pull_request_review_thread is invoked from the chosen supported event handlers.
| on: | ||
| pull_request_target: | ||
| types: [opened, reopened, synchronize, edited] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
pull_request_target runs the workflow from the base branch with write permissions — ensure this is intentional.
Using pull_request_target is appropriate here since the workflow only reads PR metadata via GraphQL and manages labels (no checkout of untrusted code). Just be aware that if someone later adds a checkout step, this becomes a significant security risk for fork-based PRs.
Consider adding a comment in the workflow to document why pull_request_target is used.
🤖 Prompt for AI Agents
In @.github/workflows/coderabbit-label-sync.yml around lines 3 - 5, Add an
inline comment above the "pull_request_target" trigger documenting that using
pull_request_target is intentional because the workflow only queries PR metadata
and manages labels (no checkout of PR code), note the security risk if a
checkout or other untrusted-code steps are added later (especially for forked
PRs), and optionally document any permission restrictions (e.g., no
write/contents permissions or safe token usage) to make the rationale and
constraints explicit next to the pull_request_target declaration.
| pull_request_review_thread: | ||
| types: [resolved, unresolved] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
GitHub Actions pull_request_review_thread webhook event
💡 Result:
pull_request_review_thread is a GitHub webhook event that fires when there is activity on a pull request review comment thread (i.e., a diff comment “thread”), specifically when the thread is marked as resolved (action: resolved). The webhook payload includes (at least) action, pull_request, thread, and sender. [1]
It is not one of the events that can directly trigger GitHub Actions workflows (it does not appear in the “Events that trigger workflows” list). [2]
If you need an Actions workflow to react to PR review activity, you typically use pull_request_review or pull_request_review_comment (and issue_comment for non-diff PR comments), depending on what you’re targeting. [1]
Sources: [1] GitHub Docs – “Webhook events and payloads” (pull_request_review_thread) ; [2] GitHub Docs – “Events that trigger workflows” (GitHub Actions)
pull_request_review_thread is not supported as a GitHub Actions workflow trigger.
While pull_request_review_thread exists as a GitHub webhook event, it cannot trigger workflows. The workflow will never fire when review threads are resolved or unresolved, defeating this workflow's core purpose.
Use one of these alternatives instead:
pull_request_review(already present) combined withissue_comment, or- A
scheduletrigger to periodically poll for resolution state, or workflow_dispatchfor manual invocation.
🧰 Tools
🪛 actionlint (1.7.10)
[error] 10-10: unknown Webhook event "pull_request_review_thread". see https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#webhook-events for list of all Webhook event names
(events)
🤖 Prompt for AI Agents
In @.github/workflows/coderabbit-label-sync.yml around lines 10 - 11, The
workflow is using the unsupported trigger name pull_request_review_thread;
replace it with supported triggers so the workflow can run—remove
pull_request_review_thread and instead rely on pull_request_review (already
present) combined with issue_comment for comment-based thread updates, or add a
schedule trigger to poll thread resolution, or add workflow_dispatch for manual
runs; update the triggers block to use one of these supported events and ensure
any logic that relied on pull_request_review_thread is invoked from the chosen
supported event handlers.
| reviewThreads(first: 100) { | ||
| nodes { | ||
| isResolved | ||
| isOutdated | ||
| comments(first: 10) { | ||
| nodes { | ||
| author { | ||
| login | ||
| } | ||
| body | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pagination not handled — PRs with >100 review threads will silently miss unresolved threads.
The GraphQL query fetches at most 100 threads and 10 comments per thread. For large PRs with extensive review activity, some threads will be silently skipped, potentially causing the label to be removed prematurely (false negative).
At minimum, consider checking reviewThreads.pageInfo.hasNextPage and logging a warning when results are truncated. Ideally, paginate through all threads.
🤖 Prompt for AI Agents
In @.github/workflows/coderabbit-label-sync.yml around lines 38 - 50, The
GraphQL query limits reviewThreads and comments (reviewThreads(first: 100) and
comments(first: 10)) so PRs with >100 threads or >10 comments per thread can be
truncated; update the query-handling to check reviewThreads.pageInfo.hasNextPage
and comments.pageInfo.hasNextPage and at minimum log a warning when either is
true, and ideally implement cursor-based pagination to fetch additional pages
(using endCursor and subsequent queries) to fully iterate all reviewThreads and
their comments before deciding label changes.
| core.setOutput('has-unresolved', unresolvedCodeRabbitThreads.length > 0); | ||
| core.setOutput('unresolved-count', unresolvedCodeRabbitThreads.length); | ||
| core.setOutput('pr-number', prNumber); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider consolidating into a single step.
The outputs from step 1 are only consumed by step 2 within the same job. You could combine both steps into a single github-script step, which would eliminate the output-passing mechanism entirely (and the injection concern above). The filtering logic and label management are simple enough to coexist in one script.
🤖 Prompt for AI Agents
In @.github/workflows/coderabbit-label-sync.yml around lines 84 - 86, The
workflow currently writes intermediate outputs with core.setOutput
('has-unresolved', 'unresolved-count', 'pr-number') to pass data to a subsequent
step; instead, merge the producer and consumer into one github-script step so
you can directly call the filtering/labeling logic without
core.setOutput/steps.<...>.outputs: move the unresolvedCodeRabbitThreads
filtering and label management into the same github-script invocation (use the
existing unresolvedCodeRabbitThreads and prNumber variables directly), remove
the separate step that reads those outputs, and delete the now-unneeded
core.setOutput calls to eliminate the output-passing and injection surface.
| script: | | ||
| const prNumber = parseInt('${{ steps.check-threads.outputs.pr-number }}'); | ||
| const hasUnresolved = '${{ steps.check-threads.outputs.has-unresolved }}' === 'true'; | ||
| const unresolvedCount = parseInt('${{ steps.check-threads.outputs.unresolved-count }}'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Avoid ${{ }} interpolation inside script: blocks — use environment variables instead.
Directly embedding ${{ steps.*.outputs.* }} in JavaScript strings is a known injection anti-pattern in GitHub Actions. While these particular outputs are set by your own prior step and are currently safe, this pattern is fragile and flagged by security linters. Pass values through env instead:
Proposed fix
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
+ env:
+ PR_NUMBER: ${{ steps.check-threads.outputs.pr-number }}
+ HAS_UNRESOLVED: ${{ steps.check-threads.outputs.has-unresolved }}
+ UNRESOLVED_COUNT: ${{ steps.check-threads.outputs.unresolved-count }}
script: |
- const prNumber = parseInt('${{ steps.check-threads.outputs.pr-number }}');
- const hasUnresolved = '${{ steps.check-threads.outputs.has-unresolved }}' === 'true';
- const unresolvedCount = parseInt('${{ steps.check-threads.outputs.unresolved-count }}');
+ const prNumber = parseInt(process.env.PR_NUMBER);
+ const hasUnresolved = process.env.HAS_UNRESOLVED === 'true';
+ const unresolvedCount = parseInt(process.env.UNRESOLVED_COUNT);Note: env should be a sibling of script under with, or placed at the step level. At step level:
- name: Update unresolved-code-rabbit-review label
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ steps.check-threads.outputs.pr-number }}
HAS_UNRESOLVED: ${{ steps.check-threads.outputs.has-unresolved }}
UNRESOLVED_COUNT: ${{ steps.check-threads.outputs.unresolved-count }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = parseInt(process.env.PR_NUMBER);
const hasUnresolved = process.env.HAS_UNRESOLVED === 'true';
const unresolvedCount = parseInt(process.env.UNRESOLVED_COUNT);🤖 Prompt for AI Agents
In @.github/workflows/coderabbit-label-sync.yml around lines 92 - 95, The script
currently interpolates GitHub Actions outputs directly into JS strings
(prNumber, hasUnresolved, unresolvedCount), which is insecure; instead, expose
those outputs as environment variables on the step (e.g., PR_NUMBER,
HAS_UNRESOLVED, UNRESOLVED_COUNT) and read them via process.env inside the
script (parseInt(process.env.PR_NUMBER), process.env.HAS_UNRESOLVED === 'true',
parseInt(process.env.UNRESOLVED_COUNT')); set the env either at the step level
or as a sibling of script under with when using actions/github-script@v7.
| // Apply label if unresolved comments exist | ||
| if (hasUnresolved && !existingLabel) { | ||
| console.log('Applying unresolved-code-rabbit-review label'); | ||
|
|
||
| await github.rest.issues.addLabels({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: prNumber, | ||
| labels: ['unresolved-code-rabbit-review'] | ||
| }); | ||
| } | ||
|
|
||
| // Remove label if all comments resolved | ||
| if (!hasUnresolved && existingLabel) { | ||
| console.log('Removing unresolved-code-rabbit-review label'); | ||
|
|
||
| await github.rest.issues.removeLabel({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: prNumber, | ||
| name: 'unresolved-code-rabbit-review' | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No error handling around label API calls.
If addLabels or removeLabel fails (e.g., due to a race condition where another workflow run modifies labels concurrently, or the label was already removed), the workflow will fail with an unhandled exception. Consider wrapping these calls in try/catch blocks and logging the error gracefully, since label sync is not a critical-path operation.
Also note that addLabels will auto-create the unresolved-code-rabbit-review label with a default color if it doesn't already exist in the repository. You may want to pre-create the label with a specific color/description, or add a setup step.
🤖 Prompt for AI Agents
In @.github/workflows/coderabbit-label-sync.yml around lines 110 - 132, Wrap the
GitHub Issues API calls github.rest.issues.addLabels and
github.rest.issues.removeLabel (the blocks guarded by
hasUnresolved/existingLabel) in try/catch so API failures don't abort the
workflow: on failure log a descriptive message including prNumber, the action
attempted and the caught error (use console.error or processLogger), and do not
rethrow; for removeLabel consider suppressing 404 errors (label not found) while
still logging other errors. This keeps label sync best-effort while preserving
the existing hasUnresolved/existingLabel logic.
Addressed Issues:
Fixes #(issue number)
Screenshots/Recordings:
Additional Notes:
Checklist
We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.
Summary by CodeRabbit