Yicong-Huang commented on code in PR #4885: URL: https://github.com/apache/texera/pull/4885#discussion_r3179232567
########## .github/workflows/pr-assignment.yml: ########## @@ -0,0 +1,205 @@ +# 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: PR assignment +on: + pull_request_target: + types: [opened, edited, closed] + +permissions: + issues: write + pull-requests: write + +jobs: + # All three behaviors live as steps under one job so the PR Checks + # tab shows a single entry per event instead of two-or-three skipped + # siblings. Step-level if-guards keep the actual work scoped. + pr-assignment: + runs-on: ubuntu-latest + steps: + - name: Self-assign PR author to the PR + if: >- + github.event.action == 'opened' + && github.event.pull_request.user.type != 'Bot' + && github.event.pull_request.assignees[0] == null + uses: actions/github-script@v8 + with: + script: | + const pr = context.payload.pull_request; + core.info(`PR #${pr.number} opened by ${pr.user.login}; self-assigning.`); + try { + await github.rest.issues.addAssignees({ + ...context.repo, + issue_number: pr.number, + assignees: [pr.user.login], + }); + core.info(`Assigned ${pr.user.login} to PR #${pr.number}`); + } catch (e) { + core.warning(`Self-assign on PR #${pr.number} failed: ${e.message}`); + } + + - name: Sync PR opener as assignee on linked issues + # Mirror the PR opener as an assignee on each same-repo issue listed + # in closingIssuesReferences. On body edits, also drop the opener + # from issues whose closing keyword was removed. Other manual + # assignees are never touched here, so this never fights with the + # merge-time credit step or human triage decisions. + if: >- + contains(fromJSON('["opened","edited"]'), github.event.action) + && github.event.pull_request.state == 'open' + && github.event.pull_request.user.type != 'Bot' + uses: actions/github-script@v8 + with: + script: | + const { owner, repo } = context.repo; + const pr = context.payload.pull_request; + const opener = pr.user.login; + core.info(`Event ${context.payload.action} on PR #${pr.number} by ${opener}; syncing closing-issue assignees.`); + + 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 } } + } + } + } + }`, { owner, repo, pr: pr.number }); + + const sameRepo = `${owner}/${repo}`; + const allRefs = prq.closingIssuesReferences.nodes; + const linked = allRefs + .filter((n) => n.repository.nameWithOwner === sameRepo) + .map((n) => n.number); + const crossRepo = allRefs.filter((n) => n.repository.nameWithOwner !== sameRepo); + core.info(`Found ${linked.length} same-repo closing reference(s): ${linked.join(', ') || '(none)'}`); + if (crossRepo.length) { + core.info(`Skipping ${crossRepo.length} cross-repo reference(s): ${crossRepo.map((n) => `${n.repository.nameWithOwner}#${n.number}`).join(', ')}`); + } + + for (const issue_number of linked) { + try { + await github.rest.issues.addAssignees({ + owner, repo, issue_number, assignees: [opener], + }); + core.info(`Assigned ${opener} to issue #${issue_number}`); + } catch (e) { + core.warning(`addAssignees on #${issue_number} failed: ${e.message}`); + } + } + + // On body edit, find closing refs that disappeared from the body + // and remove the opener from those issues. closingIssuesReferences + // is a snapshot of the *new* state, so we need text-diff to detect + // removals. Cross-repo refs are intentionally skipped. + if ( + context.payload.action === 'edited' && + context.payload.changes && + context.payload.changes.body && + typeof context.payload.changes.body.from === 'string' + ) { + const re = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi; + const oldBody = context.payload.changes.body.from || ''; + const newBody = pr.body || ''; + const oldRefs = new Set([...oldBody.matchAll(re)].map((m) => Number(m[1]))); + const newRefs = new Set([...newBody.matchAll(re)].map((m) => Number(m[1]))); + const removed = [...oldRefs].filter((n) => !newRefs.has(n)); Review Comment: Good catch. Broadened the regex to `:?\s+#` so `Closes: #123` (and `Closes: #123`) match. Multi-keyword forms (`closes #1, closes #2`) keep working. Kept the no-keyword shorthand (`Closes #1, #2`) excluded — `closingIssuesReferences` only links `#1` in that case, so the body-diff treats only `#1` as a removal, matching the assign-side semantics. Verified locally with 12 shape variants. See c8f338778. ########## .github/workflows/pr-assignment.yml: ########## @@ -0,0 +1,205 @@ +# 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: PR assignment +on: + pull_request_target: + types: [opened, edited, closed] + +permissions: + issues: write + pull-requests: write + +jobs: + # All three behaviors live as steps under one job so the PR Checks + # tab shows a single entry per event instead of two-or-three skipped + # siblings. Step-level if-guards keep the actual work scoped. + pr-assignment: + runs-on: ubuntu-latest + steps: + - name: Self-assign PR author to the PR + if: >- + github.event.action == 'opened' + && github.event.pull_request.user.type != 'Bot' + && github.event.pull_request.assignees[0] == null + uses: actions/github-script@v8 + with: + script: | + const pr = context.payload.pull_request; + core.info(`PR #${pr.number} opened by ${pr.user.login}; self-assigning.`); + try { + await github.rest.issues.addAssignees({ + ...context.repo, + issue_number: pr.number, + assignees: [pr.user.login], + }); + core.info(`Assigned ${pr.user.login} to PR #${pr.number}`); + } catch (e) { + core.warning(`Self-assign on PR #${pr.number} failed: ${e.message}`); + } + + - name: Sync PR opener as assignee on linked issues + # Mirror the PR opener as an assignee on each same-repo issue listed + # in closingIssuesReferences. On body edits, also drop the opener + # from issues whose closing keyword was removed. Other manual + # assignees are never touched here, so this never fights with the + # merge-time credit step or human triage decisions. + if: >- + contains(fromJSON('["opened","edited"]'), github.event.action) + && github.event.pull_request.state == 'open' + && github.event.pull_request.user.type != 'Bot' + uses: actions/github-script@v8 Review Comment: Right — added a new step that runs on `closed && merged == false && user.type != 'Bot'`. It pulls `closingIssuesReferences`, filters to same-repo, and removes the opener from each. The `issue-triage.yml` workflow's `unassigned` handler then re-adds `triage` if no other assignees remain, so we don't need to duplicate that logic here. Cross-repo refs are skipped, consistent with the assign side. See c8f338778. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
