From 51a2cb6308b7136c95b119aeb52e05ab0c110039 Mon Sep 17 00:00:00 2001 From: Jia Yu Date: Mon, 16 Mar 2026 23:42:54 -0700 Subject: [PATCH 1/3] Add reviewer list for auto-assignment --- .github/reviewers.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/reviewers.json diff --git a/.github/reviewers.json b/.github/reviewers.json new file mode 100644 index 000000000..b22e6b11f --- /dev/null +++ b/.github/reviewers.json @@ -0,0 +1 @@ +["paleolimbot", "james-willis", "zhangfengcdt", "prantogg"] From e884d2a562d216f09f48a9b4f273472925d30573 Mon Sep 17 00:00:00 2001 From: Jia Yu Date: Mon, 16 Mar 2026 23:43:32 -0700 Subject: [PATCH 2/3] Add reviewer assignment workflow (read-only trigger) --- .github/workflows/assign-reviewer.yml | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/assign-reviewer.yml diff --git a/.github/workflows/assign-reviewer.yml b/.github/workflows/assign-reviewer.yml new file mode 100644 index 000000000..0e8f1b84c --- /dev/null +++ b/.github/workflows/assign-reviewer.yml @@ -0,0 +1,44 @@ +# 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. + +# Captures PR metadata for the reviewer assignment workflow. +# The actual assignment is performed by assign-reviewer-write.yml +# (workflow_run trigger) which has write permission. +# See https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=321719166 + +name: "Compute Reviewer Assignment" +on: + pull_request: + types: [opened] + +jobs: + compute: + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + fs.mkdirSync('pr-meta'); + fs.writeFileSync('pr-meta/number.txt', String(context.issue.number)); + fs.writeFileSync('pr-meta/author.txt', context.payload.pull_request.user.login); + - uses: actions/upload-artifact@v4 + with: + name: pr-meta + path: pr-meta/ From ae8034bbf6ab26e7b0a0916384afaebf50afc5db Mon Sep 17 00:00:00 2001 From: Jia Yu Date: Mon, 16 Mar 2026 23:43:54 -0700 Subject: [PATCH 3/3] Add reviewer assignment workflow (write trigger) --- .github/workflows/assign-reviewer-write.yml | 111 ++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 .github/workflows/assign-reviewer-write.yml diff --git a/.github/workflows/assign-reviewer-write.yml b/.github/workflows/assign-reviewer-write.yml new file mode 100644 index 000000000..aaea5a98d --- /dev/null +++ b/.github/workflows/assign-reviewer-write.yml @@ -0,0 +1,111 @@ +# 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. + +# Reads PR metadata from the artifact produced by assign-reviewer.yml, +# loads the reviewer list from the trusted main branch, computes the +# round-robin assignment, and requests a review. +# See https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=321719166 + +name: "Assign Reviewer to PR" +on: + workflow_run: + workflows: ["Compute Reviewer Assignment"] + types: + - completed + +jobs: + assign: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: Checkout reviewers config + uses: actions/checkout@v4 + with: + repository: apache/sedona-db + ref: main + path: config + sparse-checkout: .github/reviewers.json + sparse-checkout-cone-mode: false + persist-credentials: false + + - name: Download PR metadata artifact + uses: actions/github-script@v7 + with: + script: | + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{ github.event.workflow_run.id }}, + }); + const match = artifacts.data.artifacts.find(a => a.name === 'pr-meta'); + if (!match) { + core.setFailed('No pr-meta artifact found'); + return; + } + const download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: match.id, + archive_format: 'zip', + }); + const fs = require('fs'); + fs.writeFileSync('${{ github.workspace }}/pr-meta.zip', Buffer.from(download.data)); + + - name: Unzip artifact + run: unzip -d pr-meta pr-meta.zip + + - name: Assign reviewer + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + + // Read reviewer list from trusted checkout (main branch) + const reviewers = JSON.parse(fs.readFileSync('config/.github/reviewers.json', 'utf8')); + + // Read PR metadata from artifact (untrusted - validate before use) + const prNumber = parseInt(fs.readFileSync('pr-meta/number.txt', 'utf8').trim(), 10); + const author = fs.readFileSync('pr-meta/author.txt', 'utf8').trim(); + + if (isNaN(prNumber) || prNumber <= 0) { + throw new Error(`Invalid PR number: ${prNumber}`); + } + if (!/^[a-zA-Z0-9_-]+$/.test(author)) { + throw new Error(`Invalid author: ${author}`); + } + + const eligible = reviewers.filter(r => r !== author); + if (eligible.length === 0) { + console.log('No eligible reviewers, skipping'); + return; + } + + const idx = prNumber % eligible.length; + const reviewer = eligible[idx]; + console.log(`Assigning ${reviewer} to PR #${prNumber}`); + + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + reviewers: [reviewer] + });