Add daily PR review alerts workflow#3102
Conversation
Sends a daily email (8AM Pacific / 15:00 UTC) listing open PRs requesting review from wwwillchen across all dyad-sh org repos. Also supports manual workflow_dispatch trigger. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
@BugBot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| `Failed to send PR review alert email: ${response.status} ${body}`, | ||
| ); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Six utility functions duplicated from existing script
Low Severity
Six utility functions (requireEnv, parseRecipients, readResponseBody, escapeHtml, appendStepSummary, sendMailgunEmail) and the MAILGUN_API_BASE_URL constant are copied verbatim from scripts/github-security-advisory-alert.mjs. Extracting these into a shared module would avoid the maintenance burden of keeping two copies in sync — a bug fix in one file could easily be missed in the other.
Additional Locations (1)
There was a problem hiding this comment.
Pull request overview
Adds an automated GitHub Actions workflow that emails a daily digest of open PRs requesting review from a specific user across the dyad-sh org.
Changes:
- Introduces a Node.js script to query GitHub Search for PRs needing review and send a Mailgun email (plain text + HTML).
- Adds a scheduled + manual GitHub Actions workflow to run the script daily and on demand.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
scripts/pr-review-alert.mjs |
Implements PR search, grouping/formatting, and Mailgun email delivery. |
.github/workflows/pr-review-alerts.yml |
Schedules and runs the alert script with GitHub App auth + Mailgun configuration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const match = htmlUrl.match(/github\.com\/([^/]+\/[^/]+)\//); | ||
| return match ? match[1] : "unknown"; |
There was a problem hiding this comment.
formatRepoName is hard-coded to github.com, so on GitHub Enterprise / non-standard hosts (or if the API returns URLs with a different hostname) every PR will be grouped under unknown. Consider parsing htmlUrl via new URL(htmlUrl).pathname and extracting the first two path segments (owner/repo), independent of hostname.
| const match = htmlUrl.match(/github\.com\/([^/]+\/[^/]+)\//); | |
| return match ? match[1] : "unknown"; | |
| try { | |
| const { pathname } = new URL(htmlUrl); | |
| const parts = pathname.replace(/^\/+|\/+$/g, "").split("/"); | |
| if (parts.length >= 2) { | |
| return `${parts[0]}/${parts[1]}`; | |
| } | |
| } catch { | |
| // Ignore URL parsing errors and fall through to "unknown" | |
| } | |
| return "unknown"; |
| while (true) { | ||
| const url = new URL(`${apiBaseUrl}/search/issues`); | ||
| url.searchParams.set("q", query); | ||
| url.searchParams.set("per_page", "100"); | ||
| url.searchParams.set("page", String(page)); | ||
|
|
||
| const response = await fetch(url, { | ||
| headers: { | ||
| Authorization: `Bearer ${token}`, | ||
| Accept: "application/vnd.github+json", | ||
| "X-GitHub-Api-Version": GITHUB_API_VERSION, | ||
| }, | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| const body = await readResponseBody(response); | ||
| throw new Error( | ||
| `Failed to search PRs needing review: ${response.status} ${body}`, | ||
| ); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| if (!data.items || !Array.isArray(data.items)) { | ||
| throw new Error("Unexpected search response shape"); | ||
| } | ||
|
|
||
| allPRs.push(...data.items); | ||
|
|
||
| if (allPRs.length >= data.total_count || data.items.length === 0) { | ||
| break; | ||
| } | ||
|
|
||
| page++; | ||
| } |
There was a problem hiding this comment.
The GitHub Search API only supports paging through the first 1000 results (10 pages at per_page=100). This loop can keep incrementing page when total_count is larger, leading to wasted API calls, potential 422 errors, and avoidable rate-limit burn. Consider capping the page count (and optionally surfacing in the summary/email that results may be truncated).
| # Daily at 15:00 UTC (8 AM Pacific Daylight Time) | ||
| - cron: "0 15 * * *" |
There was a problem hiding this comment.
The comment (and PR description) imply this runs at "8 AM Pacific / 15:00 UTC", but 15:00 UTC is 8 AM only during PDT (it’s 7 AM during PST). Please either adjust the cron to match the intended local time, or clarify the comment to reflect the DST behavior (fixed UTC time).
There was a problem hiding this comment.
1 issue found across 2 files
Confidence score: 4/5
- Minor workflow runtime mismatch:
.github/workflows/pr-review-alerts.ymluses a Node version below the repo’s supported >=24, which could cause the alert script to behave differently than in the project target environment. - Overall risk is low since this is confined to CI tooling rather than product code, but aligning the version reduces uncertainty.
- Pay close attention to
.github/workflows/pr-review-alerts.yml- ensure Node version matches the repo’s supported runtime.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/pr-review-alerts.yml">
<violation number="1" location=".github/workflows/pr-review-alerts.yml:40">
P2: Use the repository’s supported Node version (>=24) so the alert script runs under the same runtime the project targets.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| - name: Setup Node | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 |
There was a problem hiding this comment.
P2: Use the repository’s supported Node version (>=24) so the alert script runs under the same runtime the project targets.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/pr-review-alerts.yml, line 40:
<comment>Use the repository’s supported Node version (>=24) so the alert script runs under the same runtime the project targets.</comment>
<file context>
@@ -0,0 +1,54 @@
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+
+ - name: Send PR review alert email
</file context>
🔍 Dyadbot Code Review SummaryVerdict: ✅ YES - Ready to merge Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard. Issues SummaryNo HIGH or MEDIUM issues confirmed after validation. 🟢 Low Priority Notes (4 items)
🚫 Dropped False Positives (5 items)
Generated by Dyadbot multi-agent code review |
There was a problem hiding this comment.
Code Review
This pull request introduces a new script, scripts/pr-review-alert.mjs, which automates the process of notifying users about open pull requests awaiting their review. The script fetches relevant PRs from the GitHub API, groups them by repository, and sends a summary email via Mailgun in both plain text and HTML formats. Review feedback suggests enhancing the script's robustness by updating the repository name extraction regex to handle URLs without trailing slashes and quoting the GitHub username in the search query to prevent potential parsing errors.
| }; | ||
|
|
||
| const formatRepoName = (htmlUrl) => { | ||
| const match = htmlUrl.match(/github\.com\/([^/]+\/[^/]+)\//); |
There was a problem hiding this comment.
The regular expression used to extract the repository name requires a trailing slash after the repository name to match. While GitHub Pull Request URLs typically include a path after the repository (e.g., /pull/123), this regex would fail to match a base repository URL like https://github.com/dyad-sh/dyad. Removing the mandatory trailing slash makes the utility more robust.
const match = htmlUrl.match(/github\.com\/([^/]+\/[^/]+)/);| }; | ||
|
|
||
| const fetchPRsNeedingReview = async ({ apiBaseUrl, token, username }) => { | ||
| const query = `is:open is:pr review-requested:${username} org:dyad-sh`; |
There was a problem hiding this comment.
The GitHub search query for review-requested might fail if the username contains special characters or spaces (though rare for GitHub handles, it's possible for team names if this script were adapted). It is safer to wrap the username in quotes to ensure the query is parsed correctly by the GitHub Search API.
const query = 'is:open is:pr review-requested:"' + username + '" org:dyad-sh';

Sends a daily email (8AM Pacific / 15:00 UTC) listing open PRs requesting review from wwwillchen across all dyad-sh org repos. Also supports manual workflow_dispatch trigger.