This is an automated email from the ASF dual-hosted git repository.

Yicong-Huang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new 3471e603e2 ci: auto-assign PR author, credit author on issue close 
(#4551)
3471e603e2 is described below

commit 3471e603e2ee004328ce9935c5ca99b251a44c32
Author: Xinyuan Lin <[email protected]>
AuthorDate: Tue Apr 28 22:32:50 2026 -0700

    ci: auto-assign PR author, credit author on issue close (#4551)
    
    ### What changes were proposed in this PR?
    Adds `.github/workflows/auto-assign.yml` with two jobs:
    
    1. **assign-pr-author** — on `opened` / `reopened` / `ready_for_review`,
    assigns the PR author as the assignee. Skips bots and PRs that already
    have an assignee.
    
    2. **credit-issue-on-pr-merge** — on PR merge (`pull_request_target:
    closed` + `merged == true`), uses the GraphQL `closingIssuesReferences`
    edge to find the issues this PR closes, then replaces their assignees
    with the PR author. This covers `Closes` / `Fixes` / `Resolves` keywords
    and the manual "Linked issues" sidebar relationship.
    
    New issues are intentionally left unassigned so the existing
    `issue-triage.yml` workflow can keep them in the `triage` queue until a
    human assigns them.
    
    ### Any related issues, documentation, discussions?
    Closes #4550
    
    ### How was this PR tested?
    Config-only change; the workflow runs on the next opened/reopened PR and
    on the next PR merge. Each job guards no-op cases:
    
    - `assign-pr-author` skips bot authors, PRs missing a user login, and
    PRs that already have assignees.
    - `credit-issue-on-pr-merge` skips bot authors, skips when no linked
    issues exist, skips cross-repo linked issues, and only removes assignees
    other than the PR author (so it's idempotent if re-run).
    
    Permissions are scoped to `issues: write` and `pull-requests: write`.
    Uses the default `GITHUB_TOKEN` — no new secrets required.
    `pull_request_target` is used so PRs from forks still get assigned and
    the merge-time job has write access.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    Generated-by: Claude Code (Opus 4.7)
---
 .github/workflows/auto-assign.yml | 96 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/.github/workflows/auto-assign.yml 
b/.github/workflows/auto-assign.yml
new file mode 100644
index 0000000000..48fe10f6c2
--- /dev/null
+++ b/.github/workflows/auto-assign.yml
@@ -0,0 +1,96 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: Auto-assign
+on:
+  pull_request_target:
+    types: [opened, closed]
+
+permissions:
+  issues: write
+  pull-requests: write
+
+jobs:
+  assign-pr-author:
+    if: >-
+      github.event.action == 'opened'
+      && github.event.pull_request.user.type != 'Bot'
+      && github.event.pull_request.assignees[0] == null
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/github-script@v7
+        with:
+          script: |
+            const pr = context.payload.pull_request;
+            await github.rest.issues.addAssignees({
+              ...context.repo,
+              issue_number: pr.number,
+              assignees: [pr.user.login],
+            });
+
+  credit-issue-on-pr-merge:
+    if: github.event.action == 'closed' && github.event.pull_request.merged
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/github-script@v7
+        with:
+          script: |
+            const { owner, repo } = context.repo;
+            const opener = context.payload.pull_request.user;
+            const isHuman = (l) => l && !l.endsWith('[bot]');
+
+            const { repository: { pullRequest: prq } } = await github.graphql(`
+              query($owner: String!, $repo: String!, $pr: Int!) {
+                repository(owner: $owner, name: $repo) {
+                  pullRequest(number: $pr) {
+                    closingIssuesReferences(first: 50) {
+                      nodes {
+                        number
+                        repository { nameWithOwner }
+                        assignees(first: 20) { nodes { login } }
+                      }
+                    }
+                    commits(first: 250) {
+                      nodes { commit {
+                        parents { totalCount }
+                        authors(first: 10) { nodes { user { login } } }
+                      } }
+                    }
+                  }
+                }
+              }`, { owner, repo, pr: context.payload.pull_request.number });
+
+            const authors = new Set();
+            if (opener.type !== 'Bot' && isHuman(opener.login)) 
authors.add(opener.login);
+            for (const { commit } of prq.commits.nodes) {
+              if (commit.parents.totalCount > 1) continue;
+              for (const a of commit.authors.nodes) {
+                if (isHuman(a.user?.login)) authors.add(a.user.login);
+              }
+            }
+            const credited = [...authors].slice(0, 10);
+            if (!credited.length) return;
+            const creditedSet = new Set(credited);
+
+            for (const issue of prq.closingIssuesReferences.nodes) {
+              if (issue.repository.nameWithOwner !== `${owner}/${repo}`) 
continue;
+              const current = issue.assignees.nodes.map(n => n.login);
+              const toRemove = current.filter(l => !creditedSet.has(l));
+              const toAdd = credited.filter(l => !current.includes(l));
+              const args = { owner, repo, issue_number: issue.number };
+              if (toRemove.length) await github.rest.issues.removeAssignees({ 
...args, assignees: toRemove });
+              if (toAdd.length) await github.rest.issues.addAssignees({ 
...args, assignees: toAdd });
+            }

Reply via email to