Skip to content

fix(checklint): resolve pr number for fork prs via paginated sha lookup#25

Merged
calebephrem merged 2 commits into
open-devhub:mainfrom
calebephrem:main
Jun 28, 2026
Merged

fix(checklint): resolve pr number for fork prs via paginated sha lookup#25
calebephrem merged 2 commits into
open-devhub:mainfrom
calebephrem:main

Conversation

@calebephrem

Copy link
Copy Markdown
Member

No description provided.

@devhub-bot devhub-bot Bot added the fix Bug fix label Jun 28, 2026
@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

@calebephrem is attempting to deploy a commit to the calebephrem's projects Team on Vercel.

A member of the Team first needs to authorize it.

@beetle-ai

beetle-ai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary by Beetle

This PR fixes a critical bug in the lint workflow bot where it failed to resolve PR numbers for fork-based pull requests. Previously, the bot only checked PRs from the same repository branch, causing it to silently fail when workflows ran on commits from forked repositories. The fix implements a paginated SHA-based lookup that searches through all open PRs (up to 1000 PRs across 10 pages) to match the workflow run's commit SHA, ensuring the bot can comment on both same-repo and fork PRs.

📁 File Changes Summary

File Status Changes Description
src/events/workflow_run.completed/checklint.ts Modified +108/-53 Refactored PR number resolution logic to support fork PRs via paginated SHA lookup. Added type safety with explicit TypeScript types, extracted comment generation into separate functions (buildSuccessComment, buildFailureComment), and implemented robust error handling with logging. The core fix adds a fallback mechanism that searches all open PRs when the branch-based lookup fails, checking up to 1000 PRs (100 per page × 10 pages) to find matching commit SHAs.

Total Changes: 1 file changed, +108 additions, -53 deletions

🗺️ Walkthrough

graph TD
A["Workflow Run Completed Event"] --> B{"Is Lint Workflow?"}
B -->|"No"| Z["Exit"]
B -->|"Yes"| C["Resolve PR Number"]
C --> D{"Direct PR Link Exists?"}
D -->|"Yes"| E["Return PR Number"]
D -->|"No"| F["Try Same-Repo Branch Lookup"]
F --> G{"Found PR with Matching SHA?"}
G -->|"Yes"| E
G -->|"No"| H["Paginated Fork PR Lookup"]
H --> I["Iterate Pages 1-10"]
I --> J["Fetch 100 PRs per Page"]
J --> K{"Found PR with Matching SHA?"}
K -->|"Yes"| E
K -->|"No, More Pages"| I
K -->|"No, End of Pages"| L["Return null"]
E --> M{"Workflow Conclusion?"}
M -->|"Success"| N["Post Success Comment"]
M -->|"Failure"| O["Post Failure Comment with Logs"]
L --> P["Log Warning & Skip"]
N1["Note: Searches up to 1000 PRs to handle fork contributions"]
H -.-> N1
Loading

🎯 Key Changes

  • Fork PR Support: Implemented paginated SHA-based lookup to resolve PR numbers for fork-based contributions, fixing the primary bug where fork PRs were silently ignored
  • Robust Fallback Strategy: Three-tier resolution approach: (1) direct PR links from workflow metadata, (2) same-repo branch lookup, (3) paginated all-PR SHA matching
  • Type Safety Improvements: Added explicit TypeScript types (WorkflowRunContext, WorkflowRun) and replaced loose type assertions with proper type definitions
  • Code Organization: Extracted comment generation logic into pure functions (buildSuccessComment, buildFailureComment) for better testability and maintainability
  • Enhanced Error Handling: Added try-catch blocks with contextual logging for both same-repo and fork PR lookups, preventing silent failures
  • Performance Optimization: Implemented early exit conditions (empty page results, partial page detection) to avoid unnecessary API calls
  • Configuration Constants: Introduced LINT_WORKFLOW_NAMES Set and pagination constants (PULLS_PER_PAGE, MAX_PAGES) for easier configuration

📊 Impact Assessment

  • Security: ✅ Positive Impact - No security vulnerabilities introduced. The paginated lookup uses authenticated GitHub API calls with proper error handling. The maximum page limit (10 pages) prevents potential DoS scenarios from infinite loops. No sensitive data exposure in logs.
  • Performance: ⚠️ Moderate Impact - The paginated lookup can make up to 10 additional API calls (1000 PRs total) in worst-case scenarios for fork PRs. However, early exit conditions minimize unnecessary calls. For same-repo PRs, performance is unchanged. Consider adding caching or reducing MAX_PAGES if rate limiting becomes an issue in high-traffic repositories.
  • Maintainability: ✅ Significantly Improved - Code is much more maintainable with extracted functions, explicit types, and clear separation of concerns. The three-tier resolution strategy is well-documented through code structure. Constants at the top make configuration changes trivial. Logging provides excellent debugging visibility. The refactoring reduces cyclomatic complexity and improves readability.
  • Testing: ⚠️ Needs Attention - No test files were modified or added in this PR. The complex paginated lookup logic with multiple edge cases (empty pages, partial pages, API failures, fork vs same-repo) would benefit significantly from unit tests. Recommend adding tests for: (1) direct PR link resolution, (2) same-repo branch matching, (3) paginated fork PR lookup, (4) early exit conditions, (5) error handling paths, (6) comment generation functions.
⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only.Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

Comment on lines +130 to +151
for (let page = 1; page <= MAX_PAGES; page++) {
const { data: prs } = await context.octokit.rest.pulls.list({
owner,
repo,
state: "open",
per_page: PULLS_PER_PAGE,
page,
});

if (prs.length === 0) {
break;
}

const forkMatch = prs.find((pr) => pr.head.sha === headSha);
if (forkMatch) {
return forkMatch.number;
}

if (prs.length < PULLS_PER_PAGE) {
break;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paginated fork PR lookup could fetch up to 1000 PRs (10 pages × 100 per page) sequentially, which may cause significant latency and rate limit issues. For repositories with many open PRs, this could take several seconds and consume substantial API quota on every workflow run.
Performance Impact:

  • Each API call adds ~200-500ms latency
  • Up to 10 sequential API calls = 2-5 seconds worst case
  • GitHub API rate limits could be exhausted quickly
  • Blocks the workflow completion handler during this time
    Recommendation: Consider adding a SHA-based search or limiting the pagination more aggressively (e.g., MAX_PAGES = 3-5) since most active PRs are recent. Also consider caching or using GitHub's search API with SHA filter if available.

Confidence: 5/5

Suggested Fix

Consider reducing MAX_PAGES to a more reasonable value like 3-5 pages (300-500 PRs), which should cover most active repositories while preventing excessive API calls:

Suggested change
for (let page = 1; page <= MAX_PAGES; page++) {
const { data: prs } = await context.octokit.rest.pulls.list({
owner,
repo,
state: "open",
per_page: PULLS_PER_PAGE,
page,
});
if (prs.length === 0) {
break;
}
const forkMatch = prs.find((pr) => pr.head.sha === headSha);
if (forkMatch) {
return forkMatch.number;
}
if (prs.length < PULLS_PER_PAGE) {
break;
}
}
const MAX_PAGES = 5;

Additionally, consider adding a comment explaining the trade-off:

// Limit pagination to avoid excessive API calls and latency.
// This covers ~500 most recent open PRs, which should be sufficient
// for most repositories. Very old fork PRs may not be found.
const MAX_PAGES = 5;
Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/events/workflow_run.completed/checklint.ts around line 14, the MAX_PAGES constant is set to 10, which could cause the fork PR lookup to fetch up to 1000 PRs sequentially, leading to significant latency (2-5 seconds) and potential rate limit issues. Reduce MAX_PAGES to a more reasonable value like 5 (covering ~500 PRs) to balance between finding fork PRs and avoiding excessive API calls. Add a comment explaining this trade-off and the limitation that very old fork PRs may not be found.

📍 This suggestion applies to lines 130-151

@beetle-ai

beetle-ai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary by Beetle

This PR simplifies the fork pull request lookup logic by removing unnecessary pagination complexity. The change replaces a multi-page iteration approach (up to 10 pages, 100 PRs per page) with a single unpaginated API call that fetches up to 30 open PRs. This optimization reduces code complexity and API calls while maintaining functionality for typical use cases where fork PRs are among the most recent open PRs.

📁 File Changes Summary (Consolidated across all commits):

File Status Changes Description
src/events/workflow_run.completed/checklint.ts Modified +9/-26 Simplified fork PR lookup by removing pagination logic (removed PULLS_PER_PAGE and MAX_PAGES constants, eliminated for-loop iteration through pages). Replaced with a single API call fetching 30 open PRs and direct SHA matching. Improved error handling with cleaner return logic using optional chaining.

Total Changes: 1 file changed, +9 additions, -26 deletions

🎯 Key Changes:

  • Removed pagination constants: Eliminated PULLS_PER_PAGE (100) and MAX_PAGES (10) constants that were previously used for iterating through multiple pages of PRs
  • Simplified API call: Replaced paginated loop with a single pulls.list() call that fetches up to 30 open PRs
  • Streamlined matching logic: Removed nested loop structure and early-break conditions, replacing with a single find() operation and optional chaining (forkMatch?.number ?? null)
  • Reduced code complexity: Decreased function length by 17 lines while maintaining the same core functionality
  • Maintained same-repo PR priority: The logic still checks same-repository PRs first before falling back to fork PR lookup

📊 Impact Assessment:

  • Security: No security impact. The change maintains the same authentication and authorization model. The reduced API call surface area slightly decreases potential attack vectors.
  • Performance: Positive impact. Reduces API calls from potentially 10 requests (1000 PRs) to a single request (30 PRs). For repositories with fewer than 30 open PRs (the typical case), this eliminates unnecessary network overhead. Trade-off: Won't find fork PRs beyond the 30 most recent open PRs, but this is acceptable given that workflow runs typically correspond to recent PRs.
  • Maintainability: Significantly improved. The code is now more readable and easier to understand with 26% fewer lines. Removed complex pagination logic reduces cognitive load and potential for off-by-one errors. The simplified control flow makes debugging easier.
  • Testing: Consideration needed. The change assumes fork PRs will be within the 30 most recent open PRs. Edge case testing should verify behavior in repositories with >30 open PRs where the target fork PR might be older. Existing tests for SHA matching and same-repo PR priority should still pass. Consider adding a test case for the 30-PR limit scenario.
⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only.Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

@beetle-ai

beetle-ai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

✅ You're good to merge this PR! No issues found. Great job!

Settings
⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only.Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

@calebephrem calebephrem merged commit c7c0213 into open-devhub:main Jun 28, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant