Add generic Gathering system for flexible event/meetup management #182
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Check Unresolved PR Conversations | |
| on: | |
| pull_request_review: | |
| types: [submitted, dismissed] | |
| pull_request_target: | |
| types: [synchronize, opened, reopened] | |
| permissions: | |
| pull-requests: write | |
| jobs: | |
| check-unresolved-conversations: | |
| runs-on: ubuntu-latest | |
| name: Check for unresolved review conversations | |
| steps: | |
| - name: Check unresolved conversations and notify contributor | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const repo = context.repo; | |
| const prNumber = context.payload.pull_request | |
| ? context.payload.pull_request.number | |
| : context.payload.review.pull_request_url.split('/').pop(); | |
| // Fetch unresolved review threads via GraphQL | |
| const query = ` | |
| query($owner: String!, $repo: String!, $pr_number: Int!) { | |
| repository(owner: $owner, name: $repo) { | |
| pullRequest(number: $pr_number) { | |
| reviewThreads(first: 100) { | |
| nodes { | |
| isResolved | |
| isOutdated | |
| } | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const result = await github.graphql(query, { | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| pr_number: parseInt(prNumber), | |
| }); | |
| const threads = result.repository.pullRequest.reviewThreads.nodes; | |
| const unresolvedThreads = threads.filter(t => !t.isResolved && !t.isOutdated); | |
| const unresolvedCount = unresolvedThreads.length; | |
| console.log(`PR #${prNumber}: ${unresolvedCount} unresolved conversation(s) found.`); | |
| // Fetch existing bot comments | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| issue_number: parseInt(prNumber), | |
| }); | |
| const botLogin = 'github-actions[bot]'; | |
| const markerText = '<!-- check-unresolved-conversations -->'; | |
| const existingComment = comments.find( | |
| c => c.user.login === botLogin && c.body.includes(markerText) | |
| ); | |
| // Fetch existing bot reviews | |
| const { data: allReviews } = await github.rest.pulls.listReviews({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| pull_number: parseInt(prNumber), | |
| }); | |
| const botChangesRequestedReviews = allReviews.filter( | |
| r => r.user.login === botLogin && r.state === 'CHANGES_REQUESTED' | |
| ); | |
| if (unresolvedCount > 0) { | |
| const conversationWord = unresolvedCount === 1 ? 'conversation' : 'conversations'; | |
| const message = `${markerText} | |
| ## 💬 Unresolved Review Conversations | |
| Hi @${context.payload.pull_request | |
| ? context.payload.pull_request.user.login | |
| : (await github.rest.pulls.get({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| pull_number: parseInt(prNumber), | |
| })).data.user.login}! 👋 | |
| This pull request currently has **${unresolvedCount} unresolved review ${conversationWord}**. | |
| Please address all review feedback and push a new commit to resolve them before this PR can be merged. | |
| **Steps to resolve:** | |
| 1. Review each comment thread in the "Files changed" tab. | |
| 2. Make the necessary changes to your code. | |
| 3. Reply to each conversation to explain your changes or ask for clarification. | |
| 4. Click **"Resolve conversation"** once the feedback has been addressed. | |
| 5. Push a new commit with your changes. | |
| Once all conversations are resolved, this notice will be removed automatically. Thank you! 🙏`; | |
| if (existingComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| comment_id: existingComment.id, | |
| body: message, | |
| }); | |
| console.log(`Updated existing comment on PR #${prNumber}`); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| issue_number: parseInt(prNumber), | |
| body: message, | |
| }); | |
| console.log(`Posted unresolved conversations comment on PR #${prNumber}`); | |
| } | |
| // Submit a REQUEST_CHANGES review if one is not already pending | |
| if (botChangesRequestedReviews.length === 0) { | |
| await github.rest.pulls.createReview({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| pull_number: parseInt(prNumber), | |
| event: 'REQUEST_CHANGES', | |
| body: `This PR has ${unresolvedCount} unresolved review ${conversationWord}. Please resolve them before this PR can be merged.`, | |
| }); | |
| console.log(`Submitted REQUEST_CHANGES review on PR #${prNumber}`); | |
| } | |
| } else { | |
| // All conversations resolved — remove the warning comment if it exists | |
| if (existingComment) { | |
| await github.rest.issues.deleteComment({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| comment_id: existingComment.id, | |
| }); | |
| console.log(`Deleted resolved conversations comment on PR #${prNumber}`); | |
| } else { | |
| console.log(`No unresolved conversations on PR #${prNumber}. Nothing to do.`); | |
| } | |
| // Dismiss any pending REQUEST_CHANGES reviews by the bot | |
| for (const review of botChangesRequestedReviews) { | |
| await github.rest.pulls.dismissReview({ | |
| owner: repo.owner, | |
| repo: repo.repo, | |
| pull_number: parseInt(prNumber), | |
| review_id: review.id, | |
| message: 'All review conversations have been resolved.', | |
| }); | |
| console.log(`Dismissed REQUEST_CHANGES review ${review.id} on PR #${prNumber}`); | |
| } | |
| } |