From 625e48ffea7340d9fc97f1eacb83c804f72e1f7b Mon Sep 17 00:00:00 2001 From: Joel Baxter Date: Thu, 17 Sep 2026 10:19:11 +1000 Subject: [PATCH] Add assignees to release PRs If a PR has been assigned to someone else, they should become involved in the release PR. This is especially helpful atm for our automated gateway schema updates, where we can't automatically create PRs authenticated as the original author, but we can assign them to the PR after it's created. --- .../release-pr-bot/manage-release-pr.js | 18 +++++-- .../release-pr-bot/manage-release-pr.test.js | 48 +++++++++++++++++++ README.md | 23 +++++---- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/.github/actions/release-pr-bot/manage-release-pr.js b/.github/actions/release-pr-bot/manage-release-pr.js index 05d7fab..f007f1b 100644 --- a/.github/actions/release-pr-bot/manage-release-pr.js +++ b/.github/actions/release-pr-bot/manage-release-pr.js @@ -101,17 +101,25 @@ async function fetchUnreleasedPRs({ ) } +function involvedLogins(pr) { + const logins = [pr.user?.login, ...(pr.assignees ?? []).map((a) => a?.login)] + return [...new Set(logins.filter(Boolean))] +} + function buildReviewers(prs) { const logins = prs - .map((pr) => pr.user?.login) - .filter((login) => login && !login.includes('[bot]')) + .flatMap(involvedLogins) + .filter((login) => !login.includes('[bot]')) return [...new Set(logins)] } function buildBody(prs, mainBranch, developBranch) { - const prLines = prs.map( - (pr) => `- #${pr.number} — ${pr.title} (@${pr.user?.login})`, - ) + const prLines = prs.map((pr) => { + const mentions = involvedLogins(pr) + .map((login) => `@${login}`) + .join(', ') + return `- #${pr.number} — ${pr.title} (${mentions})` + }) return [ '## 🚀 Release PR', '', diff --git a/.github/actions/release-pr-bot/manage-release-pr.test.js b/.github/actions/release-pr-bot/manage-release-pr.test.js index a90b0d1..94d2fc3 100644 --- a/.github/actions/release-pr-bot/manage-release-pr.test.js +++ b/.github/actions/release-pr-bot/manage-release-pr.test.js @@ -120,4 +120,52 @@ describe('manage-release-pr', () => { expect(github.rest.pulls.create).toHaveBeenCalled() expect(github.rest.pulls.requestReviewers).not.toHaveBeenCalled() }) + + it('includes assignees of PRs as reviewers and mentions', async () => { + const github = makeGithub({ + commits: [{ sha: 'abc123' }], + mergedPRs: [ + { + ...unreleasedPR, + user: { login: 'gateway-bot[bot]' }, + assignees: [{ login: 'bob' }], + }, + ], + openPRs: [], + }) + + await run({ github, context: makeContext() }) + + expect(github.rest.pulls.requestReviewers).toHaveBeenCalledWith( + expect.objectContaining({ reviewers: ['bob'] }), + ) + expect(github.rest.pulls.create).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('(@gateway-bot[bot], @bob)'), + }), + ) + }) + + it('deduplicates authors and assignees across PRs', async () => { + const github = makeGithub({ + commits: [{ sha: 'abc123' }, { sha: 'def456' }], + mergedPRs: [ + { ...unreleasedPR, assignees: [{ login: 'alice' }] }, + { + ...unreleasedPR, + number: 2, + merge_commit_sha: 'def456', + user: { login: 'gateway-bot[bot]' }, + assignees: [{ login: 'alice' }], + }, + ], + openPRs: [], + }) + + await run({ github, context: makeContext() }) + + expect(github.rest.pulls.requestReviewers).toHaveBeenCalledWith( + expect.objectContaining({ reviewers: ['alice'] }), + ) + }) }) diff --git a/README.md b/README.md index c9044d9..c7c6d22 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [ra.co](https://ra.co) - This repo is used to house our organisation-wide health files, which can include `CONTRIBUTING`, `SUPPORT`, `CODE_OF_CONDUCT`, `ISSUE_TEMPLATE`(S), or `PULL_REQUEST_TEMPLATE`(S). If another organisation repo doesn't include one of these files, it will fallback to the one found in this repo. See [here](https://github.blog/changelog/2019-02-21-organization-wide-community-health-files/) for more info. @@ -16,8 +15,8 @@ This repo houses reusable GitHub Actions that any repo in the organisation can r Automatically creates or updates a release PR from `develop` → `main`. Each time it runs, it: - Finds all merged PRs on `develop` not yet in `main` -- Builds a PR body listing each one with author -- Auto-requests reviews from the authors of those PRs +- Builds a PR body listing each one with its author and assignees +- Auto-requests reviews from those authors and assignees - Updates the PR body and adds any new reviewers if the release PR already exists #### Usage @@ -46,19 +45,19 @@ jobs: To use non-default branch names: ```yaml - steps: - - uses: resident-advisor/.github/.github/actions/release-pr-bot@v1.0.1 - with: - main_branch: master - develop_branch: staging +steps: + - uses: resident-advisor/.github/.github/actions/release-pr-bot@v1.0.1 + with: + main_branch: master + develop_branch: staging ``` #### Inputs -| Input | Default | Description | -| ---------------- | --------- | ---------------------------- | -| `main_branch` | `main` | The branch to release into | -| `develop_branch` | `develop` | The branch being released | +| Input | Default | Description | +| ---------------- | --------- | -------------------------- | +| `main_branch` | `main` | The branch to release into | +| `develop_branch` | `develop` | The branch being released | ## Releasing New Action Versions