Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ management and pull request workflows.
- [Usage](#usage)
- [API Reference](#api-reference)
- [Log Viewer](#log-viewer)
- [AI Features](#ai-features)
- [User Commands](#user-commands)
- [OWNERS File Format](#owners-file-format)
- [Security](#security)
Expand Down Expand Up @@ -1239,6 +1240,53 @@ The MCP integration is built using the `fastapi-mcp` library and provides:
- **Error handling**: Graceful error responses with helpful debugging information
- **Performance optimization**: Efficient data access patterns for AI processing

## AI Features

Optional AI-powered enhancements. Requires `ai-features` configuration with a provider and model:

```yaml
ai-features:
ai-provider: "claude" # claude | gemini | cursor
ai-model: "claude-opus-4-6[1m]"
```

### Conventional Title Validation

AI-suggested fixes for PR titles that don't follow the Conventional Commits format:

```yaml
ai-features:
ai-provider: "claude"
ai-model: "claude-opus-4-6[1m]"
conventional-title: "true" # "true": suggest in check run | "false": disabled | "fix": auto-update PR title
```

| Mode | Behavior |
|------|----------|
| `"true"` | Shows AI-suggested title in check run output when validation fails |
| `"false"` | Disabled (default) |
| `"fix"` | Automatically updates the PR title with the AI suggestion |

### Cherry-Pick Conflict Resolution

When cherry-pick encounters merge conflicts, the AI CLI can automatically resolve them:

```yaml
ai-features:
ai-provider: "claude"
ai-model: "claude-opus-4-6[1m]"
resolve-cherry-pick-conflicts-with-ai:
enabled: true
timeout-minutes: 10 # Default: 10
```

When enabled:
- The AI resolves conflicts with **upstream-first priority** (target branch changes are the baseline)
- Cherry-picked PRs are labeled `CherryPicked-from-<source-branch>` (e.g., `CherryPicked-from-main`)
- AI-resolved PRs get an additional `ai-resolved-conflicts` label
- AI-resolved PRs are **never auto-verified** — manual review is always required
- If AI fails, falls back to manual cherry-pick instructions

## User Commands

Users can interact with the webhook server through GitHub comments on pull requests and issues.
Expand Down Expand Up @@ -1321,6 +1369,8 @@ Cherry-picked PRs can be automatically verified or require manual verification d
auto-verify-cherry-picked-prs: true # Default: true (auto-verify). Set to false to require manual verification
```

**AI Conflict Resolution**: Cherry-pick conflicts can be automatically resolved by AI. See [AI Features](#ai-features) for configuration.

### Label Commands

| Command | Description | Example |
Expand Down
3 changes: 3 additions & 0 deletions examples/.github-webhook-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ ai-features:
ai-provider: "claude" # claude | gemini | cursor
ai-model: "claude-opus-4-6[1m]"
conventional-title: "true" # "true": suggest in checkrun | "false": disabled | "fix": auto-update PR title
resolve-cherry-pick-conflicts-with-ai:
enabled: true
timeout-minutes: 10 # Timeout in minutes for AI CLI (default: 10)

# PR Test Oracle integration (overrides global config)
# Analyzes PR diffs with AI and recommends which tests to run
Expand Down
3 changes: 3 additions & 0 deletions examples/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ ai-features:
ai-provider: "claude" # claude | gemini | cursor
ai-model: "claude-opus-4-6[1m]"
conventional-title: "true" # "true": suggest in checkrun | "false": disabled | "fix": auto-update PR title
resolve-cherry-pick-conflicts-with-ai:
enabled: true
timeout-minutes: 10 # Timeout in minutes for AI CLI (default: 10)

repositories:
my-repository:
Expand Down
19 changes: 19 additions & 0 deletions webhook_server/config/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ $defs:
- "false": Disabled (default)
- "fix": Auto-fix PR title with AI suggestion when validation fails
default: "false"
resolve-cherry-pick-conflicts-with-ai:
type: object
description: |
AI-powered cherry-pick conflict resolution configuration.
When enabled and a cherry-pick has conflicts, the AI CLI will attempt
to resolve them with upstream-first priority. AI-resolved cherry-picks
are never auto-verified - manual review is required.
properties:
enabled:
type: boolean
description: Enable AI conflict resolution for cherry-picks
timeout-minutes:
type: integer
minimum: 1
description: Timeout in minutes for the AI CLI process (default 10)
default: 10
required:
- enabled
additionalProperties: false
required:
- ai-provider
- ai-model
Expand Down
2 changes: 1 addition & 1 deletion webhook_server/libs/handlers/issue_comment_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ async def process_cherry_pick_command(
]

if _exits_target_branches:
if not await asyncio.to_thread(pull_request.is_merged):
if not self.hook_data["issue"].get("pull_request", {}).get("merged_at"):
info_msg: str = f"""
Cherry-pick requested for PR: `{pull_request.title}` by user `{reviewed_user}`
Adding label/s `{" ".join([_cp_label for _cp_label in cp_labels])}` for automatic cheery-pick once the PR is merged
Expand Down
2 changes: 1 addition & 1 deletion webhook_server/libs/handlers/labels_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def is_label_enabled(self, label: str) -> bool:
return "branch" in enabled_labels

# Check cherry-pick labels
if label.startswith(CHERRY_PICK_LABEL_PREFIX) or label == CHERRY_PICKED_LABEL:
if label.startswith((CHERRY_PICK_LABEL_PREFIX, CHERRY_PICKED_LABEL)):
return "cherry-pick" in enabled_labels

# Unknown labels are allowed by default
Expand Down
13 changes: 12 additions & 1 deletion webhook_server/libs/handlers/pull_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from webhook_server.libs.handlers.runner_handler import RunnerHandler
from webhook_server.libs.test_oracle import call_test_oracle
from webhook_server.utils.constants import (
AI_RESOLVED_CONFLICTS_LABEL,
APPROVED_BY_LABEL_PREFIX,
AUTOMERGE_LABEL_STR,
BRANCH_LABEL_PREFIX,
Expand Down Expand Up @@ -1075,14 +1076,24 @@ async def _process_verified_for_update_or_new_pull_request(self, pull_request: P

# Check if this is a cherry-picked PR
labels = await asyncio.to_thread(lambda: list(pull_request.labels))
is_cherry_picked = any(label.name == CHERRY_PICKED_LABEL for label in labels)

# AI-resolved cherry-picks are NEVER auto-verified (takes precedence over auto-verify-cherry-picked-prs)
is_ai_resolved = any(label.name == AI_RESOLVED_CONFLICTS_LABEL for label in labels)
if is_ai_resolved:
self.logger.info(f"{self.log_prefix} AI-resolved cherry-pick detected, skipping auto-verification")
await self.labels_handler._remove_label(pull_request=pull_request, label=VERIFIED_LABEL_STR)
await self.check_run_handler.set_check_queued(name=VERIFIED_LABEL_STR)
return
Comment thread
coderabbitai[bot] marked this conversation as resolved.

is_cherry_picked = any(label.name.startswith(CHERRY_PICKED_LABEL) for label in labels)
Comment thread
myakove marked this conversation as resolved.

# If it's a cherry-picked PR and auto-verify is disabled for cherry-picks, skip auto-verification
if is_cherry_picked and not self.github_webhook.auto_verify_cherry_picked_prs:
self.logger.info(
f"{self.log_prefix} Cherry-picked PR detected and auto-verify-cherry-picked-prs is disabled, "
"skipping auto-verification"
)
await self.labels_handler._remove_label(pull_request=pull_request, label=VERIFIED_LABEL_STR)
await self.check_run_handler.set_check_queued(name=VERIFIED_LABEL_STR)
return

Expand Down
Loading