From 2d74486818354b15849961a0a0dc3e4468191aaf Mon Sep 17 00:00:00 2001 From: Daniel Hegberg Date: Thu, 7 May 2026 15:25:14 -0700 Subject: [PATCH 1/3] ci(workflows): Add issue management automations Add stale issue cleanup, closed issue message with lock, unlock on reopen, and regression labeler workflows. These automations keep the issue tracker healthy by closing unresponsive issues, preventing thread hijacking on closed issues, and auto-labeling regressions for priority escalation. --- .github/workflows/close-stale-issues.yml | 26 ++++++++++ .github/workflows/closed-issue-message.yml | 39 +++++++++++++++ .../workflows/issue-regression-labeler.yml | 49 +++++++++++++++++++ .github/workflows/unlock-reopened-issue.yml | 21 ++++++++ 4 files changed, 135 insertions(+) create mode 100644 .github/workflows/close-stale-issues.yml create mode 100644 .github/workflows/closed-issue-message.yml create mode 100644 .github/workflows/issue-regression-labeler.yml create mode 100644 .github/workflows/unlock-reopened-issue.yml diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml new file mode 100644 index 00000000..a1d4aef2 --- /dev/null +++ b/.github/workflows/close-stale-issues.yml @@ -0,0 +1,26 @@ +name: Close Stale Issues + +on: + schedule: + - cron: "0 */6 * * *" + +permissions: + issues: write + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - uses: aws-actions/stale-issue-cleanup@v6 + with: + response-requested-label: response-requested + days-before-stale: 7 + days-before-close: 5 + stale-issue-label: closing-soon + stale-issue-message: | + This issue has not received a response in 7 days. If no further activity occurs within the next 5 days, it will be automatically closed. + close-issue-message: | + Closing this issue due to inactivity. If you believe this is still relevant, please open a new issue with updated information. + minimum-upvotes-to-exempt: 5 + exempt-issue-labels: "bug,feature-request,p1" + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/closed-issue-message.yml b/.github/workflows/closed-issue-message.yml new file mode 100644 index 00000000..94c735b2 --- /dev/null +++ b/.github/workflows/closed-issue-message.yml @@ -0,0 +1,39 @@ +name: Closed Issue Message + +on: + issues: + types: [closed] + pull_request_target: + types: [closed] + +permissions: + issues: write + pull-requests: write + +jobs: + post-message-and-lock: + runs-on: ubuntu-latest + steps: + - uses: aws-actions/closed-issue-message@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: | + ⚠️ This issue has been closed. If you have a similar problem, please [open a new issue](../../issues/new/choose) and include relevant details so we can investigate properly. + + Comments on closed issues are hard for our team to track. A new issue ensures you get the attention you need. + pr-message: | + ⚠️ This pull request has been closed. If you'd like to continue this work, please open a new PR referencing this one for context. + + - uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const number = context.issue?.number || context.payload.pull_request?.number; + if (number) { + await github.rest.issues.lock({ + owner, + repo, + issue_number: number, + lock_reason: 'resolved' + }); + } diff --git a/.github/workflows/issue-regression-labeler.yml b/.github/workflows/issue-regression-labeler.yml new file mode 100644 index 00000000..301c40e3 --- /dev/null +++ b/.github/workflows/issue-regression-labeler.yml @@ -0,0 +1,49 @@ +name: Issue Regression Labeler + +on: + issues: + types: [opened, edited] + +permissions: + issues: write + +jobs: + label-regression: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + const body = context.payload.issue.body || ''; + const issueNumber = context.issue.number; + const { owner, repo } = context.repo; + + // Detect regression from checkbox format or dropdown format + const checkboxChecked = /- \[x\].*regression/i.test(body); + const dropdownYes = /### Is this a regression\?\s*\n\s*Yes/i.test(body); + const regressionChecked = checkboxChecked || dropdownYes; + + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + owner, + repo, + issue_number: issueNumber + }); + const hasRegressionLabel = labels.some(l => l.name === 'regression'); + + if (regressionChecked && !hasRegressionLabel) { + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: issueNumber, + labels: ['regression', 'p1'] + }); + core.info(`Labeled issue #${issueNumber} as regression + p1`); + } else if (!regressionChecked && hasRegressionLabel) { + await github.rest.issues.removeLabel({ + owner, + repo, + issue_number: issueNumber, + name: 'regression' + }); + core.info(`Removed regression label from issue #${issueNumber}`); + } diff --git a/.github/workflows/unlock-reopened-issue.yml b/.github/workflows/unlock-reopened-issue.yml new file mode 100644 index 00000000..619cf60c --- /dev/null +++ b/.github/workflows/unlock-reopened-issue.yml @@ -0,0 +1,21 @@ +name: Unlock Reopened Issues + +on: + issues: + types: [reopened] + +permissions: + issues: write + +jobs: + unlock: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.unlock({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); From 02aea30bc49cd46a8ee66a9ef84b1d15b4bc319b Mon Sep 17 00:00:00 2001 From: Daniel Hegberg Date: Fri, 8 May 2026 13:30:34 -0700 Subject: [PATCH 2/3] fix(workflows): Scope closed-issue-message to issues only Remove pull_request_target trigger and simplify to issues-only workflow. Replace aws-actions/closed-issue-message with actions/github-script to avoid incompatible input requirements. --- .github/workflows/closed-issue-message.yml | 37 +++++++++------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/.github/workflows/closed-issue-message.yml b/.github/workflows/closed-issue-message.yml index 94c735b2..690db9c8 100644 --- a/.github/workflows/closed-issue-message.yml +++ b/.github/workflows/closed-issue-message.yml @@ -3,37 +3,30 @@ name: Closed Issue Message on: issues: types: [closed] - pull_request_target: - types: [closed] permissions: issues: write - pull-requests: write jobs: post-message-and-lock: runs-on: ubuntu-latest steps: - - uses: aws-actions/closed-issue-message@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - issue-message: | - ⚠️ This issue has been closed. If you have a similar problem, please [open a new issue](../../issues/new/choose) and include relevant details so we can investigate properly. - - Comments on closed issues are hard for our team to track. A new issue ensures you get the attention you need. - pr-message: | - ⚠️ This pull request has been closed. If you'd like to continue this work, please open a new PR referencing this one for context. - - uses: actions/github-script@v7 with: script: | const { owner, repo } = context.repo; - const number = context.issue?.number || context.payload.pull_request?.number; - if (number) { - await github.rest.issues.lock({ - owner, - repo, - issue_number: number, - lock_reason: 'resolved' - }); - } + const number = context.issue.number; + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: number, + body: '⚠️ This issue has been closed. If you have a similar problem, please [open a new issue](../../issues/new/choose) and include relevant details so we can investigate properly.\n\nComments on closed issues are hard for our team to track. A new issue ensures you get the attention you need.' + }); + + await github.rest.issues.lock({ + owner, + repo, + issue_number: number, + lock_reason: 'resolved' + }); From ab63973e8467d1e552d457dc00f9aef2f90c3eaa Mon Sep 17 00:00:00 2001 From: Daniel Hegberg Date: Fri, 8 May 2026 20:23:24 -0700 Subject: [PATCH 3/3] ci(workflows): replace regression labeler with broader issue labeler - Adds needs-triage label on all new issues - Retains regression + p1 labeling from dropdown detection - Removes label removal logic (addLabels is idempotent) Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/issue-labeler.yml | 41 ++++++++++++++++ .../workflows/issue-regression-labeler.yml | 49 ------------------- 2 files changed, 41 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/issue-labeler.yml delete mode 100644 .github/workflows/issue-regression-labeler.yml diff --git a/.github/workflows/issue-labeler.yml b/.github/workflows/issue-labeler.yml new file mode 100644 index 00000000..9e5e9435 --- /dev/null +++ b/.github/workflows/issue-labeler.yml @@ -0,0 +1,41 @@ +name: Issue Labeler + +on: + issues: + types: [opened, edited] + +permissions: + issues: write + +jobs: + label-issue: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + const body = context.payload.issue.body || ''; + const action = context.payload.action; + const issueNumber = context.issue.number; + const { owner, repo } = context.repo; + + const labelsToAdd = []; + + if (action === 'opened') { + labelsToAdd.push('needs-triage'); + } + + const dropdownYes = /### Is this a regression\?\s*\n\s*Yes/i.test(body); + if (dropdownYes) { + labelsToAdd.push('regression', 'p1'); + } + + if (labelsToAdd.length > 0) { + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: issueNumber, + labels: labelsToAdd + }); + core.info(`Added labels to issue #${issueNumber}: ${labelsToAdd.join(', ')}`); + } diff --git a/.github/workflows/issue-regression-labeler.yml b/.github/workflows/issue-regression-labeler.yml deleted file mode 100644 index 301c40e3..00000000 --- a/.github/workflows/issue-regression-labeler.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Issue Regression Labeler - -on: - issues: - types: [opened, edited] - -permissions: - issues: write - -jobs: - label-regression: - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@v7 - with: - script: | - const body = context.payload.issue.body || ''; - const issueNumber = context.issue.number; - const { owner, repo } = context.repo; - - // Detect regression from checkbox format or dropdown format - const checkboxChecked = /- \[x\].*regression/i.test(body); - const dropdownYes = /### Is this a regression\?\s*\n\s*Yes/i.test(body); - const regressionChecked = checkboxChecked || dropdownYes; - - const { data: labels } = await github.rest.issues.listLabelsOnIssue({ - owner, - repo, - issue_number: issueNumber - }); - const hasRegressionLabel = labels.some(l => l.name === 'regression'); - - if (regressionChecked && !hasRegressionLabel) { - await github.rest.issues.addLabels({ - owner, - repo, - issue_number: issueNumber, - labels: ['regression', 'p1'] - }); - core.info(`Labeled issue #${issueNumber} as regression + p1`); - } else if (!regressionChecked && hasRegressionLabel) { - await github.rest.issues.removeLabel({ - owner, - repo, - issue_number: issueNumber, - name: 'regression' - }); - core.info(`Removed regression label from issue #${issueNumber}`); - }