open-design/.github/workflows/stale-issues.yml
Marc Chan f403ffbfce
ci: add PR-author and stale-issue inactivity workflows (#2055)
* ci: add PR-author and stale-issue inactivity workflows

Adds two queue-management automations:

- pr-author-inactivity: reminds PR authors after 72h of inactivity
  following human reviewer/maintainer feedback (issue comments,
  non-approval reviews, or inline review comments) and closes after
  120h. Author response is detected via issue comments, inline review
  replies, or commit/force-push events. Bot-authored reviews are
  intentionally excluded so authors are not pressured by automated
  nits alone.

- stale-issues: marks issues stale after 30 days of inactivity and
  closes after a further 7 days. Exempts good first issue, help
  wanted, and security labels. A pre-step also auto-applies
  'exempt-from-stale' to issues opened by org members/owners/
  collaborators, since actions/stale only supports label-based
  exemptions. PR processing is disabled (handled by the workflow
  above).

* fix: limit PR inactivity feedback to trusted reviewers

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)

* fix: count author PR reviews as inactivity responses

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
2026-05-18 16:45:37 +08:00

104 lines
4 KiB
YAML

name: Stale issues
# Mark issues as stale after 30 days of inactivity, and close them after a
# further 7 days if no activity resumes.
#
# Exemption policy:
# - Label-based exemptions: issues carrying any of the labels in
# EXEMPT_LABELS are never marked stale (security, contributor-invite,
# priority, awaiting product direction, etc.).
# - Author-based exemption: issues opened by org members, owners, or
# collaborators are also exempt. Since actions/stale only supports
# label-based exemptions, a pre-step labels qualifying issues with
# `exempt-from-stale` so the stale step honors the same rule.
#
# PRs are handled separately by pr-author-inactivity.yml; this workflow
# disables PR processing by setting the PR thresholds to -1.
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch: {}
permissions:
issues: write
pull-requests: read
concurrency:
group: stale-issues
cancel-in-progress: false
jobs:
stale:
if: github.repository == 'nexu-io/open-design'
runs-on: ubuntu-latest
steps:
- name: Exempt org-member issues from staling
uses: actions/github-script@v7
with:
script: |
const EXEMPT_LABEL = 'exempt-from-stale';
const EXEMPT_ASSOCIATIONS = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']);
// Ensure the exempt label exists so the stale step can match it.
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: EXEMPT_LABEL,
});
} catch (err) {
if (err.status !== 404) throw err;
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: EXEMPT_LABEL,
color: 'cccccc',
description: 'Issue is exempt from automatic stale handling (set by stale-issues workflow).',
});
}
const issues = await github.paginate(github.rest.issues.listForRepo, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100,
});
let labeled = 0;
for (const issue of issues) {
if (issue.pull_request) continue;
if (!EXEMPT_ASSOCIATIONS.has(issue.author_association)) continue;
if (issue.labels.some((l) => (typeof l === 'string' ? l : l.name) === EXEMPT_LABEL)) continue;
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [EXEMPT_LABEL],
});
labeled += 1;
}
core.info(`Applied ${EXEMPT_LABEL} to ${labeled} org-member issue(s).`);
- name: Mark and close stale issues
uses: actions/stale@v9
with:
days-before-issue-stale: 30
days-before-issue-close: 7
# Disable PR processing; PRs are handled by pr-author-inactivity.yml.
days-before-pr-stale: -1
days-before-pr-close: -1
stale-issue-label: 'stale'
exempt-issue-labels: 'good first issue,help wanted,security,exempt-from-stale'
stale-issue-message: |
This issue has been inactive for 30 days and is being marked as stale. If it is still relevant, please leave a comment or push an update — otherwise it will be closed in 7 days.
This is only a queue-management step, not a rejection of the report. Closed issues can be reopened when work resumes.
close-issue-message: |
Closing this issue for now because it has been inactive for more than 37 days. If it is still relevant, please leave a comment and reopen — context is preserved and we can pick it back up.
remove-issue-stale-when-updated: true
operations-per-run: 100
ascending: true