From ba41dadc91ab125fe1397677318d3470d88df9fb Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 23:48:58 -0500 Subject: [PATCH] ci: skip issue actions for team authors (#34556) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: opencode-agent[bot] Co-authored-by: Aiden Cline --- .github/workflows/compliance-close.yml | 28 +++++++++++++++++-- .github/workflows/duplicate-issues.yml | 38 ++++++++++++++++++++++++++ .github/workflows/triage.yml | 19 +++++++++++++ script/github/close-issues.ts | 19 +++++++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compliance-close.yml b/.github/workflows/compliance-close.yml index a6e4b9215f..a83824e5cf 100644 --- a/.github/workflows/compliance-close.yml +++ b/.github/workflows/compliance-close.yml @@ -35,13 +35,37 @@ jobs: const now = Date.now(); const twoHours = 2 * 60 * 60 * 1000; const orgMemberAssociations = new Set(['OWNER', 'MEMBER']); + const agentLogin = 'opencode-agent[bot]'; + const { data: file } = await github.rest.repos.getContent({ + owner: context.repo.owner, + repo: context.repo.repo, + path: '.github/TEAM_MEMBERS', + ref: 'dev', + }); + const teamMembers = new Set( + Buffer.from(file.content, 'base64') + .toString() + .split('\n') + .map((line) => line.trim().toLowerCase()) + .filter(Boolean) + ); + + function isExempt(item) { + const login = item.user?.login?.toLowerCase(); + return ( + login === agentLogin || + orgMemberAssociations.has(item.author_association) || + (login && teamMembers.has(login)) + ); + } for (const item of items) { const isPR = !!item.pull_request; const kind = isPR ? 'PR' : 'issue'; + const login = item.user?.login; - if (orgMemberAssociations.has(item.author_association)) { - core.info(`Skipping ${kind} #${item.number}; author association is ${item.author_association}`); + if (isExempt(item)) { + core.info(`Skipping ${kind} #${item.number}; author ${login || 'unknown'} is exempt`); try { await github.rest.issues.removeLabel({ owner: context.repo.owner, diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index b4500e0cc1..3972247daf 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -17,12 +17,31 @@ jobs: with: fetch-depth: 1 + - name: Check exempt issue author + id: author + run: | + LOGIN="${{ github.event.issue.user.login }}" + ASSOCIATION="${{ github.event.issue.author_association }}" + + if [ "$LOGIN" = "opencode-agent[bot]" ] || + [ "$ASSOCIATION" = "OWNER" ] || + [ "$ASSOCIATION" = "MEMBER" ] || + grep -qxiF "$LOGIN" .github/TEAM_MEMBERS; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "Skipping issue automation for exempt author: $LOGIN ($ASSOCIATION)" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + - uses: ./.github/actions/setup-bun + if: steps.author.outputs.skip != 'true' - name: Install opencode + if: steps.author.outputs.skip != 'true' run: curl -fsSL https://opencode.ai/install | bash - name: Check duplicates and compliance + if: steps.author.outputs.skip != 'true' env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -132,12 +151,31 @@ jobs: with: fetch-depth: 1 + - name: Check exempt issue author + id: author + run: | + LOGIN="${{ github.event.issue.user.login }}" + ASSOCIATION="${{ github.event.issue.author_association }}" + + if [ "$LOGIN" = "opencode-agent[bot]" ] || + [ "$ASSOCIATION" = "OWNER" ] || + [ "$ASSOCIATION" = "MEMBER" ] || + grep -qxiF "$LOGIN" .github/TEAM_MEMBERS; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "Skipping issue automation for exempt author: $LOGIN ($ASSOCIATION)" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + - uses: ./.github/actions/setup-bun + if: steps.author.outputs.skip != 'true' - name: Install opencode + if: steps.author.outputs.skip != 'true' run: curl -fsSL https://opencode.ai/install | bash - name: Recheck compliance + if: steps.author.outputs.skip != 'true' env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 27852a12ce..0350e43877 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -16,13 +16,32 @@ jobs: with: fetch-depth: 1 + - name: Check exempt issue author + id: author + run: | + LOGIN="${{ github.event.issue.user.login }}" + ASSOCIATION="${{ github.event.issue.author_association }}" + + if [ "$LOGIN" = "opencode-agent[bot]" ] || + [ "$ASSOCIATION" = "OWNER" ] || + [ "$ASSOCIATION" = "MEMBER" ] || + grep -qxiF "$LOGIN" .github/TEAM_MEMBERS; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "Skipping issue automation for exempt author: $LOGIN ($ASSOCIATION)" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + - name: Setup Bun + if: steps.author.outputs.skip != 'true' uses: ./.github/actions/setup-bun - name: Install opencode + if: steps.author.outputs.skip != 'true' run: curl -fsSL https://opencode.ai/install | bash - name: Triage issue + if: steps.author.outputs.skip != 'true' env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/script/github/close-issues.ts b/script/github/close-issues.ts index 9e1f597951..12d7aa191f 100755 --- a/script/github/close-issues.ts +++ b/script/github/close-issues.ts @@ -11,10 +11,20 @@ if (!token) { } const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000) +const agentLogin = "opencode-agent[bot]" +const teamMembers = new Set( + (await Bun.file(new URL("../../.github/TEAM_MEMBERS", import.meta.url)).text()) + .split("\n") + .map((line) => line.trim().toLowerCase()) + .filter(Boolean), +) +const teamAssociations = new Set(["OWNER", "MEMBER"]) type Issue = { number: number updated_at: string + author_association: string + user: { login: string } | null } const headers = { @@ -24,6 +34,11 @@ const headers = { "X-GitHub-Api-Version": "2022-11-28", } +function shouldSkip(i: Issue) { + const login = i.user?.login.toLowerCase() + return login === agentLogin || (login ? teamMembers.has(login) : false) || teamAssociations.has(i.author_association) +} + async function close(num: number) { const base = `https://api.github.com/repos/${repo}/issues/${num}` @@ -63,6 +78,10 @@ async function main() { for (const i of all) { const updated = new Date(i.updated_at) if (updated < cutoff) { + if (shouldSkip(i)) { + console.log(`Skipping stale issue #${i.number}; author ${i.user?.login ?? "unknown"} is exempt`) + continue + } stale.push(i.number) } else { console.log(`\nFound fresh issue #${i.number}, stopping`)