Skip to content

Stale Issue Management #41

Stale Issue Management

Stale Issue Management #41

Workflow file for this run

# Stale Issue Management
# Automatically manage inactive issues and pull requests
name: Stale Issue Management
on:
schedule:
# Run daily at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
# Allow manual triggering
permissions:
issues: write
pull-requests: write
contents: read
jobs:
stale:
name: Mark Stale Issues
runs-on: ubuntu-latest
steps:
- name: Mark stale issues and PRs
uses: actions/stale@v9
with:
# Authentication
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Stale issue configuration
days-before-issue-stale: 30
days-before-issue-close: 7
stale-issue-label: 'stale'
stale-issue-message: |
👋 This issue has been automatically marked as stale because it has had no activity for 30 days.
**What happens next:**
- This issue will be closed in 7 days if no further activity occurs
- To keep this issue open, simply add a comment or reaction
- If this is still relevant, please provide an update
**Remove stale status:**
- Comment on this issue
- Add a reaction to this issue
- Push commits to a linked PR
- Remove the `stale` label
Thank you for your contribution! 🙏
close-issue-message: |
🔒 This issue has been automatically closed due to inactivity.
**Why was this closed?**
- No activity for 37 days (30 days + 7 day warning)
- Marked as stale with no follow-up
**This issue can be reopened:**
- If you're still experiencing this problem
- If you have additional information to share
- Simply comment and the issue will be reopened
Thanks for understanding! 🙏
# Stale PR configuration
days-before-pr-stale: 14
days-before-pr-close: 7
stale-pr-label: 'stale'
stale-pr-message: |
👋 This pull request has been automatically marked as stale because it has had no activity for 14 days.
**What happens next:**
- This PR will be closed in 7 days if no further activity occurs
- To keep this PR open, push new commits or add a comment
**Remove stale status:**
- Push new commits to this PR
- Add a comment explaining the status
- Request review from maintainers
- Remove the `stale` label
Thank you for your contribution! 🙏
close-pr-message: |
🔒 This pull request has been automatically closed due to inactivity.
**Why was this closed?**
- No activity for 21 days (14 days + 7 day warning)
- Marked as stale with no follow-up
**This PR can be reopened:**
- Push new commits to the branch
- Reopen the PR and add a comment
- Create a fresh PR with updated changes
Thanks for understanding! 🙏
# Labels that prevent stale marking
exempt-issue-labels: 'priority:high,priority:critical,pinned,enhancement:approved,bug:confirmed,in-progress,has-pr'
exempt-pr-labels: 'priority:high,priority:critical,pinned,work-in-progress,needs-review,approved'
# Labels to remove when stale
labels-to-remove-when-stale: 'needs-triage,needs-review'
labels-to-remove-when-unstale: 'stale'
# Additional options
remove-stale-when-updated: true
delete-branch: false
start-date: '2024-01-01T00:00:00Z'
ascending: true
# Debug mode (set to true for testing)
debug-only: false
# Clean up closed issues
cleanup-closed-issues:
name: Cleanup Closed Issues
runs-on: ubuntu-latest
needs: stale
if: always()
steps:
- name: Remove labels from closed issues
uses: actions/github-script@v7
with:
script: |
// Get all closed issues that still have certain labels
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed',
labels: 'in-progress,has-pr,needs-review,needs-triage',
per_page: 100
});
console.log(`Found ${issues.length} closed issues with cleanup labels`);
for (const issue of issues) {
const labelsToRemove = ['in-progress', 'has-pr', 'needs-review', 'needs-triage'];
const currentLabels = issue.labels.map(l => l.name);
for (const label of labelsToRemove) {
if (currentLabels.includes(label)) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
name: label
});
console.log(`Removed ${label} from closed issue #${issue.number}`);
} catch (error) {
console.log(`Failed to remove ${label} from issue #${issue.number}: ${error.message}`);
}
}
}
// Add completed label if not present
if (!currentLabels.includes('completed')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['completed']
});
console.log(`Added completed label to issue #${issue.number}`);
}
}
# Generate stale issues report
stale-report:
name: Generate Stale Report
runs-on: ubuntu-latest
needs: stale
if: github.event_name == 'workflow_dispatch'
steps:
- name: Generate stale issues report
uses: actions/github-script@v7
with:
script: |
// Get stale issues
const { data: staleIssues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'stale',
per_page: 100
});
// Get stale PRs
const { data: stalePRs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100
});
const stalePRsFiltered = stalePRs.filter(pr =>
pr.labels.some(label => label.name === 'stale')
);
console.log(`📊 Stale Items Report:`);
console.log(`- Stale Issues: ${staleIssues.length}`);
console.log(`- Stale PRs: ${stalePRsFiltered.length}`);
if (staleIssues.length > 0) {
console.log('\n🐛 Stale Issues:');
staleIssues.forEach(issue => {
console.log(` - #${issue.number}: ${issue.title} (created: ${issue.created_at})`);
});
}
if (stalePRsFiltered.length > 0) {
console.log('\n🔄 Stale PRs:');
stalePRsFiltered.forEach(pr => {
console.log(` - #${pr.number}: ${pr.title} (created: ${pr.created_at})`);
});
}
// Create summary issue if there are many stale items
if (staleIssues.length + stalePRsFiltered.length >= 5) {
const reportBody = [
'# 📊 Stale Items Report',
'',
'## Summary',
`- **Stale Issues:** ${staleIssues.length}`,
`- **Stale PRs:** ${stalePRsFiltered.length}`,
`- **Report Date:** ${new Date().toISOString().split('T')[0]}`,
''
];
if (staleIssues.length > 0) {
reportBody.push('## 🐛 Stale Issues');
staleIssues.forEach(issue => {
reportBody.push(`- #${issue.number}: ${issue.title}`);
});
reportBody.push('');
}
if (stalePRsFiltered.length > 0) {
reportBody.push('## 🔄 Stale Pull Requests');
stalePRsFiltered.forEach(pr => {
reportBody.push(`- #${pr.number}: ${pr.title}`);
});
reportBody.push('');
}
reportBody.push(
'## Actions Taken',
'- Items marked as stale will be closed in 7 days without activity',
'- Repository maintainers should review and take action on important items',
'',
'---',
'*This report was generated automatically by the stale issue management workflow.*'
);
const reportBodyString = reportBody.join('\n');
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `📊 Stale Items Report - ${new Date().toISOString().split('T')[0]}`,
body: reportBodyString,
labels: ['maintenance', 'report']
});
console.log('📄 Created stale items report issue');
}