[Bug]: SOC predictor is not accurate. #247
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Stale Issues | |
| on: | |
| schedule: | |
| - cron: '0 9 * * 1' # Monday 9am UTC | |
| workflow_dispatch: # Manual trigger for testing | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| issues: write | |
| jobs: | |
| check-stale: | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check stale issues | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const NOW = Date.now(); | |
| const DAY_MS = 24 * 60 * 60 * 1000; | |
| const EXEMPT_LABELS = ['pinned', 'security']; | |
| const issues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| per_page: 100, | |
| }); | |
| for (const issue of issues) { | |
| if (issue.pull_request) continue; | |
| if (issue.labels.some(l => EXEMPT_LABELS.includes(l.name))) continue; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| per_page: 100, | |
| }); | |
| // Find last non-bot activity | |
| let lastActivity = new Date(issue.created_at); | |
| let reminders = 0; | |
| for (const c of comments) { | |
| if (c.user.type === 'Bot' || c.body.includes('<!-- stale-reminder')) { | |
| if (c.body.includes('<!-- stale-reminder')) { | |
| reminders++; | |
| } | |
| continue; | |
| } | |
| const d = new Date(c.created_at); | |
| if (d > lastActivity) { | |
| lastActivity = d; | |
| reminders = 0; // Reset — count only reminders after last human comment | |
| } | |
| } | |
| // Re-count reminders after last human activity | |
| reminders = 0; | |
| for (const c of comments) { | |
| if (new Date(c.created_at) > lastActivity && c.body.includes('<!-- stale-reminder')) { | |
| reminders++; | |
| } | |
| } | |
| const daysInactive = (NOW - lastActivity.getTime()) / DAY_MS; | |
| if (daysInactive >= 28 && reminders >= 3) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: '<!-- stale-close -->\nThis issue has been automatically closed due to inactivity (4 weeks without a response). Feel free to reopen if the issue still persists.', | |
| }); | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| state: 'closed', | |
| state_reason: 'not_planned', | |
| }); | |
| core.info(`Closed issue #${issue.number} (inactive ${Math.floor(daysInactive)} days)`); | |
| } else if (daysInactive >= 21 && reminders < 3) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: '<!-- stale-reminder 3 -->\n**This issue will be automatically closed in 1 week** if no update is provided. If this issue is still relevant, please comment with an update.', | |
| }); | |
| core.info(`Warning posted on issue #${issue.number} (inactive ${Math.floor(daysInactive)} days)`); | |
| } else if (daysInactive >= 14 && reminders < 2) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: '<!-- stale-reminder 2 -->\nThis issue has been inactive for 2 weeks. Please provide an update.', | |
| }); | |
| core.info(`Reminder 2 posted on issue #${issue.number} (inactive ${Math.floor(daysInactive)} days)`); | |
| } else if (daysInactive >= 7 && reminders < 1) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: '<!-- stale-reminder 1 -->\nThis issue has been inactive for 1 week. Please provide an update on whether this is still relevant. If no activity occurs, this issue will eventually be closed automatically.', | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| labels: ['stale'], | |
| }); | |
| core.info(`Reminder 1 posted on issue #${issue.number} (inactive ${Math.floor(daysInactive)} days)`); | |
| } | |
| } | |
| remove-stale-label: | |
| if: github.event_name == 'issue_comment' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Remove stale label on human comment | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const comment = context.payload.comment; | |
| const issue = context.payload.issue; | |
| if (comment.user.type === 'Bot') return; | |
| if (comment.body.includes('<!-- stale-')) return; | |
| const hasStale = issue.labels.some(l => l.name === 'stale'); | |
| if (!hasStale) return; | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| name: 'stale', | |
| }); | |
| core.info(`Removed stale label from issue #${issue.number}`); |