Skip to content

feat: detect clean rebase on PR synchronize and preserve review labels#1060

Merged
myakove merged 8 commits intomainfrom
feat/issue-1059-clean-rebase-detection
Mar 31, 2026
Merged

feat: detect clean rebase on PR synchronize and preserve review labels#1060
myakove merged 8 commits intomainfrom
feat/issue-1059-clean-rebase-detection

Conversation

@myakove
Copy link
Copy Markdown
Collaborator

@myakove myakove commented Mar 31, 2026

Summary

  • Detect clean rebases on PR synchronize events by comparing SHA-256 hashes of git diff output before/after the push
  • Preserve review labels (approved-by-*, lgtm-by-*, verified, commented-by-*, changes-requested-by-*) on clean rebase instead of stripping them
  • Post a PR comment listing which labels were preserved when a clean rebase is detected

Implementation Details

  • _is_clean_rebase() method computes merge-base diffs for old and new heads, hashes them with SHA-256, and compares
  • Conservative fallback: returns False (strip labels as usual) on any git command failure
  • Uses shlex.quote() on all interpolated git command values for safety
  • Skips detection early if repository clone is not available
  • Skips _process_verified_for_update_or_new_pull_request() on clean rebase to truly preserve the verified label
  • Clean rebase path runs comment posting and CI setup in parallel via asyncio.gather

Test plan

  • 21 new tests in test_clean_rebase_detection.py covering:
    • Clean rebase detected (matching diffs) → labels preserved
    • Non-clean rebase (different diffs) → labels removed as usual
    • All 5 git command failure paths → conservative fallback
    • shlex.quote() usage verification
    • Repo-not-cloned early exit
    • Verified label preservation (not removed by downstream processing)
    • Comment content with/without review labels
    • Parallel task failure handling
  • 133 existing PR handler tests pass with no regressions

Closes #1059

Summary by CodeRabbit

  • New Features

    • Detect clean rebases on pull request synchronizations: skip full re-verification, refresh the verified check state without toggling the verified label, preserve selected review/verified labels, and avoid removing labels on clean-rebase events.
    • Post a best-effort notification on clean-rebase events summarizing preserved labels and the prior commit; posting failures are logged without blocking processing.
  • Tests

    • Added and updated tests covering clean-rebase detection, sync behavior, label preservation, verified-check handling, comment posting, and error/timeout scenarios.

When a PR is synchronized, detect if it's a clean rebase (same code
changes replayed on a newer base) by comparing SHA-256 hashes of
git diff output. On clean rebase, preserve review labels (approved-by,
lgtm-by, verified, commented-by, changes-requested-by) and post a
comment listing preserved labels.

Closes #1059
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

Detects clean rebases on PR synchronize by hashing git diffs before/after the push; when detected, preserves review/verified labels, posts a best-effort comment listing preserved labels, and continues normal synchronize processing while skipping label removal and adjusting verified-check handling.

Changes

Cohort / File(s) Summary
Pull request handler
webhook_server/libs/handlers/pull_request_handler.py
Added _is_clean_rebase() (runs git fetch, merge-base, git diff --binary with -C <clone_dir> and timeout=60, hashes diffs via SHA-256); added _post_clean_rebase_comment() and _sync_verified_check_for_clean_rebase(); updated process_pull_request_webhook_data() to branch on clean rebase and extended process_opened_or_synchronize_pull_request signature to accept is_clean_rebase and label_names.
New tests: clean rebase
webhook_server/tests/test_clean_rebase_detection.py
New pytest module covering _is_clean_rebase success/failure paths, git command construction/quoting/timeouts, diff hashing behavior, preserved-label selection/format, comment posting error handling, verified-check sync for clean rebase, and overall synchronize-flow interactions.
Updated tests: synchronize cases
webhook_server/tests/test_pull_request_handler.py
Synchronize tests updated to provide before/after SHAs, mock _is_clean_rebase() (returning False in tests), and expect process_opened_or_synchronize_pull_request(..., is_clean_rebase=...) plus conditional label-removal behavior.

Sequence Diagram

sequenceDiagram
    participant GitHub as GitHub Webhook
    participant Handler as PullRequestHandler
    participant Git as Git Operations
    participant Hasher as SHA256 Hasher
    participant Labels as Label Manager
    participant Comment as Comment Poster

    GitHub->>Handler: synchronize event (before, after)
    Handler->>Git: git -C <clone_dir> fetch origin (timeout=60)
    Handler->>Git: git -C <clone_dir> merge-base origin/<base_ref> <before> (timeout=60)
    Handler->>Git: git -C <clone_dir> merge-base origin/<base_ref> <after> (timeout=60)
    par Compute before/after hashes
        Handler->>Git: git -C <clone_dir> diff --binary <merge-base>..<before> (timeout=60)
        Git-->>Hasher: diff bytes (before)
        Hasher->>Hasher: SHA-256(before)
    and
        Handler->>Git: git -C <clone_dir> diff --binary <merge-base>..<after> (timeout=60)
        Git-->>Hasher: diff bytes (after)
        Hasher->>Hasher: SHA-256(after)
    end
    Hasher-->>Handler: hashes equal?
    alt Clean rebase
        Handler->>Comment: _post_clean_rebase_comment(preserved_labels)
        Handler->>Handler: process_opened_or_synchronize_pull_request(..., is_clean_rebase=True, label_names=...)
        Handler->>Labels: _sync_verified_check_for_clean_rebase(label_names)
    else Not clean
        Handler->>Labels: remove_labels_when_pull_request_sync()
        Handler->>Handler: process_opened_or_synchronize_pull_request(..., is_clean_rebase=False)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

commented-myakove, commented-coderabbitai[bot]

Suggested reviewers

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

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.49% 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 PR title accurately describes the main change: detecting clean rebases on PR synchronize and preserving review labels, which is the primary objective of the changeset.
Linked Issues check ✅ Passed All coding requirements from issue #1059 are implemented: clean rebase detection using git diff + SHA-256 hashing, label preservation logic, comment posting on detection, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes directly address the linked issue objectives. The modifications to webhook handler, new test coverage, and the commit message fix regarding API calls are all within scope of clean rebase detection and label preservation.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-1059-clean-rebase-detection

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
AI Features
  • Conventional Title: Mode: fix (claude/claude-opus-4-6[1m])
  • Cherry-Pick Conflict Resolution: Enabled (claude/claude-opus-4-6[1m])
  • Test Oracle: Triggers: approved (claude/claude-opus-4-6[1m]); /test-oracle can be used anytime

💡 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.

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/handlers/pull_request_handler.py`:
- Around line 227-255: The current clean-rebase flow eagerly reads
pull_request.labels and builds a comment before scheduling sync tasks, so a
failure fetching labels prevents process_opened_or_synchronize_pull_request from
running; make the comment-posting best-effort by moving label reading and
create_issue_comment into a separate helper (e.g., _post_clean_rebase_comment)
that wraps the label fetch and comment creation in try/except (re-raising
asyncio.CancelledError) and logs exceptions, and ensure sync_tasks always
includes self.process_opened_or_synchronize_pull_request(pull_request=...,
is_clean_rebase=True) regardless of comment fetch/post success so CI/setup
continues even if posting the clean-rebase comment fails.
- Around line 92-168: The _is_clean_rebase logic must "fail closed": wrap the
parts that can raise (accessing pull_request.base.ref and each await
run_command(...) call inside the _is_clean_rebase function) in try/except that
catches Exception, logs a warning including the exception info, and returns
False; ensure you re-raise asyncio.CancelledError immediately if caught.
Specifically, guard the access to pull_request.base.ref, and each call to
run_command, so any unexpected exception results in an immediate return False
(with a warning) rather than propagating, while preserving the special-case
re-raise for asyncio.CancelledError.

In `@webhook_server/tests/test_clean_rebase_detection.py`:
- Around line 371-399: The test currently only asserts different hashes for two
strings and doesn't verify that _is_clean_rebase actually uses hashlib.sha256;
update the test_hashes_diff_output_with_sha256 to patch
webhook_server.libs.handlers.pull_request_handler.hashlib.sha256 with
wraps=hashlib.sha256 (using patch(..., wraps=hashlib.sha256) as mock_sha256)
before calling await handler._is_clean_rebase(mock_pull_request), then assert
result is False and that mock_sha256.call_count == 2 to prove both diffs were
hashed by that function.
🪄 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: 84acb62c-be97-4142-b291-2cf238a952b8

📥 Commits

Reviewing files that changed from the base of the PR and between 09d213d and 9f5d08b.

📒 Files selected for processing (3)
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_clean_rebase_detection.py
  • webhook_server/tests/test_pull_request_handler.py

Comment thread webhook_server/libs/handlers/pull_request_handler.py Outdated
Comment thread webhook_server/libs/handlers/pull_request_handler.py Outdated
Comment thread webhook_server/tests/test_clean_rebase_detection.py
Binary files produce generic "Binary files differ" text in git diff,
which would hash identically for different binary changes, causing
false positive clean rebase detection. The --binary flag outputs full
binary content (base85-encoded) ensuring different changes produce
different hashes.
- Wrap _is_clean_rebase() in try/except to return False on any
  unexpected exception (re-raises CancelledError)
- Add timeout=60 to all run_command calls to prevent hangs
- Extract comment posting into _post_clean_rebase_comment() helper
  with its own try/except so CI/setup always runs even if comment fails
- Fix test to verify hashlib.sha256 is actually called
@myakove
Copy link
Copy Markdown
Collaborator Author

myakove commented Mar 31, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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/handlers/pull_request_handler.py`:
- Around line 189-212: The handler is directly reading labels from
pull_request.labels and posting comments via pull_request.create_issue_comment;
instead, replace the direct label access with a call to
self.labels_handler.pull_request_labels_names(...) to get review_labels, and
send the comment through the unified API on self.github_webhook.unified_api (do
not call pull_request.create_issue_comment). Keep the same comment_body
construction logic but obtain labels and perform the write using the repo's
unified API wrapper to preserve centralized retry/auth/logging.
- Around line 128-144: The code currently assumes run_command success implies a
valid merge-base; instead, after calling run_command for both old and new heads
(checking old_merge_base_out and new_merge_base_out), verify that
old_merge_base_out.strip() and new_merge_base_out.strip() are non-empty and
return False (treat as non-clean) if either is empty before creating
old_merge_base_q / new_merge_base_q; update the handling around the variables
old_merge_base_out, new_merge_base_out, old_merge_base, new_merge_base and the
run_command calls so an empty stdout leads to logging a warning and returning
False rather than proceeding to build the diff range.

In `@webhook_server/tests/test_clean_rebase_detection.py`:
- Line 13: Tests currently mutate MagicMock.labels at the class level by
assigning to type(mock_pull_request).labels, leaking state between tests;
replace those assignments with a scoped patch using unittest.mock.patch.object
and unittest.mock.PropertyMock (e.g., use patch.object(type(mock_pull_request),
"labels", new_callable=PropertyMock) as pm: pm.return_value = [...] or use the
context/fixture form) so the labels attribute is set only for the duration of
the test and is automatically restored; update occurrences that reference
type(mock_pull_request).labels (the places around mock_pull_request in these
tests) to use this scoped patch pattern.
🪄 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: 1327f9bd-1cf3-449f-9900-adc214aab9d7

📥 Commits

Reviewing files that changed from the base of the PR and between 9f5d08b and 7650231.

📒 Files selected for processing (2)
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_clean_rebase_detection.py

Comment thread webhook_server/libs/handlers/pull_request_handler.py
Comment thread webhook_server/libs/handlers/pull_request_handler.py Outdated
Comment thread webhook_server/tests/test_clean_rebase_detection.py
- Guard against empty merge-base stdout before building diff ranges
- Use labels_handler.pull_request_labels_names() instead of direct
  pull_request.labels access in _post_clean_rebase_comment
- Replace class-level type() mutations with scoped AsyncMock in tests
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/handlers/pull_request_handler.py`:
- Around line 93-96: Replace the live PyGithub lookup used to set base_ref
(currently assigned via await asyncio.to_thread(lambda: pull_request.base.ref))
with the value from the webhook payload: read
self.hook_data["pull_request"]["base"]["ref"] directly when assigning base_ref;
keep the same variable name (base_ref: str) and preserve the surrounding code
that uses before_sha, after_sha and clone_dir
(self.github_webhook.clone_repo_dir); optionally add a simple fallback or
validation if the payload key is missing (e.g., default to an empty string) to
avoid KeyError.

In `@webhook_server/tests/test_clean_rebase_detection.py`:
- Around line 95-124: Patch the scheduled external integration call_test_oracle
to an AsyncMock in the shared test setup so process_pull_request_webhook_data()
doesn't schedule the real background task; inside the handler fixture where
PullRequestHandler is constructed, import AsyncMock and use
monkeypatch.setattr(...) (or unittest.mock.patch) to replace the module-level
call_test_oracle with AsyncMock(), ensuring any asyncio.create_task(...)
invocations use the stubbed AsyncMock instead of the real function; reference
process_pull_request_webhook_data and call_test_oracle to locate where to apply
the monkeypatch in the fixture.
🪄 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: 8775fef3-b0e8-456c-87f2-a11146c93e3d

📥 Commits

Reviewing files that changed from the base of the PR and between 7650231 and 455e316.

📒 Files selected for processing (2)
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_clean_rebase_detection.py

Comment thread webhook_server/libs/handlers/pull_request_handler.py
Comment thread webhook_server/tests/test_clean_rebase_detection.py
- Read base.ref from hook_data instead of live PyGithub API call
- Stub call_test_oracle in all synchronize tests to prevent background
  task leaks
- Rename unused pull_request param to _pull_request in _is_clean_rebase
@myakove
Copy link
Copy Markdown
Collaborator Author

myakove commented Mar 31, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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/handlers/pull_request_handler.py`:
- Around line 1036-1039: The verification tip text in _prepare_tips_section is
outdated because clean rebases now preserve the verified label; update the
welcome/tips message to reflect that verified is not removed on a clean rebase
instead of saying “verified is removed on every new commit.” Locate
_prepare_tips_section and revise the string(s) that mention the verified label
to clarify the new behavior (mention that verified is preserved for clean
rebases), and ensure consistency with the logic in
_sync_verified_check_for_clean_rebase and
_process_verified_for_update_or_new_pull_request.
- Around line 284-289: The code performs two separate label reads for the same
PR during a clean rebase; fetch the PR's label snapshot once and pass it into
both helpers to avoid duplicate API calls and race windows. Call
pull_request_labels_names(pull_request) in the clean_rebase branch, store the
result (e.g., label_names) and pass label_names as an extra argument into
_post_clean_rebase_comment and into the path that calls
_sync_verified_check_for_clean_rebase (e.g., add a label_names parameter to
process_opened_or_synchronize_pull_request or to the internal
_sync_verified_check_for_clean_rebase), update those functions' signatures to
accept the label_names snapshot and use it instead of calling
pull_request_labels_names() internally.
🪄 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: 985bdcff-c23e-4b42-80db-10d9c2cd19ea

📥 Commits

Reviewing files that changed from the base of the PR and between 7a320e7 and b22d2c9.

📒 Files selected for processing (2)
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_clean_rebase_detection.py

Comment thread webhook_server/libs/handlers/pull_request_handler.py
Comment thread webhook_server/libs/handlers/pull_request_handler.py
- Fetch labels once in synchronize handler and pass to both helpers
  to avoid duplicate API calls and race windows
- Update verification tip in welcome message to reflect clean rebase
  behavior
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 31, 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.

♻️ Duplicate comments (1)
webhook_server/libs/handlers/pull_request_handler.py (1)

285-295: ⚠️ Potential issue | 🟠 Major

HIGH: Don’t let label snapshotting block clean-rebase synchronize.

Line 287 reintroduces the earlier failure mode: if pull_request_labels_names() raises, process_opened_or_synchronize_pull_request(..., is_clean_rebase=True) never gets scheduled, so CI/setup stops on the path that is supposed to be best-effort. Reuse the already-loaded webhook payload labels here, or catch the lookup failure and fall back to the normal synchronize flow.

🛠️ Possible fix
             if clean_rebase:
                 before_sha: str = self.hook_data["before"]
-                label_names = await self.labels_handler.pull_request_labels_names(pull_request=pull_request)
+                label_names = [label["name"] for label in pull_request_data["labels"]]
                 sync_tasks = [
                     self._post_clean_rebase_comment(
                         pull_request=pull_request, before_sha=before_sha, label_names=label_names
                     ),
                     self.process_opened_or_synchronize_pull_request(
                         pull_request=pull_request, is_clean_rebase=True, label_names=label_names
                     ),
                 ]

Based on learnings, "For this repository, prioritize speed and minimizing API calls in reviews and suggestions: reuse webhook payload data, batch GraphQL queries, cache IDs (labels/users), and avoid N+1 patterns."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webhook_server/libs/handlers/pull_request_handler.py` around lines 285 - 295,
The current clean-rebase path calls
labels_handler.pull_request_labels_names(...) which can raise and prevent
scheduling process_opened_or_synchronize_pull_request; modify the block around
clean_rebase so you first try to reuse the webhook payload label snapshot
(self.hook_data["pull_request"]["labels"] or similar) and only call
pull_request_labels_names() as a fallback, or wrap that call in a try/except
that logs the error and still appends the
process_opened_or_synchronize_pull_request task with label_names=None (or the
payload labels), ensuring both _post_clean_rebase_comment and
process_opened_or_synchronize_pull_request are always scheduled regardless of
label lookup failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@webhook_server/libs/handlers/pull_request_handler.py`:
- Around line 285-295: The current clean-rebase path calls
labels_handler.pull_request_labels_names(...) which can raise and prevent
scheduling process_opened_or_synchronize_pull_request; modify the block around
clean_rebase so you first try to reuse the webhook payload label snapshot
(self.hook_data["pull_request"]["labels"] or similar) and only call
pull_request_labels_names() as a fallback, or wrap that call in a try/except
that logs the error and still appends the
process_opened_or_synchronize_pull_request task with label_names=None (or the
payload labels), ensuring both _post_clean_rebase_comment and
process_opened_or_synchronize_pull_request are always scheduled regardless of
label lookup failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2f9ca3e2-4574-4471-bea8-227126fafe07

📥 Commits

Reviewing files that changed from the base of the PR and between 7a320e7 and a003079.

📒 Files selected for processing (2)
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_clean_rebase_detection.py

Use pull_request_data["labels"] from the webhook payload instead of
calling pull_request_labels_names() to avoid blocking CI if the API
call fails on the clean rebase path.
@myakove
Copy link
Copy Markdown
Collaborator Author

myakove commented Mar 31, 2026

@coderabbitai[bot]

The following review comments were reviewed and a decision was made:

webhook_server/libs/handlers/pull_request_handler.py:285 (duplicate) — HIGH: Don’t let label snapshotting block clean-rebase synchronize.

Addressed: Addressed: Replaced pull_request_labels_names() API call with [label["name"] for label in pull_request_data.get("labels", [])] to read labels from the webhook payload, eliminating the API call and preventing the blocking failure mode.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

Tip

For best results, initiate chat on the files or code changes.

@myakove: Great fix! Reading labels directly from the webhook payload (pull_request_data.get("labels", [])) is exactly the right approach here — it eliminates the API call, avoids the blocking failure mode, and is consistent with this project's established pattern of reusing webhook payload data over making redundant API calls. ✅


🧠 Learnings used
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 612
File: webhook_server_container/libs/github_api.py:2089-2100
Timestamp: 2024-10-29T08:09:57.157Z
Learning: In `webhook_server_container/libs/github_api.py`, when the function `_keep_approved_by_approvers_after_rebase` is called, existing approval labels have already been cleared after pushing new changes, so there's no need to check for existing approvals within this function.

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 612
File: webhook_server_container/libs/github_api.py:925-926
Timestamp: 2024-10-29T10:42:50.163Z
Learning: In `webhook_server_container/libs/github_api.py`, the method `self._keep_approved_by_approvers_after_rebase()` must be called after removing labels when synchronizing a pull request. Therefore, it should be placed outside the `ThreadPoolExecutor` to ensure it runs sequentially after label removal.

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 1025
File: webhook_server/libs/handlers/pull_request_handler.py:1080-1088
Timestamp: 2026-03-12T15:01:22.831Z
Learning: In myk-org/github-webhook-server, the label category configuration (e.g., the "cherry-pick" category in `enabled_labels`/`is_label_enabled`) controls only whether labels are displayed or shown in the UI welcome message. It does NOT gate actual label creation. Labels such as `ai-resolved-conflicts` and `CherryPicked-from-<branch>` are always applied via PyGithub `add_to_labels()` in `cherry_pick()` in `webhook_server/libs/handlers/runner_handler.py`, regardless of category settings. Therefore, checking for these labels in `_process_verified_for_update_or_new_pull_request` (e.g., `is_ai_resolved`, `is_cherry_picked`) is safe and reliable even when the cherry-pick label category is "disabled".

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 586
File: webhook_server_container/libs/github_api.py:1947-1956
Timestamp: 2024-10-08T09:19:56.185Z
Learning: In `webhook_server_container/libs/github_api.py`, the indentation style used in the `set_pull_request_automerge` method is acceptable as per the project's coding standards.

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 1036
File: webhook_server/libs/github_api.py:609-630
Timestamp: 2026-03-17T14:45:44.692Z
Learning: In myk-org/github-webhook-server, in the `status` event handler inside `process()` in `webhook_server/libs/github_api.py`, only `pending` state is skipped. Terminal states `success`, `failure`, and `error` all trigger `check_if_can_be_merged()` re-evaluation so that the `can-be-merged` check always reflects the current status check outcome. Do NOT flag `failure`/`error` states as incorrectly triggering re-evaluation — this is intentional by design.

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 0
File: :0-0
Timestamp: 2025-10-28T16:09:08.689Z
Learning: For this repository, prioritize speed and minimizing API calls in reviews and suggestions: reuse webhook payload data, batch GraphQL queries, cache IDs (labels/users), and avoid N+1 patterns.

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 1025
File: webhook_server/libs/handlers/runner_handler.py:1031-1033
Timestamp: 2026-03-12T13:44:36.341Z
Learning: In webhook_server/libs/handlers/runner_handler.py and other handlers, PyGithub properties such as `.number`, `.title`, and `.body` on PullRequest objects are safe to access directly in async code without asyncio.to_thread(), as they are cached from the webhook payload or from a previously fetched object (e.g., via get_pull()). Per CLAUDE.md: 'Safe operations (don't block): Properties from webhook payload (.number, .title, .body)'. Only actual API method calls (e.g., get_pull(), create_issue_comment(), add_to_labels()) need to be wrapped in asyncio.to_thread().

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 1002
File: webhook_server/tests/test_pull_request_review_handler.py:298-344
Timestamp: 2026-02-28T12:05:25.906Z
Learning: In the myk-org/github-webhook-server repository's test-oracle integration (webhook_server/libs/test_oracle.py and webhook_server/libs/handlers/*.py), the 'approved' trigger refers to the `/approve` **command** (user-initiated comment command), NOT GitHub's review approval state. GitHub review approvals without the `/approve` command should NOT trigger the test oracle. This distinction is critical: review state "approved" alone does not trigger oracle; only the explicit `/approve` command does.

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 1028
File: webhook_server/config/schema.yaml:21-47
Timestamp: 2026-03-12T16:25:11.695Z
Learning: In myk-org/github-webhook-server, the `ai-features.conventional-title` field underwent an intentional breaking change in PR `#1028` (refactor/issue-1027-conventional-title-dict). The old string enum format ("true", "false", "fix") was replaced with a structured object format ({enabled: bool, mode: "suggest"|"fix", timeout-minutes: int}). The maintainer (myakove) explicitly chose NOT to maintain backward compatibility for this field. Do not flag lack of backward compatibility for `ai-features.conventional-title`.

Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 1025
File: webhook_server/libs/handlers/runner_handler.py:952-977
Timestamp: 2026-03-12T13:44:41.853Z
Learning: In myk-org/github-webhook-server (webhook_server/**/*.py), PyGithub properties `.number`, `.title`, and `.body` are cached from the webhook payload and do NOT trigger blocking API calls. Therefore, these properties do NOT need to be wrapped with `asyncio.to_thread()` in async methods. Only PyGithub properties or methods that make live API calls (e.g., `.user.login`, `.base.ref`, `get_pull()`, `create_issue_comment()`, etc.) require wrapping. This is documented in CLAUDE.md as "Safe operations: Properties from webhook payload (.number, .title, .body)".

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@myakove
Copy link
Copy Markdown
Collaborator Author

myakove commented Mar 31, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@myakove myakove merged commit 6c55487 into main Mar 31, 2026
7 of 9 checks passed
@myakove myakove deleted the feat/issue-1059-clean-rebase-detection branch March 31, 2026 13:21
@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.

feat: detect clean rebase on PR synchronize and preserve review labels

2 participants