mirror of
https://github.com/langgenius/dify-plugin-sdks.git
synced 2026-07-22 10:25:23 -04:00
736d178485
- Added a new trigger configuration for `pull_request_merged.yaml` to enhance event handling. - Updated the `deployment_status_created.py` to return event variables correctly. - Expanded the `deployment_status_created.yaml` with additional properties for better event context. - Removed obsolete trigger files related to discussions and issues, streamlining the trigger set. These changes improve the overall functionality and maintainability of the GitHub trigger integration.
264 lines
11 KiB
Python
264 lines
11 KiB
Python
import fnmatch
|
|
from collections.abc import Mapping
|
|
from typing import Any
|
|
|
|
from werkzeug import Request
|
|
|
|
from dify_plugin.entities.trigger import Event
|
|
from dify_plugin.errors.trigger import TriggerIgnoreEventError
|
|
from dify_plugin.interfaces.trigger import TriggerEvent
|
|
|
|
|
|
class PullRequestTrigger(TriggerEvent):
|
|
"""
|
|
GitHub Pull Request Event Trigger
|
|
|
|
This unified trigger handles all GitHub pull request events and extracts relevant
|
|
information from the webhook payload to provide as variables to the workflow.
|
|
"""
|
|
|
|
def _trigger(self, request: Request, parameters: Mapping[str, Any]) -> Event:
|
|
"""
|
|
Handle GitHub pull request event trigger with comprehensive filtering
|
|
|
|
Parameters:
|
|
- action_filter: Filter by action type (opened, closed, edited, etc.)
|
|
- target_branches: Filter by target branch (comma-separated, supports wildcards)
|
|
- source_branches: Filter by source branch (comma-separated, supports wildcards)
|
|
- labels: Filter by PR labels (comma-separated)
|
|
- skip_draft: Skip draft PRs (default: true)
|
|
- exclude_authors: Exclude PRs from these authors
|
|
- file_count_limit: Ignore PRs with too many file changes
|
|
- min_additions: Minimum lines added
|
|
- max_additions: Maximum lines added
|
|
"""
|
|
# Get the event payload
|
|
payload = request.get_json()
|
|
if not payload:
|
|
raise ValueError("No payload received")
|
|
|
|
# Get the action type
|
|
action = payload.get("action", "")
|
|
|
|
# Apply action filter
|
|
action_filter = parameters.get("action_filter", [])
|
|
if action_filter and action not in action_filter:
|
|
raise TriggerIgnoreEventError(
|
|
f"Action '{action}' not in filter list: {action_filter}"
|
|
)
|
|
|
|
# Extract pull request information
|
|
pull_request = payload.get("pull_request", {})
|
|
repository = payload.get("repository", {})
|
|
sender = payload.get("sender", {})
|
|
changes = payload.get("changes", {})
|
|
|
|
# Skip draft PRs if configured
|
|
skip_draft = parameters.get("skip_draft", True)
|
|
if skip_draft and pull_request.get("draft", False):
|
|
# Only skip for actions that create/update the PR
|
|
if action in ["opened", "reopened", "synchronize", "edited"]:
|
|
raise TriggerIgnoreEventError("Skipping draft pull request")
|
|
|
|
# Apply target branch filter
|
|
target_branches = parameters.get("target_branches")
|
|
if target_branches:
|
|
target_branch = pull_request.get("base", {}).get("ref", "")
|
|
branches = [b.strip() for b in target_branches.split(",")]
|
|
if not any(fnmatch.fnmatch(target_branch, pattern) for pattern in branches):
|
|
raise TriggerIgnoreEventError(
|
|
f"Target branch '{target_branch}' doesn't match filter: {branches}"
|
|
)
|
|
|
|
# Apply source branch filter
|
|
source_branches = parameters.get("source_branches")
|
|
if source_branches:
|
|
source_branch = pull_request.get("head", {}).get("ref", "")
|
|
branches = [b.strip() for b in source_branches.split(",")]
|
|
if not any(fnmatch.fnmatch(source_branch, pattern) for pattern in branches):
|
|
raise TriggerIgnoreEventError(
|
|
f"Source branch '{source_branch}' doesn't match filter: {branches}"
|
|
)
|
|
|
|
# Apply label filter
|
|
labels_filter = parameters.get("labels")
|
|
if labels_filter:
|
|
required_labels = [l.strip() for l in labels_filter.split(",")]
|
|
pr_labels = [label.get("name", "") for label in pull_request.get("labels", [])]
|
|
if not any(label in pr_labels for label in required_labels):
|
|
raise TriggerIgnoreEventError(
|
|
f"PR doesn't have required labels: {required_labels}"
|
|
)
|
|
|
|
# Apply author exclusion filter
|
|
exclude_authors = parameters.get("exclude_authors")
|
|
if exclude_authors:
|
|
excluded = [a.strip() for a in exclude_authors.split(",")]
|
|
author = pull_request.get("user", {}).get("login", "")
|
|
if author in excluded:
|
|
raise TriggerIgnoreEventError(f"Author '{author}' is excluded")
|
|
|
|
# Apply file count limit
|
|
file_count_limit = parameters.get("file_count_limit")
|
|
if file_count_limit is not None:
|
|
changed_files = pull_request.get("changed_files", 0)
|
|
if changed_files > int(file_count_limit):
|
|
raise TriggerIgnoreEventError(
|
|
f"Too many files changed: {changed_files} > {file_count_limit}"
|
|
)
|
|
|
|
# Apply additions filters
|
|
min_additions = parameters.get("min_additions")
|
|
if min_additions is not None:
|
|
additions = pull_request.get("additions", 0)
|
|
if additions < int(min_additions):
|
|
raise TriggerIgnoreEventError(
|
|
f"Too few additions: {additions} < {min_additions}"
|
|
)
|
|
|
|
max_additions = parameters.get("max_additions")
|
|
if max_additions is not None:
|
|
additions = pull_request.get("additions", 0)
|
|
if additions > int(max_additions):
|
|
raise TriggerIgnoreEventError(
|
|
f"Too many additions: {additions} > {max_additions}"
|
|
)
|
|
|
|
# Extract labels
|
|
labels = [
|
|
{
|
|
"name": label.get("name", ""),
|
|
"color": label.get("color", ""),
|
|
"description": label.get("description", ""),
|
|
}
|
|
for label in pull_request.get("labels", [])
|
|
]
|
|
|
|
# Extract assignees
|
|
assignees = [
|
|
{
|
|
"login": assignee.get("login", ""),
|
|
"avatar_url": assignee.get("avatar_url", ""),
|
|
"html_url": assignee.get("html_url", ""),
|
|
}
|
|
for assignee in pull_request.get("assignees", [])
|
|
]
|
|
|
|
# Extract requested reviewers (users)
|
|
requested_reviewers = [
|
|
{
|
|
"login": reviewer.get("login", ""),
|
|
"avatar_url": reviewer.get("avatar_url", ""),
|
|
"html_url": reviewer.get("html_url", ""),
|
|
}
|
|
for reviewer in pull_request.get("requested_reviewers", [])
|
|
]
|
|
|
|
# Extract requested teams
|
|
requested_teams = [
|
|
{
|
|
"name": team.get("name", ""),
|
|
"slug": team.get("slug", ""),
|
|
}
|
|
for team in pull_request.get("requested_teams", [])
|
|
]
|
|
|
|
# Extract milestone information
|
|
milestone = None
|
|
if pull_request.get("milestone"):
|
|
milestone = {
|
|
"title": pull_request["milestone"].get("title", ""),
|
|
"description": pull_request["milestone"].get("description", ""),
|
|
"due_on": pull_request["milestone"].get("due_on", ""),
|
|
}
|
|
|
|
# Extract head (source) information
|
|
head = {
|
|
"ref": pull_request.get("head", {}).get("ref", ""),
|
|
"sha": pull_request.get("head", {}).get("sha", ""),
|
|
"repo": {
|
|
"name": pull_request.get("head", {}).get("repo", {}).get("name", ""),
|
|
"full_name": pull_request.get("head", {}).get("repo", {}).get("full_name", ""),
|
|
} if pull_request.get("head", {}).get("repo") else None,
|
|
}
|
|
|
|
# Extract base (target) information
|
|
base = {
|
|
"ref": pull_request.get("base", {}).get("ref", ""),
|
|
"sha": pull_request.get("base", {}).get("sha", ""),
|
|
"repo": {
|
|
"name": pull_request.get("base", {}).get("repo", {}).get("name", ""),
|
|
"full_name": pull_request.get("base", {}).get("repo", {}).get("full_name", ""),
|
|
} if pull_request.get("base", {}).get("repo") else None,
|
|
}
|
|
|
|
# Extract changes information (for edited action)
|
|
changes_info = {}
|
|
if action == "edited" and changes:
|
|
if "title" in changes:
|
|
changes_info["title"] = {"from": changes["title"].get("from", "")}
|
|
if "body" in changes:
|
|
changes_info["body"] = {"from": changes["body"].get("from", "")}
|
|
|
|
# Build variables for the workflow
|
|
variables = {
|
|
"action": action,
|
|
"pull_request": {
|
|
"number": pull_request.get("number"),
|
|
"title": pull_request.get("title", ""),
|
|
"body": pull_request.get("body", ""),
|
|
"state": pull_request.get("state", ""),
|
|
"merged": pull_request.get("merged", False),
|
|
"draft": pull_request.get("draft", False),
|
|
"html_url": pull_request.get("html_url", ""),
|
|
"diff_url": pull_request.get("diff_url", ""),
|
|
"patch_url": pull_request.get("patch_url", ""),
|
|
"created_at": pull_request.get("created_at", ""),
|
|
"updated_at": pull_request.get("updated_at", ""),
|
|
"closed_at": pull_request.get("closed_at", ""),
|
|
"merged_at": pull_request.get("merged_at", ""),
|
|
"head": head,
|
|
"base": base,
|
|
"user": {
|
|
"login": pull_request.get("user", {}).get("login", ""),
|
|
"avatar_url": pull_request.get("user", {}).get("avatar_url", ""),
|
|
"html_url": pull_request.get("user", {}).get("html_url", ""),
|
|
},
|
|
"labels": labels,
|
|
"assignees": assignees,
|
|
"requested_reviewers": requested_reviewers,
|
|
"requested_teams": requested_teams,
|
|
"milestone": milestone,
|
|
"additions": pull_request.get("additions", 0),
|
|
"deletions": pull_request.get("deletions", 0),
|
|
"changed_files": pull_request.get("changed_files", 0),
|
|
"commits": pull_request.get("commits", 0),
|
|
"review_comments": pull_request.get("review_comments", 0),
|
|
"comments": pull_request.get("comments", 0),
|
|
},
|
|
"repository": {
|
|
"name": repository.get("name", ""),
|
|
"full_name": repository.get("full_name", ""),
|
|
"html_url": repository.get("html_url", ""),
|
|
"description": repository.get("description", ""),
|
|
"private": repository.get("private", False),
|
|
"owner": {
|
|
"login": repository.get("owner", {}).get("login", ""),
|
|
"avatar_url": repository.get("owner", {}).get("avatar_url", ""),
|
|
"html_url": repository.get("owner", {}).get("html_url", ""),
|
|
},
|
|
"default_branch": repository.get("default_branch", ""),
|
|
},
|
|
"sender": {
|
|
"login": sender.get("login", ""),
|
|
"avatar_url": sender.get("avatar_url", ""),
|
|
"html_url": sender.get("html_url", ""),
|
|
"type": sender.get("type", ""),
|
|
},
|
|
}
|
|
|
|
# Add changes info if present
|
|
if changes_info:
|
|
variables["changes"] = changes_info
|
|
|
|
return Event(variables=variables) |