-
Notifications
You must be signed in to change notification settings - Fork 7.7k
feat(workflow): Backlog management bot #11518
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
Samuel Fialka seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
👋 Hello SamuelFialka, 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 ...
|
8a17a91
to
b760d45
Compare
aac1f34
to
f409eb2
Compare
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 for managing stale issues in the repository backlog. The bot runs daily to keep issues organized and up-to-date by handling different types of inactive issues.
Key changes:
- Adds a daily scheduled workflow that processes open issues older than 90 days
- Automatically closes issues with specific labels or without assignees
- Sends reminder comments to assigned issues and migrates questions to discussions
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
File | Description |
---|---|
.github/workflows/backlog-bot.yml |
Defines the GitHub Actions workflow that runs daily and executes the cleanup script |
.github/scripts/backlog-cleanup.js |
Contains the main logic for processing issues, sending reminders, closing stale issues, and migrating questions to discussions |
owner, | ||
repo, | ||
issue_number: issue.number, | ||
per_page: 10, |
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.
Using per_page: 10 when checking for recent comments is inefficient. Since you're only looking for recent 'Friendly Reminder' comments from github-actions[bot], consider using per_page: 100 or at least 50 to reduce API calls, especially for issues with many comments.
per_page: 10, | |
per_page: 50, |
Copilot uses AI. Check for mistakes.
async function migrateToDiscussion(github, owner, repo, issue) { | ||
const discussionCategory = 'Q&A'; | ||
|
||
const { data: categories } = await github.rest.discussions.listCategories({ |
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 discussion categories are fetched for every issue that needs migration. Consider fetching categories once at the beginning of the script and reusing the result to avoid redundant API calls.
Copilot uses AI. Check for mistakes.
owner, | ||
repo, | ||
title: issue.title, | ||
body: `Originally created by @${issue.user.login} in #${issue.number}\n\n---\n\n${issue.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.
The issue body could be null or undefined, which would result in 'undefined' being appended to the discussion body. Add a null check: ${issue.body || ''}
or similar handling.
body: `Originally created by @${issue.user.login} in #${issue.number}\n\n---\n\n${issue.body}`, | |
body: `Originally created by @${issue.user.login} in #${issue.number}\n\n---\n\n${issue.body || ''}`, |
Copilot uses AI. Check for mistakes.
const { data: comments } = await github.rest.issues.listComments({ | ||
owner, | ||
repo, | ||
issue_number: issue.number, | ||
per_page: 10, | ||
}); | ||
|
||
const recentFriendlyReminder = comments.find(comment => | ||
comment.user.login === 'github-actions[bot]' && | ||
comment.body.includes('⏰ Friendly Reminder') && | ||
(now - new Date(comment.created_at)) < sevenDays | ||
); | ||
if (recentFriendlyReminder) { | ||
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.
Comments are fetched for every issue that meets the age threshold, even if the issue will be closed or skipped due to exempt labels. Consider checking exempt labels and close conditions before fetching comments to reduce unnecessary API calls.
const { data: comments } = await github.rest.issues.listComments({ | |
owner, | |
repo, | |
issue_number: issue.number, | |
per_page: 10, | |
}); | |
const recentFriendlyReminder = comments.find(comment => | |
comment.user.login === 'github-actions[bot]' && | |
comment.body.includes('⏰ Friendly Reminder') && | |
(now - new Date(comment.created_at)) < sevenDays | |
); | |
if (recentFriendlyReminder) { | |
totalSkipped++; | |
continue; | |
} |
Copilot uses AI. Check for mistakes.
Memory usage test (comparing PR against master branch)The table below shows the summary of memory usage change (decrease - increase) in bytes and percentage for each target.
Click to expand the detailed deltas report [usage change in BYTES]
|
|
||
on: | ||
schedule: | ||
- cron: '0 2 * * *' # Run daily at 2 AM UTC |
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 to avoid clashing with other jobs that run at the same time.
- cron: '0 2 * * *' # Run daily at 2 AM UTC | |
- cron: '0 4 * * *' # Run daily at 2 AM UTC |
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 |
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 supply chain attacks
uses: actions/checkout@v4 | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 |
uses: actions/checkout@v4 | ||
|
||
- name: Run backlog cleanup script | ||
uses: actions/github-script@v7 |
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 supply chain attacks
uses: actions/github-script@v7 | |
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 |
async function migrateToDiscussion(github, owner, repo, issue) { | ||
const discussionCategory = 'Q&A'; | ||
|
||
const { data: categories } = await github.rest.discussions.listCategories({ | ||
owner, | ||
repo, | ||
}); | ||
|
||
const category = categories.find(cat => | ||
cat.name.toLowerCase() === discussionCategory.toLowerCase() | ||
); | ||
|
||
if (!category) { | ||
throw new Error(`Discussion category '${discussionCategory}' not found.`); | ||
} | ||
|
||
const { data: discussion } = await github.rest.discussions.create({ | ||
owner, | ||
repo, | ||
title: issue.title, | ||
body: `Originally created by @${issue.user.login} in #${issue.number}\n\n---\n\n${issue.body}`, | ||
category_id: category.id, | ||
}); | ||
|
||
await github.rest.issues.createComment({ | ||
owner, | ||
repo, | ||
issue_number: issue.number, | ||
body: `💬 This issue was moved to [Discussions](${discussion.html_url}) for better visibility.`, | ||
}); | ||
|
||
await github.rest.issues.update({ | ||
owner, | ||
repo, | ||
issue_number: issue.number, | ||
state: 'closed', | ||
}); | ||
|
||
return discussion.html_url; | ||
} |
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.
I think this will cause the discussion to lose its comments (unlike when clicking Convert to Discussion
). I don't see any API available to do this though. IDK what would be the best approach here.
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.
Add some try/catch
blocks to avoid crashing the script if something fails.
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.
Could we also implement a dry-run mode toggled by an input from a workflow dispatch ?
Did you test it in some fork to check if everything is working fine ? If so, could you post the link ? |
const exemptLabels = ['to-be-discussed']; | ||
const closeLabels = ['awaiting-response']; | ||
const discussionLabel = 'questions'; |
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 exemptLabels = ['to-be-discussed']; | |
const closeLabels = ['awaiting-response']; | |
const discussionLabel = 'questions'; | |
const exemptLabels = ['Status: Community help needed', 'Status: Needs investigation']; | |
const closeLabels = ['Status: Awaiting Response']; | |
const discussionLabel = 'Type: Question'; |
|
||
for (const issue of issues) { | ||
const isAssigned = issue.assignees && issue.assignees.length > 0; | ||
const lastUpdate = new Date(issue.updated_at); |
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.
Do you know what triggers this updated at ? If I change a label, will this moment be considered the last update ?
const recentFriendlyReminder = comments.find(comment => | ||
comment.user.login === 'github-actions[bot]' && | ||
comment.body.includes('⏰ Friendly Reminder') && | ||
(now - new Date(comment.created_at)) < sevenDays |
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.
Why is this checking if "recent" is seven days ? Once the comment is posted, won't the daysSinceUpdate
become 0 and next reminder only be posted after 90 more days ? (this is what we want it to do)
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:
The purpose of this automation is to keep the issue backlog clean, manageable, and up-to-date.
Feel free to suggest adjustments to label logic, thresholds, or wording in comments.
Tests scenarios
The script and workflow were successfully tested on GitHub Issues in my own fork. Behavior such as issue closure and reminder comments worked as expected.
Related links
Please provide links to related issue, PRs etc.
(eg. Closes #number of issue)