-
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?
Conversation
👋 Hello dmitriyastapov, we appreciate your contribution to this project! 📘 Please review the project's Contributions Guide for key guidelines on code, documentation, testing, and more. 🖊️ Please also make sure you have read and signed the Contributor License Agreement for this project. Click to see more instructions ...
Review and merge process you can expect ...
|
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.
Pull request overview
This PR introduces an automated GitHub Actions workflow to manage stale issues in the repository. The bot runs daily at 4 AM UTC and can also be triggered manually with an optional dry-run mode for testing. It processes open issues based on inactivity (90+ days) and applies different actions depending on issue status, labels, and assignment.
Key changes:
- Automated closure of unassigned or "awaiting-response" issues after 90+ days of inactivity
- Friendly reminder comments to assignees on inactive but assigned issues
- Automatic labeling of question-type issues for migration to GitHub Discussions
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
.github/workflows/backlog-bot.yml |
GitHub Actions workflow that schedules daily runs at 4 AM UTC with manual trigger support and dry-run capability |
.github/scripts/backlog-cleanup.js |
Core logic implementing issue triage, closure, reminder, and discussion migration behaviors with pagination support |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3471a58 to
9dd9b95
Compare
9dd9b95 to
b8e3c9b
Compare
lucasssvaz
left a 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.
Just some logging improvements and optimizations. Also added missing labels that should be exempt. PTAL if you agree with the changes and double check everything is working as expected. @Parsaabasi If there is anything you would like to change please let me know.
| module.exports = async ({ github, context, dryRun }) => { | ||
| const now = new Date(); | ||
| const thresholdDays = 90; | ||
| const exemptLabels = ['Status: Community help needed', 'Status: Needs investigation']; |
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.
Missing some labels that should also be skipped.
| const exemptLabels = ['Status: Community help needed', 'Status: Needs investigation']; | |
| const exemptLabels = [ | |
| 'Status: Community help needed', | |
| 'Status: Needs investigation', | |
| 'Move to Discussion', | |
| 'Status: Blocked upstream 🛑', | |
| 'Status: Blocked by ESP-IDF 🛑' | |
| ]; |
| - Please provide a status update | ||
| - Add any blocking details | ||
| - Or label it 'Status: Awaiting Response' if you're waiting on something |
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.
| - Please provide a status update | |
| - Add any blocking details | |
| - Or label it 'Status: Awaiting Response' if you're waiting on something | |
| - Please provide a status update | |
| - Add any blocking details and labels | |
| - Or label it 'Status: Awaiting Response' if you're waiting on the user's response |
| /** | ||
| * 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' by adding the 'Move to Discussion' label. | ||
| * (Actual migration to Discussions must be handled manually.) | ||
| */ |
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.
| /** | |
| * 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' by adding the 'Move to Discussion' label. | |
| * (Actual migration to Discussions must be handled manually.) | |
| */ | |
| /** | |
| * GitHub Action script for managing issue backlog. | |
| * | |
| * Behavior: | |
| * - Pull Requests are skipped (only opened issues are processed) | |
| * - Skips issues with labels defined in 'exemptLabels' | |
| * - Closes issues with labels defined in 'closeLabels' 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 'Type: Question' by adding the 'Move to Discussion' label. | |
| * (Actual migration to Discussions must be handled manually due to API limitations.) | |
| */ |
| for (const issue of issues) { | ||
| const isAssigned = issue.assignees && issue.assignees.length > 0; | ||
| const lastUpdate = new Date(issue.updated_at); | ||
| const daysSinceUpdate = Math.floor((now - lastUpdate) / (1000 * 60 * 60 * 24)); |
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.
| const daysSinceUpdate = Math.floor((now - lastUpdate) / (1000 * 60 * 60 * 24)); | |
| const oneDayMs = 1000 * 60 * 60 * 24; | |
| const daysSinceUpdate = Math.floor((now - lastUpdate) / oneDayMs); |
| const lines = result.split('\n'); | ||
| const minIndent = Math.min(...lines.filter(l => l.trim()).map(l => l.match(/^\s*/)[0].length)); |
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.
To avoid using empty lines in Math.min
| const lines = result.split('\n'); | |
| const minIndent = Math.min(...lines.filter(l => l.trim()).map(l => l.match(/^\s*/)[0].length)); | |
| const lines = result.split('\n'); | |
| if (!lines.some(l => l.trim())) return ''; | |
| const minIndent = Math.min(...lines.filter(l => l.trim()).map(l => l.match(/^\s*/)[0].length)); |
| if (issue.labels.some(label => exemptLabels.includes(label.name))) { | ||
| totalSkipped++; | ||
| continue; |
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.
| if (issue.labels.some(label => exemptLabels.includes(label.name))) { | |
| totalSkipped++; | |
| continue; | |
| if (issue.labels.some(label => exemptLabels.includes(label.name))) { | |
| console.log(`Skipping #${issue.number} (exempt label)`); | |
| totalSkipped++; | |
| continue; |
| if (daysSinceUpdate < thresholdDays) { | ||
| totalSkipped++; | ||
| continue; |
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.
| if (daysSinceUpdate < thresholdDays) { | |
| totalSkipped++; | |
| continue; | |
| if (daysSinceUpdate < thresholdDays) { | |
| console.log(`Skipping #${issue.number} (recent activity)`); | |
| totalSkipped++; | |
| continue; |
| if (await hasRecentFriendlyReminder(github, owner, repo, issue.number, sevenDaysMs)) { | ||
| totalSkipped++; | ||
| continue; | ||
| } |
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.
| if (await hasRecentFriendlyReminder(github, owner, repo, issue.number, sevenDaysMs)) { | |
| totalSkipped++; | |
| continue; | |
| } | |
| if (await hasRecentFriendlyReminder(github, owner, repo, issue.number, sevenDaysMs)) { | |
| console.log(`Skipping #${issue.number} (recent reminder)`); | |
| totalSkipped++; | |
| continue; | |
| } |
| try { | ||
| await github.rest.issues.createComment({ |
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.
| try { | |
| await github.rest.issues.createComment({ | |
| try { | |
| console.log(`Closing #${issue.number} (inactivity)`); | |
| await github.rest.issues.createComment({ |
| try { | ||
| await github.rest.issues.addLabels({ |
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.
| try { | |
| await github.rest.issues.addLabels({ | |
| try { | |
| console.log(`Adding label to #${issue.number} (Move to discussion)`); | |
| await github.rest.issues.addLabels({ |
Description of Change
This PR introduces a GitHub Action workflow that helps manage stale issues in the repository.
The bot runs daily and performs the following tasks:
Added a dry-run mode that lets you preview all actions without making any changes, to run it write "1" in the "Run workflow" input filed
Test Scenarios
The script and workflow were tested on GitHub Issues in my own fork.
Related links
Testing forked repo
(eg. Closes #number of issue)