diff --git a/.github/scripts/coderabbit_plan_trigger.js b/.github/scripts/coderabbit_plan_trigger.js new file mode 100644 index 000000000..ca6eefd02 --- /dev/null +++ b/.github/scripts/coderabbit_plan_trigger.js @@ -0,0 +1,83 @@ +// Script to trigger CodeRabbit plan for intermediate and advanced issues + +const marker = ''; + +async function triggerCodeRabbitPlan(github, owner, repo, issue, marker) { + const comment = `${marker} @coderabbitai plan`; + + try { + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issue.number, + body: comment, + }); + console.log(`Triggered CodeRabbit plan for issue #${issue.number}`); + return true; + } catch (commentErr) { + console.log(`Failed to trigger CodeRabbit plan for issue #${issue.number}:`, commentErr.message || commentErr); + return false; + } +} + +function hasIntermediateOrAdvancedLabel(issue, label) { + // Check if issue has intermediate or advanced label (case-insensitive) + const hasIntermediateLabel = issue.labels?.some(l => l?.name?.toLowerCase() === 'intermediate'); + const hasAdvancedLabel = issue.labels?.some(l => l?.name?.toLowerCase() === 'advanced'); + + // Also check if newly added label is intermediate/advanced + const isNewLabelIntermediate = label?.name?.toLowerCase() === 'intermediate'; + const isNewLabelAdvanced = label?.name?.toLowerCase() === 'advanced'; + + return hasIntermediateLabel || hasAdvancedLabel || isNewLabelIntermediate || isNewLabelAdvanced; +} + +async function hasExistingCodeRabbitPlan(github, owner, repo, issueNumber) { + // Check for existing CodeRabbit plan comment (limited to first 500 comments) + const comments = []; + const iterator = github.paginate.iterator(github.rest.issues.listComments, { + owner, repo, issue_number: issueNumber, per_page: 100 + }); + + let count = 0; + for await (const { data: page } of iterator) { + comments.push(...page); + count += page.length; + if (count >= 500) break; // Hard upper bound to prevent unbounded pagination + } + + return comments.some(c => c.body?.includes('@coderabbitai plan')); +} + +function logSummary(owner, repo, issue) { + console.log('=== Summary ==='); + console.log(`Repository: ${owner}/${repo}`); + console.log(`Issue Number: ${issue.number}`); + console.log(`Issue Title: ${issue.title || '(no title)'}`); + console.log(`Labels: ${issue.labels?.map(l => l.name).join(', ') || 'none'}`); +} + +module.exports = async ({ github, context }) => { + try { + const { owner, repo } = context.repo; + const { issue, label } = context.payload; + + // Validations + if (!issue?.number) return console.log('No issue in payload'); + + if (!hasIntermediateOrAdvancedLabel(issue, label)) { + return console.log('Issue does not have intermediate or advanced label'); + } + + if (await hasExistingCodeRabbitPlan(github, owner, repo, issue.number)) { + return console.log(`CodeRabbit plan already triggered for #${issue.number}`); + } + + // Post CodeRabbit plan trigger + await triggerCodeRabbitPlan(github, owner, repo, issue, marker); + + logSummary(owner, repo, issue); + } catch (err) { + console.log('❌ Error:', err.message); + } +}; diff --git a/.github/workflows/bot-coderabbit-plan-trigger.yml b/.github/workflows/bot-coderabbit-plan-trigger.yml new file mode 100644 index 000000000..ac1bde8ed --- /dev/null +++ b/.github/workflows/bot-coderabbit-plan-trigger.yml @@ -0,0 +1,43 @@ +# This workflow automatically triggers CodeRabbit's plan feature for intermediate and advanced issues. +name: CodeRabbit Plan Trigger +on: + issues: + types: [opened, labeled] + +permissions: + issues: write + contents: read + +jobs: + coderabbit_plan_trigger: + runs-on: ubuntu-latest + concurrency: + group: coderabbit-plan-${{ github.event.issue.number }} + cancel-in-progress: false + # Only run for issues labeled with 'intermediate' or 'advanced' (case-insensitive) + if: > + (github.event_name == 'issues' && ( + contains(github.event.issue.labels.*.name, 'intermediate') || + contains(github.event.issue.labels.*.name, 'advanced') || + contains(github.event.issue.labels.*.name, 'Intermediate') || + contains(github.event.issue.labels.*.name, 'Advanced') || + (github.event.label && (github.event.label.name == 'intermediate' || github.event.label.name == 'advanced' || github.event.label.name == 'Intermediate' || github.event.label.name == 'Advanced')) + )) + + steps: + - name: Harden the runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1 + + - name: Trigger CodeRabbit Plan + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0 + with: + script: | + const script = require('./.github/scripts/coderabbit_plan_trigger.js'); + await script({ github, context}); diff --git a/CHANGELOG.md b/CHANGELOG.md index ba152e27d..d2c16f2d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Added method chaining examples to the developer training guide (`docs/sdk_developers/training/coding_token_transactions.md`) (#1194) - Added documentation explaining how to pin GitHub Actions to specific commit SHAs (`docs/sdk_developers/how-to-pin-github-actions.md`)(#1211) - examples/mypy.ini for stricter type checking in example scripts +- Formatted examples/tokens directory using black code formatter for consistent code style +- Added `.github/workflows/bot-coderabbit-plan-trigger.yml` to automatically invoke CodeRabbit's plan feature on intermediate and advanced issues, providing implementation guidance to help contributors assess complexity and understand requirements. (#1289) - Added a GitHub Actions workflow that reminds contributors to link pull requests to issues. - Added `__str__` and `__repr__` methods to `AccountInfo` class for improved logging and debugging experience (#1098) - Added Good First Issue (GFI) management and frequency documentation to clarify maintainer expectations and SDK-level GFI governance.