-
Couldn't load subscription status.
- 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 ...
|
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 |
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]
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.github/scripts/backlog-cleanup.js
Outdated
| 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.
You are right, there is no API available. Maybe let's keep this step as a manual action for a human to navigate the reporter to the Discussion instead?
The bot could also copy and paste every comment from Issues to Discussion, but this could be more confusing.
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 ?
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 performed a dry run on the forked repository linked in the issue description. Do you want to do dryrun on regular basis?
|
Did you test it in some fork to check if everything is working fine ? If so, could you post the link ? |
|
|
||
| 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 ?
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.
Triggers are changing labels, editing the title, body, assignees, comments, closing or reopening the issue.
.github/scripts/backlog-cleanup.js
Outdated
| 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)
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.
You are right, I've removed this line.
f409eb2 to
c136a86
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
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
c136a86 to
b012dab
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
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| const { data } = await github.rest.issues.listComments({ | ||
| owner, | ||
| repo, | ||
| issue_number: issue.number, | ||
| per_page: 50, | ||
| }); | ||
| comments = data; |
Copilot
AI
Oct 22, 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 comment fetching uses per_page: 50 without pagination. For issues with more than 50 comments, recent 'Friendly Reminder' comments might not be fetched, causing duplicate reminders to be sent. Implement pagination or fetch comments in reverse chronological order to ensure recent comments are captured.
| const { data } = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| per_page: 50, | |
| }); | |
| comments = data; | |
| 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++; | |
| } |
| if (!category) { | ||
| throw new Error(`Discussion category '${discussionCategory}' not found.`); | ||
| } | ||
| const { data: discussion } = await github.rest.discussions.create({ |
Copilot
AI
Oct 22, 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 GitHub REST API does not have a github.rest.discussions.create endpoint. Discussion creation requires the GraphQL API. This will cause the migration feature to fail at runtime. Use the GraphQL API with the createDiscussion mutation instead.
| const { data } = await github.rest.discussions.listCategories({ owner, repo }); | ||
| categories = data; |
Copilot
AI
Oct 22, 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 GitHub REST API does not have a github.rest.discussions.listCategories endpoint. Fetching discussion categories requires the GraphQL API. This will cause the category lookup to fail. Use the GraphQL API to query the repository's discussion categories.
| const { data } = await github.rest.discussions.listCategories({ owner, repo }); | |
| categories = data; | |
| const result = await github.graphql(` | |
| query($owner: String!, $repo: String!) { | |
| repository(owner: $owner, name: $repo) { | |
| discussionCategories(first: 100) { | |
| nodes { | |
| id | |
| name | |
| } | |
| } | |
| } | |
| } | |
| `, { owner, repo }); | |
| categories = result.repository.discussionCategories.nodes; |
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
Testing forked repo
(eg. Closes #number of issue)