Skip to content

fix: prevent duplicate cherry-pick PRs via label check#1055

Merged
myakove merged 1 commit intomainfrom
fix/issue-1054-prevent-duplicate-cherry-picks
Mar 24, 2026
Merged

fix: prevent duplicate cherry-pick PRs via label check#1055
myakove merged 1 commit intomainfrom
fix/issue-1054-prevent-duplicate-cherry-picks

Conversation

@myakove
Copy link
Copy Markdown
Collaborator

@myakove myakove commented Mar 24, 2026

Summary

  • Check if cherry-pick label already exists on the PR before processing
  • Skip branches whose cherry-pick-{branch} label is already present
  • Post comment telling user to remove the label to re-trigger
  • Works for both merged and unmerged PRs
  • Early return when no valid target branches (avoids unnecessary API calls)

Triggered by duplicate cherry-pick PRs on RedHatQE/mtv-api-tests#369

Closes #1054

Test plan

  • test_process_cherry_pick_command_skips_already_cherry_picked - partial skip (merged PR)
  • test_process_cherry_pick_command_all_already_cherry_picked - full skip (merged PR)
  • test_process_cherry_pick_command_skips_already_cherry_picked_unmerged - partial skip (unmerged PR)
  • All existing cherry-pick tests pass (9 total)
  • All 200 tests pass

Summary by CodeRabbit

  • Bug Fixes

    • Cherry-pick operations now skip branches that are already labeled, post a comment listing skipped branches with re-trigger instructions, and return early if no valid targets remain.
    • Label naming/formatting standardized for consistency.
    • Per-branch handling ensures failed label additions skip that branch without affecting others.
  • Tests

    • Added idempotency and skip-case tests covering merged/unmerged PRs and label-add failures.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 2026

Warning

Rate limit exceeded

@myakove has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 9 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: 74bfa6a5-c6b7-4505-a83c-228a08cd5c43

📥 Commits

Reviewing files that changed from the base of the PR and between 9226052 and 0668b43.

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

Walkthrough

Process_cherry_pick_command was changed to use hyphenated labels (cherry-pick-<branch>), detect and skip branches already labeled, add labels only for missing branches, post comments about skipped branches, and run cherry-picks only for filtered branches. Tests added for idempotency and TOCTOU cases.

Changes

Cohort / File(s) Summary
Core Cherry-Pick Handler
webhook_server/libs/handlers/issue_comment_handler.py
Reworked process_cherry_pick_command to: use cherry-pick-<branch> label format; fetch PR labels and filter out branches already labeled; early-return if no target branches remain; post comments listing already-present labels; add labels and call runner_handler.cherry_pick only for branches that still need processing; handle merged/unmerged flows with per-branch label-add skip on failure.
Test Suite Updates
webhook_server/tests/test_issue_comment_handler.py
Replaced hardcoded label strings with CHERRY_PICK_LABEL_PREFIX; initialized mock_pull_request.labels = [] where needed; adjusted _add_label mock returns; added async tests for idempotency and skip behavior (partial skip, full skip, TOCTOU label-add failure, and unmerged-PR skip cases).

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
actor User
participant Webhook as "Webhook Server\n(IssueCommentHandler)"
participant Labels as "labels_handler"
participant Runner as "runner_handler"
participant GitHub as "GitHub (PR & Labels)"
User->>Webhook: Trigger cherry-pick command / add labels
Webhook->>GitHub: Fetch PR details and current labels
Webhook->>Labels: Compute target cherry-pick-<branch> labels
Labels-->>Webhook: Return existing PR labels
alt Some branches already labeled
Webhook->>GitHub: Post comment listing already-labeled branches
Webhook->>Labels: _add_label for missing branches
Webhook->>Runner: cherry_pick for missing branches
else No branches need action
Webhook->>GitHub: Post comment that all branches already labeled (no action)
end
Runner->>GitHub: Create cherry-pick PRs (for branches processed)
GitHub-->>User: Notify results

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • rnetser
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: prevent duplicate cherry-pick PRs via label check' accurately describes the primary change: adding label detection to prevent creating duplicate cherry-pick PRs.
Linked Issues check ✅ Passed The PR fully satisfies issue #1054 objectives: detects existing cherry-pick labels, skips branches with pre-existing labels, posts user instructions, applies to both merged/unmerged PRs, and includes early-return optimization.
Out of Scope Changes check ✅ Passed All changes are directly scoped to preventing duplicate cherry-picks: label format updates, duplicate prevention logic, early returns, and corresponding test coverage—no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.

✏️ 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-1054-prevent-duplicate-cherry-picks

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.

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/libs/handlers/issue_comment_handler.py`:
- Around line 482-490: The loop currently calls runner_handler.cherry_pick for
each branch and delays adding the cp_labels until after the whole loop, risking
missing idempotency markers when a later cherry-pick fails; fix by moving the
label add inside the per-branch loop so each successful cherry-pick records its
source PR label before proceeding to the next branch: inside the for _branch in
_branches_to_process loop (where runner_handler.cherry_pick is called),
immediately call labels_handler._add_label for the corresponding cp_label (use a
zip or map between _branches_to_process and cp_labels or derive the label for
_branch) and only add the label after the await runner_handler.cherry_pick
returns successfully. Ensure you do not duplicate labels if they already exist.
🪄 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: 2fc7e64e-38df-43b9-ae23-4c12be4e88f2

📥 Commits

Reviewing files that changed from the base of the PR and between 5a8886e and 0d17391.

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

Comment thread webhook_server/libs/handlers/issue_comment_handler.py
@myakove myakove force-pushed the fix/issue-1054-prevent-duplicate-cherry-picks branch from 0d17391 to 858c93e Compare March 24, 2026 22:16
@myakove
Copy link
Copy Markdown
Collaborator Author

myakove commented Mar 24, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 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/issue_comment_handler.py`:
- Around line 419-423: Update the example wording that describes the cherry-pick
label so it no longer implies completion; instead state that the label is set
before runner_handler.cherry_pick() as an idempotency/request marker and may
remain after a failed attempt, and clarify that removal of the label is required
to re-trigger the operation; modify the paragraph that currently treats the
label as a completion marker to explicitly call it an "idempotency/request
marker" and mention runner_handler.cherry_pick() to show when the label is
applied.
- Around line 449-458: The current TOCTOU comes from using a snapshot
(existing_labels / _branches_to_process) to decide work but ignoring the
_add_label() result as a merged-path lock before calling cherry_pick(); update
the loop that processes _branches_to_process so that for each branch you call
_add_label(branch) and only call cherry_pick(...) when _add_label returned True,
otherwise skip/record it as already handled (respecting _already_cherry_picked
semantics) and do not call cherry_pick; apply the same fix in the other
processing block that handles branches (the code around where _add_label and
cherry_pick are currently called) and add a regression test that simulates
_add_label() returning False on a merged PR and asserts that cherry_pick() is
not invoked for that branch.
🪄 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: 185be1d8-42a5-46c9-93d2-5f4271154630

📥 Commits

Reviewing files that changed from the base of the PR and between 0d17391 and 858c93e.

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

Comment thread webhook_server/libs/handlers/issue_comment_handler.py Outdated
Comment thread webhook_server/libs/handlers/issue_comment_handler.py
@myakove
Copy link
Copy Markdown
Collaborator Author

myakove commented Mar 24, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 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
Copy link
Copy Markdown
Collaborator Author

myakove commented Mar 24, 2026

@coderabbitia
I need your oppinion about alternative approch.

right now we check via lables, another approch is to check via our comment when cherry pick secceded
example od such comment Cherry-picked PR Support: change independent_nonpersistent disk mode after cloud-init into v2.10: #386

knowen parts are Cherry-picked PR, into {target branch} and we also have the cherry-pick PR number in the comment
in unmerged PR flow it should be simple, we only add labels, once PR is merged we try to cherry-pick.
in merged PR we shource our comment, if not found we add the labels and chery pick it, if found we take the cherry-pick PR number and check if exists (in a ny status)

I need to know if this is better and if it worth the extra api calls (since it will be more api calls)

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 2026

Tip

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

🧩 Analysis chain

Let me look at the cherry-pick comment posting code to give you a well-informed opinion.


🏁 Script executed:

#!/bin/bash
# Find how cherry-pick success comments are posted
rg -n "Cherry-picked PR" --type py -C 5

# Also look at how cherry_pick function posts comments
rg -n "cherry.pick" webhook_server/libs/handlers/runner_handler.py -C 5 | head -100

# Check how labels are fetched in the current implementation
rg -n "cherry.pick" webhook_server/libs/handlers/issue_comment_handler.py -C 3 | head -100

Length of output: 12881


@myakove — good question. Here's my analysis of both approaches:


Comparison

Concern Label-based (current) Comment-based (proposed)
API calls 1 (labels already fetched) N+1 (list all comments + 1 PR-exists check per branch)
Applies to unmerged PRs ✅ Yes ❌ No — comments only exist after success; labels still needed
Re-trigger mechanism Remove label (explicit, documented) Unclear — user can't "remove" a comment to re-trigger
Fragility Stable — label name is deterministic Fragile — comment embeds pull_request.title verbatim (special chars, slashes, etc.)
Format stability Label format is a constant Comment format could drift (there are already 2 variants: regular + AI-resolved)
Semantic accuracy Represents intent/scheduling Represents actual success

Key issue with the comment approach

Looking at the actual code in runner_handler.py, the success comment format is:

f"Cherry-picked PR {pull_request.title} into {target_branch}: {cherry_pick_pr_url}"

It's a URL, not #386. More importantly, pull_request.title is embedded verbatim — parsing this with a regex is brittle because titles can contain colons, special characters, or even the substring " into ". You'd also need to handle the AI-resolved variant, which wraps the same line inside a multi-line message.


My recommendation: stick with the label-based approach

HIGH reason — the comment approach doesn't actually replace labels; it adds a second mechanism on top. Since labels are still required for the unmerged flow, you'd end up maintaining both systems. For the merged flow, the label already gets added (_add_label) before cherry_pick() is called — so after a successful cherry-pick, both the label AND the comment exist. The label is the simpler, cheaper signal.

MEDIUM reason — the "remove label to re-trigger" escape hatch is already intentional and documented in the docstring. The comment approach has no clean re-trigger mechanism (you can't un-post a comment to re-enable processing).

The comment-based approach would only add value if you wanted to detect cherry-picks that were done outside of this system (e.g., manually), but that's a separate use case. For the problem in #1054, the label check is sufficient and costs zero extra API calls.


🧠 Learnings used
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: 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: 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: 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.

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/issue_comment_handler.py`:
- Around line 474-481: Fix the typo and remove the redundant list comprehension
in the info message: in issue_comment_handler.py update the info_msg string used
near pull_request.create_issue_comment to change "cheery-pick" → "cherry-pick"
and replace `" ".join([_cp_label for _cp_label in cp_labels])` with `"
".join(cp_labels)` (referencing pull_request, info_msg, and cp_labels to locate
the code).

In `@webhook_server/tests/test_issue_comment_handler.py`:
- Around line 984-996: The nested context managers around patch.object calls
should be consolidated into a single with statement to address Ruff SIM117:
combine the patches for issue_comment_handler.repository.get_branch,
issue_comment_handler.runner_handler.cherry_pick (AsyncMock),
issue_comment_handler.labels_handler._add_label (AsyncMock), and
mock_pull_request.create_issue_comment into one with ... as ... tuple-style
context so all mocks (mock_cherry_pick, mock_add_label, mock_comment, etc.) are
created in a single block; apply the same consolidation pattern to the other
occurrences (the blocks around lines 1038-1048, 1078-1090, 1113-1119) while
preserving the new_callable and return_value arguments for AsyncMock patches.
🪄 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: 55c7c169-ebe3-4c66-93e0-c8e1ecc500a4

📥 Commits

Reviewing files that changed from the base of the PR and between 858c93e and 9226052.

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

Comment thread webhook_server/libs/handlers/issue_comment_handler.py
Comment thread webhook_server/tests/test_issue_comment_handler.py Outdated
Skip cherry-pick for branches whose cherry-pick label already exists on
the PR. Posts comment telling user to remove the label to re-trigger.
Works for both merged and unmerged PRs.

Closes #1054
@myakove myakove merged commit 5ef48b4 into main Mar 24, 2026
7 of 9 checks passed
@myakove myakove deleted the fix/issue-1054-prevent-duplicate-cherry-picks branch March 24, 2026 23:00
@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.

bug: cherry-pick labels re-added to already cherry-picked PR, triggering duplicate cherry-pick PRs

2 participants