Skip to content

fix: handle status events to trigger can-be-merged re-evaluation (#1034)#1036

Merged
myakove merged 5 commits intomainfrom
fix/issue-1034-status-event-handler
Mar 17, 2026
Merged

fix: handle status events to trigger can-be-merged re-evaluation (#1034)#1036
myakove merged 5 commits intomainfrom
fix/issue-1034-status-event-handler

Conversation

@myakove
Copy link
Copy Markdown
Collaborator

@myakove myakove commented Mar 17, 2026

Summary

  • Add status event handler in github_api.py so external services using GitHub's Status API (e.g., pre-commit.ci) trigger can-be-merged re-evaluation when their checks succeed
  • Add SHA-based PR lookup for status events in get_pull_request()
  • Skip processing for non-success states (pending, failure, error)

Problem

When pre-commit.ci completes, it reports via GitHub's Status API (not Check Runs API). The webhook server only handled check_run events, so can-be-merged was never re-evaluated after pre-commit.ci finished. This caused permanent "Some check runs not started: pre-commit.ci - pr" failures.

Changes

File Change
webhook_server/libs/github_api.py Add status event handler in process(), skip clone for status events until needed, SHA-based PR lookup fallback
webhook_server/tests/test_github_api.py 4 tests: success triggers re-eval, pending/failure skipped, SHA PR lookup

Test plan

  • All tests pass
  • Deploy and verify: push a commit to a PR with pre-commit.ci, confirm can-be-merged re-evaluates after pre-commit.ci succeeds

Closes #1034

Summary by CodeRabbit

  • New Features

    • Status events now associate with pull requests (by matching commit SHAs) and trigger can-be-merged re-evaluation for terminal states; pending statuses are skipped.
    • Check-run and status handling now defer repository cloning/fetching until necessary, aligning status flow with check-run behavior.
    • Improved logging and metrics around status processing and PR association.
  • Tests

    • Added tests covering status event handling (success, pending, failure, error), PR lookup, and conditional recheck logic.

External services like pre-commit.ci report via GitHub's Status API.
When a status check succeeds, re-evaluate can-be-merged for the PR.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 17, 2026

Warning

Rate limit exceeded

@myakove has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 466e67c7-e26b-41f7-ab5f-5368a792e27e

📥 Commits

Reviewing files that changed from the base of the PR and between fac346d and 2ad2ad9.

📒 Files selected for processing (1)
  • webhook_server/tests/test_github_api.py

Walkthrough

Implements handling for GitHub status events: matches status.commit.sha to open PR head SHAs, ignores pending states, defers repository cloning until required, and triggers the PullRequestHandler can-be-merged re-evaluation for terminal status states with logging and metrics.

Changes

Cohort / File(s) Summary
Status Event Handler
webhook_server/libs/github_api.py
Adds status branch in process() that skips pending, finds open PR(s) by comparing status.commit.sha to PR head SHAs (via a helper), defers cloning for check_run/status, and invokes OwnersFileHandler + PullRequestHandler to re-run can-be-merged with logging/metrics.
Status Event Tests
webhook_server/tests/test_github_api.py
Adds async tests for status events covering states (success,pending,failure,error), validates PR lookup by status SHA (including fallback to commit.get_pulls()), cloning behavior, owners/metrics/logging interactions, and conditional recheck invocation.

Sequence Diagram(s)

sequenceDiagram
    participant GitHub as GitHub Webhook
    participant Processor as github_api.process()
    participant PRLookup as get_pull_request()
    participant Repo as Repository Clone
    participant Handler as PullRequestHandler
    participant Metrics as Metrics Logger

    GitHub->>Processor: status event (commit.sha, state)
    alt state == "pending"
        Processor->>Metrics: log skip / increment metric
        Processor-->>GitHub: exit
    else state in ("success","failure","error")
        Processor->>PRLookup: find open PR(s) matching commit.sha
        PRLookup-->>Processor: PR found / not found
        alt PR found
            Processor->>Repo: clone repo (deferred until needed)
            Processor->>Handler: init OwnersFileHandler & run can-be-merged evaluation
            Handler-->>Metrics: log evaluation results
            Metrics-->>Processor: update context
        else no PR found
            Processor->>Metrics: log missing PR, exit
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

size/M, can-be-merged

Suggested reviewers

  • rnetser
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: handling status events to trigger can-be-merged re-evaluation, matching the core objective of issue #1034.
Linked Issues check ✅ Passed The PR fulfills all coding requirements from #1034: status event handler added, SHA-based PR lookup implemented, can-be-merged re-evaluation triggered for success states, non-success states skipped, and comprehensive tests added.
Out of Scope Changes check ✅ Passed All changes directly align with #1034 scope: status event routing, PR association logic, and test coverage for the new feature with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-1034-status-event-handler
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@myakove-bot
Copy link
Copy Markdown
Collaborator

Report bugs in Issues

Welcome! 🎉

This pull request will be automatically processed with the following features:

🔄 Automatic Actions

  • Reviewer Assignment: Reviewers are automatically assigned based on the OWNERS file in the repository root
  • Size Labeling: PR size labels (XS, S, M, L, XL, XXL) are automatically applied based on changes
  • Issue Creation: Disabled for this repository
  • Pre-commit Checks: pre-commit runs automatically if .pre-commit-config.yaml exists
  • Branch Labeling: Branch-specific labels are applied to track the target branch
  • Auto-verification: Auto-verified users have their PRs automatically marked as verified
  • Labels: All label categories are enabled (default configuration)

📋 Available Commands

PR Status Management

  • /wip - Mark PR as work in progress (adds WIP: prefix to title)
  • /wip cancel - Remove work in progress status
  • /hold - Block PR merging (approvers only)
  • /hold cancel - Unblock PR merging
  • /verified - Mark PR as verified
  • /verified cancel - Remove verification status
  • /reprocess - Trigger complete PR workflow reprocessing (useful if webhook failed or configuration changed)
  • /regenerate-welcome - Regenerate this welcome message

Review & Approval

  • /lgtm - Approve changes (looks good to me)
  • /approve - Approve PR (approvers only)
  • /automerge - Enable automatic merging when all requirements are met (maintainers and approvers only)
  • /assign-reviewers - Assign reviewers based on OWNERS file
  • /assign-reviewer @username - Assign specific reviewer
  • /check-can-merge - Check if PR meets merge requirements

Testing & Validation

  • /retest tox - Run Python test suite with tox
  • /retest build-container - Rebuild and test container image
  • /retest python-module-install - Test Python package installation
  • /retest pre-commit - Run pre-commit hooks and checks
  • /retest conventional-title - Validate commit message format
  • /retest all - Run all available tests

Container Operations

  • /build-and-push-container - Build and push container image (tagged with PR number)
    • Supports additional build arguments: /build-and-push-container --build-arg KEY=value

Cherry-pick Operations

  • /cherry-pick <branch> - Schedule cherry-pick to target branch when PR is merged
    • Multiple branches: /cherry-pick branch1 branch2 branch3

Label Management

  • /<label-name> - Add a label to the PR
  • /<label-name> cancel - Remove a label from the PR

✅ Merge Requirements

This PR will be automatically approved when the following conditions are met:

  1. Approval: /approve from at least one approver
  2. LGTM Count: Minimum 1 /lgtm from reviewers
  3. Status Checks: All required status checks must pass
  4. No Blockers: No wip, hold, has-conflicts labels and PR must be mergeable (no conflicts)
  5. Verified: PR must be marked as verified

📊 Review Process

Approvers and Reviewers

Approvers:

  • myakove
  • rnetser

Reviewers:

  • myakove
  • rnetser
Available Labels
  • hold
  • verified
  • wip
  • lgtm
  • approve
  • automerge

💡 Tips

  • WIP Status: Use /wip when your PR is not ready for review
  • Verification: The verified label is automatically removed on each new commit
  • Cherry-picking: Cherry-pick labels are processed when the PR is merged
  • Container Builds: Container images are automatically tagged with the PR number
  • Permission Levels: Some commands require approver permissions
  • Auto-verified Users: Certain users have automatic verification and merge privileges

For more information, please refer to the project documentation or contact the maintainers.

@qodo-code-review
Copy link
Copy Markdown

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Review Summary by Qodo

Handle status events to trigger can-be-merged re-evaluation

🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Handle GitHub Status API events to trigger can-be-merged re-evaluation
• Skip processing for non-success status states (pending, failure, error)
• Add SHA-based PR lookup fallback for status events
• Add comprehensive test coverage for status event handling
Diagram
flowchart LR
  A["Status Event Webhook"] -->|state=success| B["Clone Repository"]
  A -->|state=pending/failure| C["Skip Processing"]
  B --> D["Initialize OwnersFileHandler"]
  D --> E["Re-evaluate can-be-merged"]
  E --> F["Log Completion"]
  C --> F
  G["Status Event"] -->|SHA lookup| H["Find PR by head.sha"]
  H --> I["Return PR"]
Loading

Grey Divider

File Changes

1. webhook_server/libs/github_api.py ✨ Enhancement +49/-2

Add status event handler and SHA-based PR lookup

• Modified repository cloning logic to defer cloning for both check_run and status events until
 needed
• Added status event handler in process() method that only processes success states
• Implemented SHA-based PR lookup in get_pull_request() for status events to find matching open
 PRs
• Added logging for status context name and state transitions

webhook_server/libs/github_api.py


2. webhook_server/tests/test_github_api.py 🧪 Tests +219/-0

Add comprehensive status event handling tests

• Added test_process_status_event_success() to verify success state triggers can-be-merged
 re-evaluation
• Added test_process_status_event_pending_skipped() to verify pending state is skipped
• Added test_process_status_event_failure_skipped() to verify failure state is skipped
• Added test_get_pull_request_from_status_sha() to verify SHA-based PR lookup for status events

webhook_server/tests/test_github_api.py


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Mar 17, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. get_pulls iteration not threaded 📘 Rule violation ➹ Performance
Description
The new status-SHA PR lookup iterates a PyGithub PaginatedList and reads PR properties
(_pull_request.head.sha) outside asyncio.to_thread(), which can block the event loop during
webhook handling. This risks degrading FastAPI responsiveness under load.
Code

webhook_server/libs/github_api.py[R922-924]

+                for _pull_request in await asyncio.to_thread(self.repository.get_pulls, state="open"):
+                    if _pull_request.head.sha == sha:
+                        self.logger.debug(
Evidence
Compliance requires *all* PyGithub operations, including PaginatedList iteration and
potentially-blocking property access, to run inside asyncio.to_thread(). The added status handler
wraps only the get_pulls call, but performs iteration and _pull_request.head.sha access on the
event loop thread.

CLAUDE.md
webhook_server/libs/github_api.py[921-924]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The status event PR lookup calls `await asyncio.to_thread(self.repository.get_pulls, ...)` but then iterates the returned PyGithub `PaginatedList` and accesses `_pull_request.head.sha` outside `to_thread`, which can still perform blocking API requests on the event loop.

## Issue Context
Compliance requires *all* PyGithub operations (including `PaginatedList` iteration and property access that may trigger API calls) to be wrapped in `asyncio.to_thread()`.

## Fix Focus Areas
- webhook_server/libs/github_api.py[921-930]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Status failures not rechecked 🐞 Bug ✓ Correctness
Description
GithubWebhook.process() returns early for status events unless state == "success", so
can-be-merged is never re-evaluated when a Status API check ends in failure/error and the
merge eligibility signal can become stale.
Code

webhook_server/libs/github_api.py[R609-619]

+                # Only re-evaluate can-be-merged when a status check succeeds
+                state = self.hook_data.get("state", "")
+                if state != "success":
+                    token_metrics = await self._get_token_metrics()
+                    self.logger.info(
+                        f"{self.log_prefix} "
+                        f"Webhook processing completed successfully: status (state={state}, skipped) - "
+                        f"{token_metrics}",
+                    )
+                    await self._update_context_metrics()
+                    return None
Evidence
The new status handler explicitly skips all non-success states, but check_if_can_be_merged()
reads commit statuses to compute merge eligibility; therefore skipping failure/error status
transitions prevents updating can-be-merged to reflect those new status results.

webhook_server/libs/github_api.py[608-633]
webhook_server/libs/handlers/pull_request_handler.py[1192-1207]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`status` webhooks only trigger can-be-merged re-evaluation when `state == &quot;success&quot;`. When a status check transitions to `failure`/`error`, the webhook returns early and never re-evaluates `can-be-merged`, so the merge eligibility signal can become stale.

### Issue Context
`PullRequestHandler.check_if_can_be_merged()` evaluates commit statuses (`last_commit.get_statuses()`). If status failures/errors do not trigger re-evaluation, can-be-merged won&#x27;t reflect the new failing state.

### Fix Focus Areas
- webhook_server/libs/github_api.py[608-633]
- webhook_server/libs/handlers/pull_request_handler.py[1192-1207]

### Expected change
- Re-evaluate on terminal states (e.g., `success`, `failure`, `error`) and skip only non-terminal ones (e.g., `pending`).
- Update/extend tests accordingly (pending skipped; failure/error should now re-evaluate).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Status fields treated optional 📘 Rule violation ✓ Correctness
Description
The new status-event handler uses hook_data.get(..., "") defaults for required webhook fields like
state and sha, causing malformed/partial payloads to be silently skipped instead of failing
fast. This deviates from the GitHub webhook spec expectations and can hide integration issues.
Code

webhook_server/libs/github_api.py[R610-620]

+                state = self.hook_data.get("state", "")
+                if state != "success":
+                    token_metrics = await self._get_token_metrics()
+                    self.logger.info(
+                        f"{self.log_prefix} "
+                        f"Webhook processing completed successfully: status (state={state}, skipped) - "
+                        f"{token_metrics}",
+                    )
+                    await self._update_context_metrics()
+                    return None
+
Evidence
Compliance requires webhook payload handling to follow GitHub’s defined fields and to avoid
unnecessary defensive programming for required data. The added code paths default missing required
fields (state, sha) to empty strings and then exit early, which changes behavior from fail-fast
to silent drop.

CLAUDE.md
CLAUDE.md
webhook_server/libs/github_api.py[609-612]
webhook_server/libs/github_api.py[918-921]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The status webhook handler treats required fields (`state`, `sha`) as optional by using `.get(..., &quot;&quot;)` and then silently skipping when values are missing/non-matching, rather than failing fast.

## Issue Context
Per compliance, webhook payload parsing should adhere to GitHub’s spec and avoid defensive guards for required/stable fields; malformed payloads should surface as errors (or be explicitly rejected with a clear exception).

## Fix Focus Areas
- webhook_server/libs/github_api.py[608-621]
- webhook_server/libs/github_api.py[918-931]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Skipped status still expensive 🐞 Bug ➹ Performance
Description
Even when a status event is skipped (non-success), the code still performs PR resolution and
last-commit fetching first, including an O(open PRs) SHA scan in get_pull_request(), increasing
API usage and latency under frequent status updates.
Code

webhook_server/libs/github_api.py[R918-930]

+        if self.github_event == "status":
+            sha = self.hook_data.get("sha", "")
+            if sha:
+                self.logger.debug(f"{self.log_prefix} Searching open PRs for status SHA: {sha}")
+                for _pull_request in await asyncio.to_thread(self.repository.get_pulls, state="open"):
+                    if _pull_request.head.sha == sha:
+                        self.logger.debug(
+                            f"{self.log_prefix} Found pull request {_pull_request.title} "
+                            f"[{_pull_request.number}] for status context "
+                            f"{self.hook_data.get('context', 'unknown')}"
+                        )
+                        return _pull_request
+                self.logger.debug(f"{self.log_prefix} No open PR found matching status SHA")
Evidence
process() always calls get_pull_request() before checking the status state, so non-success
status events still pay the cost of PR lookup. The newly added status SHA lookup can iterate all
open PRs; and after a PR is found, _get_last_commit() calls pull_request.get_commits() and
materializes the list, which is additional work that happens before the skip return.

webhook_server/libs/github_api.py[465-466]
webhook_server/libs/github_api.py[506-513]
webhook_server/libs/github_api.py[608-619]
webhook_server/libs/github_api.py[918-930]
webhook_server/libs/github_api.py[1039-1041]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Non-success `status` events are intended to be skipped, but `process()` still resolves the PR and fetches the PR’s last commit before it checks `state` and returns. This includes potentially scanning all open PRs by SHA, which is expensive under frequent status updates.

### Issue Context
`process()` currently calls `get_pull_request()` unconditionally for all non-ping/non-push events. For `status`, that call can iterate `repository.get_pulls(state=&quot;open&quot;)` to match `sha`, and after a PR is found `_get_last_commit()` calls `pull_request.get_commits()`.

### Fix Focus Areas
- webhook_server/libs/github_api.py[465-466]
- webhook_server/libs/github_api.py[506-513]
- webhook_server/libs/github_api.py[608-619]
- webhook_server/libs/github_api.py[918-930]
- webhook_server/libs/github_api.py[1039-1041]

### Expected change
- If `github_event == &quot;status&quot;` and the state is a value you intend to skip (e.g., `pending`), return before calling `get_pull_request()`.
- Alternatively, teach `get_pull_request()` to avoid the O(open PRs) scan for skipped `status` states (it can read `self.hook_data[&quot;state&quot;]`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Bad initialize mock return 🐞 Bug ⛯ Reliability
Description
The new status success test mocks OwnersFileHandler.initialize() to return None, contradicting
the real method contract (returns self) and reducing the test’s ability to catch regressions
involving owners-file initialization.
Code

webhook_server/tests/test_github_api.py[R874-878]

+                                        patch.object(
+                                            OwnersFileHandler,
+                                            "initialize",
+                                            new=AsyncMock(return_value=None),
+                                        ),
Evidence
Production OwnersFileHandler.initialize() returns an OwnersFileHandler instance, but the test
patches it to return None, so the test does not exercise/validate the contract that downstream
handlers receive a properly initialized handler.

webhook_server/libs/handlers/owners_files_handler.py[30-56]
webhook_server/tests/test_github_api.py[872-880]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`test_process_status_event_success` patches `OwnersFileHandler.initialize()` to return `None`, which contradicts the real method signature/behavior (it returns `self`). This weakens the test and may mask regressions.

### Issue Context
The production code assigns `owners_file_handler = await owners_file_handler.initialize(...)` and passes it to `PullRequestHandler(...)`. Returning `None` in the test does not reflect the actual contract.

### Fix Focus Areas
- webhook_server/tests/test_github_api.py[819-883]
- webhook_server/libs/handlers/owners_files_handler.py[30-56]

### Expected change
- Patch `OwnersFileHandler.initialize` to return an `OwnersFileHandler` instance (e.g., via a side_effect that returns `self`) rather than `None`.
- Add `assert_awaited_once()` on the initialize mock (and optionally `_clone_repository`) to ensure the status success flow exercises those steps.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@webhook_server/tests/test_github_api.py`:
- Around line 998-1001: The test function test_get_pull_request_from_status_sha
declares unused fixture parameters minimal_hook_data and minimal_headers; remove
these parameters from the test signature so pytest won't instantiate those
fixtures unnecessarily, leaving only the fixtures actually used (e.g., logger)
and update any references inside the test if they mistakenly referenced the
removed names; ensure the pytest.mark.asyncio decorator and async signature
remain intact for the test coroutine.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6f6e551b-a923-4f47-8459-67fba64015fa

📥 Commits

Reviewing files that changed from the base of the PR and between 23d03c9 and 383e41c.

📒 Files selected for processing (2)
  • webhook_server/libs/github_api.py
  • webhook_server/tests/test_github_api.py

Comment thread webhook_server/tests/test_github_api.py Outdated
Comment thread webhook_server/libs/github_api.py Outdated
Comment thread webhook_server/libs/github_api.py Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 17, 2026
- Wrap PaginatedList iteration and .head.sha access in asyncio.to_thread
  for both check_run and status SHA lookups
- Re-evaluate can-be-merged on failure/error states (terminal), skip
  only pending (non-terminal)
- Remove unused test fixture parameters
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@webhook_server/libs/github_api.py`:
- Around line 610-611: The status-handling flow currently uses defensive .get()
on the webhook payload (e.g., state = self.hook_data.get("state", "")) which
hides malformed payloads; change these to direct accesses (e.g.,
self.hook_data["state"]) for required fields in the status flow so the code
fails fast (allow the KeyError to propagate or convert it to an explicit
exception with context), and apply the same change to the other .get() usages
against self.hook_data in that flow so missing GitHub fields are surfaced
immediately.
- Around line 627-633: The status re-evaluation path currently does heavyweight
work by awaiting self._clone_repository and initializing OwnersFileHandler
before calling PullRequestHandler.check_if_can_be_merged; remove these steps for
the status-only flow and invoke check_if_can_be_merged without performing a
local checkout or full OWNERS initialization. Concretely, stop calling
_clone_repository and OwnersFileHandler.initialize in this path, instead pass a
None or lightweight placeholder for owners_file_handler (or have
PullRequestHandler lazily load OWNERS only when needed) so
check_if_can_be_merged (method check_if_can_be_merged on PullRequestHandler)
runs using only PR/commit API state and cached/graphql queries.
- Around line 912-915: The loop that awaits asyncio.to_thread(_get_pr_head_sha,
...) for each PullRequest (open_pulls from self.repository.get_pulls) should be
batched with asyncio.gather to avoid sequential blocking: call asyncio.to_thread
once to get list(open_pulls), then create a comprehension of
asyncio.to_thread(_get_pr_head_sha, pr) for each pr and await asyncio.gather to
get head_shas, then iterate zip(open_pulls, head_shas) and compare pr_head_sha
to head_sha; keep direct access to cached properties like _pull_request.title
and _pull_request.number without wrapping them in to_thread.

In `@webhook_server/tests/test_github_api.py`:
- Around line 819-1004: Add a parallel test to cover the terminal "error" state:
create a new async test (e.g.,
test_process_status_event_error_triggers_reevaluation) that mirrors
test_process_status_event_failure_triggers_reevaluation but sets
status_data["state"]="error", constructs the same mocks (GithubWebhook,
get_api_with_highest_rate_limit, get_github_repo_api, PullRequestHandler, etc.),
patches webhook._clone_repository and OwnersFileHandler.initialize as in the
failure test, calls await webhook.process(), and asserts
PullRequestHandler.return_value.check_if_can_be_merged.assert_awaited_once() to
ensure parity for terminal-state handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d372d77a-5e67-49fa-89fc-fe28f8b64e32

📥 Commits

Reviewing files that changed from the base of the PR and between 383e41c and 8c344bf.

📒 Files selected for processing (2)
  • webhook_server/libs/github_api.py
  • webhook_server/tests/test_github_api.py

Comment thread webhook_server/libs/github_api.py Outdated
Comment thread webhook_server/libs/github_api.py Outdated
Comment thread webhook_server/libs/github_api.py
Comment thread webhook_server/tests/test_github_api.py Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 17, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@webhook_server/libs/github_api.py`:
- Around line 921-934: For the status webhook handling, reorder the logic in the
method that checks self.github_event so the head-SHA filter runs before using
commit_obj.get_pulls(): when self.github_event == "status", first compute sha =
self.hook_data["sha"], fetch open_pulls via self.repository.get_pulls and
resolve each PR head SHA using _get_pr_head_sha (as in the existing head_shas /
zip loop) and return the matching PR if pr_head_sha == sha; only if no matching
open PR is found then fall back to calling commit_obj.get_pulls() (or whatever
existing commit-based lookup is currently used). This ensures the head-SHA match
logic (variables: sha, open_pulls, head_shas, _get_pr_head_sha) executes before
the commit_obj.get_pulls() fallback for status events.

In `@webhook_server/tests/test_github_api.py`:
- Around line 819-1045: Consolidate the four near-duplicate tests
(test_process_status_event_success, test_process_status_event_pending_skipped,
test_process_status_event_failure_triggers_reevaluation,
test_process_status_event_error_triggers_reevaluation) into a single
parameterized test using pytest.mark.parametrize that iterates over (state,
expected_call) pairs (e.g. ("success", True), ("pending", False), ("failure",
True), ("error", True)); move the common setup (logger, status_data template,
headers, tempfile patching, and all patched calls like Config,
get_api_with_highest_rate_limit, get_github_repo_api,
get_repository_github_app_api, get_apis_and_tokes_from_config, and
PullRequestHandler) into the test body or a helper function/fixture and only set
status_data["state"] from the param before instantiating GithubWebhook and
awaiting process(); finally assert
mock_pr_handler.return_value.check_if_can_be_merged.assert_awaited_once() when
expected_call is True and .assert_not_awaited() when False so the matrix drives
the behavior instead of copying scaffolding.
- Around line 1047-1085: The test test_get_pull_request_from_status_sha omits a
commit object so the code path in GithubWebhook.get_pull_request that falls back
to status- SHA lookup isn't exercised; add a "commit" key to status_data whose
mock commit.get_pulls() returns a PR whose head.sha is different (stale) to
simulate a mismatched commit PR, and ensure the patched
get_repository_github_app_api/get_github_repo_api behavior remains so that
mock_repo.get_pulls(state="open") returns the correct PR with head.sha
"status-sha-123"; this forces get_pull_request() to ignore the commit PR and
proceed to the status- SHA lookup and still assert result == mock_pr and
mock_repo.get_pulls.assert_called_once_with(state="open").
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c452f2b8-692d-4dc4-8905-5599afdd4c23

📥 Commits

Reviewing files that changed from the base of the PR and between 8c344bf and 8f42792.

📒 Files selected for processing (2)
  • webhook_server/libs/github_api.py
  • webhook_server/tests/test_github_api.py

Comment thread webhook_server/libs/github_api.py
Comment thread webhook_server/tests/test_github_api.py Outdated
Comment thread webhook_server/tests/test_github_api.py
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 17, 2026
- Move check_run/status SHA lookups before commit.get_pulls() fallback
- Parameterize 4 status state tests into single test
- Add commit object with stale PR to status SHA test
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@webhook_server/libs/github_api.py`:
- Around line 609-630: The code currently skips only "pending" but still re-runs
PullRequestHandler.check_if_can_be_merged for failure/error; change the
conditional to only proceed when self.hook_data["state"] == "success" and return
early for any other state (including "pending", "failure", "error"), logging the
skipped state and calling token metrics/_update_context_metrics as currently
done; locate the block around self.hook_data["state"], update the if-check to
require "success" before instantiating OwnersFileHandler and calling
PullRequestHandler(...).check_if_can_be_merged(pull_request=pull_request).

In `@webhook_server/tests/test_github_api.py`:
- Around line 919-925: Add an explicit assertion that the commit fallback was
not used by asserting mock_repo.get_commit was never called in the test where
the status SHA already matches an open PR; after setting up stale_pr,
mock_commit.get_pulls, and mock_repo.get_commit, add a check using
mock_repo.get_commit.assert_not_called() (and similarly in the other test case
mentioned) to ensure get_commit() is not invoked when an open PR's head.sha
matches the status.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8a9abd67-ac4e-4a57-8347-7c58a210532f

📥 Commits

Reviewing files that changed from the base of the PR and between 8f42792 and fac346d.

📒 Files selected for processing (2)
  • webhook_server/libs/github_api.py
  • webhook_server/tests/test_github_api.py

Comment thread webhook_server/libs/github_api.py
Comment thread webhook_server/tests/test_github_api.py
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 17, 2026
@myakove myakove merged commit ffb4c8a into main Mar 17, 2026
7 of 9 checks passed
@myakove myakove deleted the fix/issue-1034-status-event-handler branch March 17, 2026 14:55
@myakove-bot
Copy link
Copy Markdown
Collaborator

New container for ghcr.io/myk-org/github-webhook-server:latest published

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: status events don't trigger can-be-merged re-evaluation

2 participants