-
Notifications
You must be signed in to change notification settings - Fork 7.8k
feat(ci): add a backlog clean up bot #12128
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: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,239 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * GitHub Action script for managing issue backlog. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Behavior: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Pull Requests are skipped (only opened issues are processed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Skips issues with 'to-be-discussed' label. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Closes issues with label 'awaiting-response' or without assignees, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * with a standard closure comment. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Sends a Friendly Reminder comment to assigned issues without | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * exempt labels that have been inactive for 90+ days. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Avoids sending duplicate Friendly Reminder comments if one was | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * posted within the last 7 days. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * - Marks issues labeled 'questions' to 'Move to Discussion'. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dedent = (strings, ...values) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const raw = typeof strings === 'string' ? [strings] : strings.raw; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let result = ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raw.forEach((str, i) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result += str + (values[i] || ''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const lines = result.split('\n'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const minIndent = Math.min(...lines.filter(l => l.trim()).map(l => l.match(/^\s*/)[0].length)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return lines.map(l => l.slice(minIndent)).join('\n').trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function addMoveToDiscussionLabel(github, owner, repo, issue, isDryRun) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const targetLabel = "Move to Discussion"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hasLabel = issue.labels.some( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| l => l.name.toLowerCase() === targetLabel.toLowerCase() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (hasLabel) return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isDryRun) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`[DRY-RUN] Would add '${targetLabel}' to issue #${issue.number}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await github.rest.issues.addLabels({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| owner, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| repo, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| issue_number: issue.number, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: [targetLabel], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(`Failed to add label to #${issue.number}`, err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function fetchAllOpenIssues(github, owner, repo) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const issues = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let page = 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await github.rest.issues.listForRepo({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| owner, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| repo, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: 'open', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| per_page: 100, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| page, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const data = response.data || []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.length === 0) break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onlyIssues = data.filter(issue => !issue.pull_request); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| issues.push(...onlyIssues); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.length < 100) break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| page++; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Error fetching issues:', err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return issues; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const shouldSendReminder = (issue, exemptLabels, closeLabels) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hasExempt = issue.labels.some(l => exemptLabels.includes(l.name)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hasClose = issue.labels.some(l => closeLabels.includes(l.name)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return issue.assignees.length > 0 && !hasExempt && !hasClose; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module.exports = async ({ github, context, dryRun }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const now = new Date(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const thresholdDays = 90; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const exemptLabels = ['Status: Community help needed', 'Status: Needs investigation', 'Move to Discussion']; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const exemptLabels = ['Status: Community help needed', 'Status: Needs investigation', 'Move to Discussion']; | |
| const exemptLabels = ['Status: Community help needed', 'Status: Needs investigation']; |
Copilot
AI
Dec 12, 2025
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.
The threshold of 90 days and the list of exempt/close labels are hardcoded. Consider making these configurable through environment variables or workflow inputs to allow easier adjustment without modifying the script. This would improve maintainability and flexibility for different repository needs.
| const thresholdDays = 90; | |
| const exemptLabels = ['Status: Community help needed', 'Status: Needs investigation', 'Move to Discussion']; | |
| const closeLabels = ['Status: Awaiting Response']; | |
| // Allow configuration via environment variables, fallback to defaults | |
| const thresholdDays = process.env.BACKLOG_CLEANUP_THRESHOLD_DAYS | |
| ? parseInt(process.env.BACKLOG_CLEANUP_THRESHOLD_DAYS, 10) | |
| : 90; | |
| const exemptLabels = process.env.BACKLOG_CLEANUP_EXEMPT_LABELS | |
| ? process.env.BACKLOG_CLEANUP_EXEMPT_LABELS.split(',').map(l => l.trim()) | |
| : ['Status: Community help needed', 'Status: Needs investigation', 'Move to Discussion']; | |
| const closeLabels = process.env.BACKLOG_CLEANUP_CLOSE_LABELS | |
| ? process.env.BACKLOG_CLEANUP_CLOSE_LABELS.split(',').map(l => l.trim()) | |
| : ['Status: Awaiting Response']; |
Copilot
AI
Dec 12, 2025
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.
Issues with exempt labels are skipped before checking the threshold days, but the documentation and intended behavior suggest that exempt labels should prevent reminders and closures, not prevent staleness checks entirely. An issue with an exempt label that also has 'Status: Awaiting Response' or is unassigned might still need to be processed. Consider checking exempt labels only where they're actually relevant (in the reminder logic) rather than at the top of the loop.
Copilot
AI
Dec 12, 2025
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.
After marking a question issue for migration to discussions, the script continues to the next issue. However, if the issue has been inactive for 90+ days and is assigned, it should potentially still receive a reminder. The current logic skips all further processing for question issues. Consider whether question issues that are assigned and inactive should also receive reminders, or clarify the intended behavior in the documentation.
| continue; // Do not apply reminder logic | |
| // Do not skip further processing; allow reminder logic for assigned, inactive question issues |
Copilot
AI
Dec 12, 2025
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.
The logic for checking recent friendly reminders is incomplete. The code finds a reminder comment but doesn't verify if it was posted within the last 7 days as documented in the file header. This means duplicate reminders will still be sent as long as they're more than 7 days apart, but the current implementation skips the issue entirely if ANY friendly reminder comment exists, regardless of age. Add a date check to verify the reminder is within the last 7 days, similar to the documented behavior.
| const recentFriendlyReminder = comments.find(comment => | |
| comment.user.login === 'github-actions[bot]' && | |
| comment.body.includes('⏰ Friendly Reminder') | |
| ); | |
| if (recentFriendlyReminder) { | |
| totalSkipped++; | |
| continue; | |
| // Find the most recent friendly reminder comment from github-actions[bot] | |
| const friendlyReminders = comments | |
| .filter(comment => | |
| comment.user.login === 'github-actions[bot]' && | |
| comment.body.includes('⏰ Friendly Reminder') | |
| ) | |
| .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); | |
| const mostRecentReminder = friendlyReminders.length > 0 ? friendlyReminders[0] : null; | |
| const now = new Date(); | |
| const sevenDaysMs = 7 * 24 * 60 * 60 * 1000; | |
| if (mostRecentReminder) { | |
| const reminderDate = new Date(mostRecentReminder.created_at); | |
| if (now - reminderDate < sevenDaysMs) { | |
| totalSkipped++; | |
| continue; | |
| } |
Copilot
AI
Dec 12, 2025
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.
Comments are fetched for every issue that passes the initial checks, even when they're not needed. The comments are only used for checking friendly reminders on assigned issues without exempt/close labels. Consider moving the comment fetching logic inside the shouldSendReminder check to avoid unnecessary API calls for issues that will be closed or don't need reminders. This could significantly reduce API rate limit consumption, especially on repositories with many issues.
| let comments = []; | |
| try { | |
| let page = 1; | |
| while (true) { | |
| const { data } = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| per_page: 100, | |
| page, | |
| }); | |
| if (!data || data.length === 0) break; | |
| comments.push(...data); | |
| if (data.length < 100) break; | |
| page++; | |
| } | |
| } catch (err) { | |
| console.error(`Error fetching comments for issue #${issue.number}:`, err); | |
| } | |
| const recentFriendlyReminder = comments.find(comment => | |
| comment.user.login === 'github-actions[bot]' && | |
| comment.body.includes('⏰ Friendly Reminder') | |
| ); | |
| if (recentFriendlyReminder) { | |
| totalSkipped++; | |
| continue; | |
| } | |
| if (shouldSendReminder(issue, exemptLabels, closeLabels)) { | |
| if (shouldSendReminder(issue, exemptLabels, closeLabels)) { | |
| // Fetch comments only if we might send a reminder | |
| let comments = []; | |
| try { | |
| let page = 1; | |
| while (true) { | |
| const { data } = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| per_page: 100, | |
| page, | |
| }); | |
| if (!data || data.length === 0) break; | |
| comments.push(...data); | |
| if (data.length < 100) break; | |
| page++; | |
| } | |
| } catch (err) { | |
| console.error(`Error fetching comments for issue #${issue.number}:`, err); | |
| } | |
| const recentFriendlyReminder = comments.find(comment => | |
| comment.user.login === 'github-actions[bot]' && | |
| comment.body.includes('⏰ Friendly Reminder') | |
| ); | |
| if (recentFriendlyReminder) { | |
| totalSkipped++; | |
| continue; | |
| } |
Copilot
AI
Dec 12, 2025
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.
The label name in the comment text ('awaiting-response') doesn't match the actual label name defined in closeLabels ('Status: Awaiting Response'). This inconsistency could confuse users who are looking for the exact label name to apply. Consider using the actual label name or referencing it from the closeLabels array.
| - Or label it 'awaiting-response' if you're waiting on something | |
| - Or label it 'Status: Awaiting Response' if you're waiting on something |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| name: "Backlog Management Bot" | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: '0 4 * * *' # Run daily at 4 AM UTC | ||
| workflow_dispatch: | ||
| inputs: | ||
| dry-run: | ||
| description: "Run without modifying issues" | ||
| required: false | ||
| default: "0" | ||
|
|
||
| permissions: | ||
| issues: write | ||
| discussions: write | ||
| contents: read | ||
|
|
||
| jobs: | ||
| backlog-bot: | ||
| name: "Check issues" | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
|
|
||
| - name: Run backlog cleanup script | ||
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const script = require('./.github/scripts/backlog-cleanup.js'); | ||
| const dryRun = "${{ github.event.inputs.dry-run }}"; | ||
| await script({ github, context, dryRun }); |
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.
The documentation states that the script "Marks issues labeled 'questions' to 'Move to Discussion'" but doesn't explain what happens after marking. It would be helpful to clarify that this is only adding a label and that actual migration to discussions would need to be handled separately (either manually or by another automation).