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

damccorm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 3c41bd28c9a [PR Bot] Best-effort assign reviewers via GitHub API 
(#40313)
3c41bd28c9a is described below

commit 3c41bd28c9a8a27350058242c7cd77e287a52a81
Author: Danny McCormick <[email protected]>
AuthorDate: Mon Sep 28 15:28:45 2026 +0000

    [PR Bot] Best-effort assign reviewers via GitHub API (#40313)
---
 scripts/ci/pr-bot/findPrsNeedingAttention.ts |  4 +++
 scripts/ci/pr-bot/processNewPrs.ts           |  5 +++
 scripts/ci/pr-bot/shared/githubUtils.ts      | 25 ++++++++++++++
 scripts/ci/pr-bot/shared/userCommand.ts      |  5 +++
 scripts/ci/pr-bot/test/githubUtilsTest.ts    | 51 +++++++++++++++++++++++++++-
 5 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/scripts/ci/pr-bot/findPrsNeedingAttention.ts 
b/scripts/ci/pr-bot/findPrsNeedingAttention.ts
index 46233b4ce31..46514671be0 100644
--- a/scripts/ci/pr-bot/findPrsNeedingAttention.ts
+++ b/scripts/ci/pr-bot/findPrsNeedingAttention.ts
@@ -133,6 +133,10 @@ async function assignToNewReviewers(
       labels: pull.labels,
     })
   );
+  await github.requestPrReviewers(
+    pull.number,
+    Object.values(prState.reviewersAssignedForLabels)
+  );
 
   await stateClient.writePrState(pull.number, prState);
   let labelsToUpdate = Object.keys(reviewerStateToUpdate);
diff --git a/scripts/ci/pr-bot/processNewPrs.ts 
b/scripts/ci/pr-bot/processNewPrs.ts
index 90957451acc..fbcd78a0db8 100644
--- a/scripts/ci/pr-bot/processNewPrs.ts
+++ b/scripts/ci/pr-bot/processNewPrs.ts
@@ -300,6 +300,7 @@ async function processPull(
           pull.number,
           commentStrings.assignCommitter(chosenCommitter)
         );
+        await github.requestPrReviewers(pull.number, [chosenCommitter]);
         await github.nextActionReviewers(pull.number, pull.labels);
         prState.nextAction = REVIEWERS_ACTION;
 
@@ -359,6 +360,10 @@ async function processPull(
       labels: pull.labels,
     })
   );
+  await github.requestPrReviewers(
+    pull.number,
+    Object.values(prState.reviewersAssignedForLabels)
+  );
 
   github.nextActionReviewers(pull.number, pull.labels);
   prState.nextAction = "Reviewers";
diff --git a/scripts/ci/pr-bot/shared/githubUtils.ts 
b/scripts/ci/pr-bot/shared/githubUtils.ts
index 696f5f9c739..ce05aa3b44b 100644
--- a/scripts/ci/pr-bot/shared/githubUtils.ts
+++ b/scripts/ci/pr-bot/shared/githubUtils.ts
@@ -42,6 +42,31 @@ export async function addPrComment(pullNumber: number, body: 
string) {
   });
 }
 
+export async function requestPrReviewers(
+  pullNumber: number,
+  reviewers: string[],
+  githubClient?: any
+) {
+  if (!reviewers || reviewers.length === 0) {
+    return;
+  }
+  const client = githubClient || getGitHubClient();
+  for (const reviewer of reviewers) {
+    try {
+      await client.rest.pulls.requestReviewers({
+        owner: REPO_OWNER,
+        repo: REPO,
+        pull_number: pullNumber,
+        reviewers: [reviewer],
+      });
+    } catch (err) {
+      console.log(
+        `Unable to request review from ${reviewer} on PR ${pullNumber} (best 
effort): ${err}`
+      );
+    }
+  }
+}
+
 export async function nextActionReviewers(
   pullNumber: number,
   existingLabels: Label[]
diff --git a/scripts/ci/pr-bot/shared/userCommand.ts 
b/scripts/ci/pr-bot/shared/userCommand.ts
index e1bb6067903..8c09013553e 100644
--- a/scripts/ci/pr-bot/shared/userCommand.ts
+++ b/scripts/ci/pr-bot/shared/userCommand.ts
@@ -109,6 +109,7 @@ async function assignToNextReviewer(
         labels: existingLabels,
       })
     );
+    await github.requestPrReviewers(pullNumber, [chosenReviewer]);
 
     // Set next action to reviewer
     await github.nextActionReviewers(pullNumber, existingLabels);
@@ -233,6 +234,10 @@ async function assignReviewerSet(
       labels: existingLabels,
     })
   );
+  await github.requestPrReviewers(
+    pullNumber,
+    Object.values(prState.reviewersAssignedForLabels)
+  );
 
   github.nextActionReviewers(pullNumber, existingLabels);
   prState.nextAction = "Reviewers";
diff --git a/scripts/ci/pr-bot/test/githubUtilsTest.ts 
b/scripts/ci/pr-bot/test/githubUtilsTest.ts
index b6da236247a..bf6b460a51c 100644
--- a/scripts/ci/pr-bot/test/githubUtilsTest.ts
+++ b/scripts/ci/pr-bot/test/githubUtilsTest.ts
@@ -17,7 +17,7 @@
  */
 
 var assert = require("assert");
-const { hasLabel } = require("../shared/githubUtils");
+const { hasLabel, requestPrReviewers } = require("../shared/githubUtils");
 
 describe("githubUtils", function () {
   describe("hasLabel()", function () {
@@ -58,6 +58,55 @@ describe("githubUtils", function () {
       assert.equal(hasLabel(null, "awaiting triage"), false);
     });
   });
+
+  describe("requestPrReviewers()", function () {
+    it("should request review for each reviewer via the GitHub client", async 
function () {
+      const requested: string[][] = [];
+      const mockClient = {
+        rest: {
+          pulls: {
+            requestReviewers: async ({
+              reviewers,
+            }: {
+              reviewers: string[];
+            }) => {
+              requested.push(reviewers);
+            },
+          },
+        },
+      };
+      await requestPrReviewers(123, ["reviewer1", "reviewer2"], mockClient);
+      assert.deepEqual(requested, [["reviewer1"], ["reviewer2"]]);
+    });
+
+    it("should continue best-effort if requesting a reviewer fails", async 
function () {
+      const requested: string[][] = [];
+      const mockClient = {
+        rest: {
+          pulls: {
+            requestReviewers: async ({
+              reviewers,
+            }: {
+              reviewers: string[];
+            }) => {
+              if (reviewers[0] === "nonCollaborator") {
+                throw new Error(
+                  "Reviews may only be requested from collaborators."
+                );
+              }
+              requested.push(reviewers);
+            },
+          },
+        },
+      };
+      await requestPrReviewers(
+        123,
+        ["nonCollaborator", "collaboratorReviewer"],
+        mockClient
+      );
+      assert.deepEqual(requested, [["collaboratorReviewer"]]);
+    });
+  });
 });
 
 export {};

Reply via email to