Skip to content

Commit 900bc0d

Browse files
authored
fix: enhance Github plugin (#2123)
1 parent 28faab5 commit 900bc0d

36 files changed

+2236
-110
lines changed

tools/github/manifest.yaml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ author: langgenius
22
created_at: "2024-09-20T08:03:44.658609186Z"
33
description:
44
en_US: GitHub is an online software source code hosting service.
5-
pt_BR:
6-
GitHub é uma plataforma online para serviços de hospedagem de código fonte
7-
de software.
5+
pt_BR: GitHub é uma plataforma online para serviços de hospedagem de código fonte de software.
86
zh_Hans: GitHub是一个在线软件源代码托管服务平台。
97
icon: icon.svg
108
label:
@@ -13,18 +11,18 @@ label:
1311
zh_Hans: GitHub
1412
meta:
1513
arch:
16-
- amd64
17-
- arm64
14+
- amd64
15+
- arm64
1816
runner:
1917
entrypoint: main
2018
language: python
2119
version: "3.12"
22-
version: 0.2.8
20+
version: 0.2.9
2321
minimum_dify_version: 1.7.0
2422
name: github
2523
plugins:
2624
tools:
27-
- provider/github.yaml
25+
- provider/github.yaml
2826
resource:
2927
memory: 1048576
3028
permission:
@@ -34,6 +32,6 @@ resource:
3432
tool:
3533
enabled: true
3634
tags:
37-
- utilities
35+
- utilities
3836
type: plugin
39-
version: 0.2.8
37+
version: 0.3.0

tools/github/provider/github.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ def _oauth_get_authorization_url(self, redirect_uri: str, system_credentials: Ma
2424
params = {
2525
"client_id": system_credentials["client_id"],
2626
"redirect_uri": redirect_uri,
27-
"scope": system_credentials.get("scope", "read:user"),
27+
"scope": system_credentials.get("scope", "read:user repo"),
2828
"state": state,
29+
# Optionally: allow_signup, login, etc.
2930
}
3031
return f"{self._AUTH_URL}?{urllib.parse.urlencode(params)}"
3132

@@ -38,6 +39,7 @@ def _oauth_get_credentials(
3839
code = request.args.get("code")
3940
if not code:
4041
raise ToolProviderOAuthError("No code provided")
42+
# Optionally: validate state here
4143

4244
data = {
4345
"client_id": system_credentials["client_id"],
@@ -57,6 +59,10 @@ def _oauth_get_credentials(
5759
def _oauth_refresh_credentials(
5860
self, redirect_uri: str, system_credentials: Mapping[str, Any], credentials: Mapping[str, Any]
5961
) -> ToolOAuthCredentials:
62+
"""
63+
Refresh the credentials
64+
"""
65+
# TODO: Implement the refresh credentials logic
6066
return ToolOAuthCredentials(credentials=credentials, expires_at=-1)
6167

6268
def _validate_credentials(self, credentials: dict) -> None:

tools/github/provider/github.yaml

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,42 @@ credentials_for_provider:
1818

1919
oauth_schema:
2020
client_schema:
21-
- name: "client_id"
22-
type: "secret-input"
23-
required: true
24-
url: https://github.com/settings/applications/new
25-
placeholder:
26-
en_US: "Please input your Client ID"
27-
zh_Hans: "请输入你的 Client ID"
28-
pt_BR: "Insira seu Client ID"
29-
help:
30-
en_US: "Client ID is used to authenticate requests to the GitHub API."
31-
zh_Hans: "Client ID 用于认证请求到 GitHub API。"
32-
pt_BR: "Client ID é usado para autenticar solicitações à API do GitHub."
33-
label:
34-
zh_Hans: "Client ID"
35-
en_US: "Client ID"
36-
- name: "client_secret"
37-
type: "secret-input"
38-
required: true
39-
url: https://github.com/settings/applications/new
40-
placeholder:
41-
en_US: "Please input your Client Secret"
42-
zh_Hans: "请输入你的 Client Secret"
43-
pt_BR: "Insira seu Client Secret"
44-
help:
45-
en_US: "Client Secret is used to authenticate requests to the GitHub API."
46-
zh_Hans: "Client Secret 用于认证请求到 GitHub API。"
47-
pt_BR: "Client Secret é usado para autenticar solicitações à API do GitHub."
48-
label:
49-
zh_Hans: "Client Secret"
50-
en_US: "Client Secret"
21+
- name: "client_id"
22+
type: "secret-input"
23+
required: true
24+
url: https://github.com/settings/applications/new
25+
placeholder:
26+
en_US: "Please input your Client ID"
27+
zh_Hans: "请输入你的 Client ID"
28+
pt_BR: "Insira seu Client ID"
29+
help:
30+
en_US: "Client ID is used to authenticate requests to the GitHub API."
31+
zh_Hans: "Client ID 用于认证请求到 GitHub API。"
32+
pt_BR: "Client ID é usado para autenticar solicitações à API do GitHub."
33+
label:
34+
zh_Hans: "Client ID"
35+
en_US: "Client ID"
36+
- name: "client_secret"
37+
type: "secret-input"
38+
required: true
39+
url: https://github.com/settings/applications/new
40+
placeholder:
41+
en_US: "Please input your Client Secret"
42+
zh_Hans: "请输入你的 Client Secret"
43+
pt_BR: "Insira seu Client Secret"
44+
help:
45+
en_US: "Client Secret is used to authenticate requests to the GitHub API."
46+
zh_Hans: "Client Secret 用于认证请求到 GitHub API。"
47+
pt_BR: "Client Secret é usado para autenticar solicitações à API do GitHub."
48+
label:
49+
zh_Hans: "Client Secret"
50+
en_US: "Client Secret"
5151
credentials_schema:
52-
- name: "access_tokens"
53-
type: "secret-input"
54-
label:
55-
zh_Hans: "Access Token"
56-
en_US: "Access Token"
52+
- name: "access_tokens"
53+
type: "secret-input"
54+
label:
55+
zh_Hans: "Access Token"
56+
en_US: "Access Token"
5757

5858
extra:
5959
python:
@@ -62,9 +62,7 @@ identity:
6262
author: langgenius
6363
description:
6464
en_US: GitHub is an online software source code hosting service.
65-
pt_BR:
66-
GitHub é uma plataforma online para serviços de hospedagem de código fonte
67-
de software.
65+
pt_BR: GitHub é uma plataforma online para serviços de hospedagem de código fonte de software.
6866
zh_Hans: GitHub 是一个在线软件源代码托管服务平台。
6967
icon: icon.svg
7068
label:
@@ -73,17 +71,28 @@ identity:
7371
zh_Hans: GitHub
7472
name: github
7573
tags:
76-
- utilities
74+
- utilities
7775
tools:
78-
- tools/github_repositories.yaml
79-
- tools/github_repository_readme.yaml
80-
- tools/github_repository_info.yaml
81-
- tools/github_repository_issues.yaml
82-
- tools/github_repository_pulls.yaml
83-
- tools/github_repository_releases.yaml
84-
- tools/github_repository_contributors.yaml
85-
- tools/github_repository_commits.yaml
86-
- tools/github_repository_contents.yaml
87-
- tools/github_user_info.yaml
88-
- tools/github_user_repos.yaml
89-
- tools/github_search_code.yaml
76+
- tools/github_repositories.yaml
77+
- tools/github_repository_readme.yaml
78+
- tools/github_repository_info.yaml
79+
- tools/github_repository_issues.yaml
80+
- tools/github_repository_pulls.yaml
81+
- tools/github_repository_releases.yaml
82+
- tools/github_repository_contributors.yaml
83+
- tools/github_repository_commits.yaml
84+
- tools/github_repository_contents.yaml
85+
- tools/github_user_info.yaml
86+
- tools/github_user_repos.yaml
87+
- tools/github_search_code.yaml
88+
# Pull Request tools
89+
- tools/github_pull_detail.yaml
90+
- tools/github_pull_files.yaml
91+
- tools/github_pull_comments.yaml
92+
- tools/github_pull_reviews.yaml
93+
- tools/github_create_pull.yaml
94+
- tools/github_merge_pull.yaml
95+
- tools/github_update_pull.yaml
96+
- tools/github_create_pull_review.yaml
97+
# Issue tools
98+
- tools/github_issue_comment.yaml

tools/github/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
dify_plugin==0.4.2
1+
dify_plugin==0.6.0b10
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import json
2+
from collections.abc import Generator
3+
from datetime import datetime
4+
from typing import Any
5+
6+
import requests
7+
8+
from dify_plugin import Tool
9+
from dify_plugin.entities.provider_config import CredentialType
10+
from dify_plugin.entities.tool import ToolInvokeMessage
11+
from dify_plugin.errors.model import InvokeError
12+
13+
from .github_error_handler import handle_github_api_error
14+
15+
16+
class GithubCreatePullTool(Tool):
17+
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage, None, None]:
18+
"""
19+
Create a new pull request
20+
"""
21+
owner = tool_parameters.get("owner", "")
22+
repo = tool_parameters.get("repo", "")
23+
title = tool_parameters.get("title", "")
24+
head = tool_parameters.get("head", "")
25+
base = tool_parameters.get("base", "")
26+
body = tool_parameters.get("body", "")
27+
draft = tool_parameters.get("draft", False)
28+
maintainer_can_modify = tool_parameters.get("maintainer_can_modify", True)
29+
credential_type = self.runtime.credential_type
30+
31+
if not owner:
32+
yield self.create_text_message("Please input owner")
33+
return
34+
if not repo:
35+
yield self.create_text_message("Please input repo")
36+
return
37+
if not title:
38+
yield self.create_text_message("Please input title")
39+
return
40+
if not head:
41+
yield self.create_text_message("Please input head branch")
42+
return
43+
if not base:
44+
yield self.create_text_message("Please input base branch")
45+
return
46+
47+
if credential_type == CredentialType.API_KEY and "access_tokens" not in self.runtime.credentials:
48+
yield self.create_text_message("GitHub API Access Tokens is required.")
49+
return
50+
51+
if credential_type == CredentialType.OAUTH and "access_tokens" not in self.runtime.credentials:
52+
yield self.create_text_message("GitHub OAuth Access Tokens is required.")
53+
return
54+
55+
access_token = self.runtime.credentials.get("access_tokens")
56+
try:
57+
headers = {
58+
"Content-Type": "application/vnd.github+json",
59+
"Authorization": f"Bearer {access_token}",
60+
"X-GitHub-Api-Version": "2022-11-28",
61+
}
62+
s = requests.session()
63+
api_domain = "https://api.github.com"
64+
url = f"{api_domain}/repos/{owner}/{repo}/pulls"
65+
66+
payload = {
67+
"title": title,
68+
"head": head,
69+
"base": base,
70+
"draft": draft,
71+
"maintainer_can_modify": maintainer_can_modify,
72+
}
73+
if body:
74+
payload["body"] = body
75+
76+
response = s.request(method="POST", headers=headers, url=url, json=payload)
77+
78+
if response.status_code == 201:
79+
pull = response.json()
80+
81+
result = {
82+
"success": True,
83+
"number": pull.get("number", 0),
84+
"title": pull.get("title", ""),
85+
"state": pull.get("state", ""),
86+
"url": pull.get("html_url", ""),
87+
"draft": pull.get("draft", False),
88+
"head": {
89+
"ref": pull.get("head", {}).get("ref", ""),
90+
"sha": pull.get("head", {}).get("sha", "")[:7] if pull.get("head", {}).get("sha") else "",
91+
},
92+
"base": {
93+
"ref": pull.get("base", {}).get("ref", ""),
94+
},
95+
"created_at": datetime.strptime(
96+
pull.get("created_at", ""), "%Y-%m-%dT%H:%M:%SZ"
97+
).strftime("%Y-%m-%d %H:%M:%S") if pull.get("created_at") else "",
98+
}
99+
100+
s.close()
101+
yield self.create_text_message(json.dumps(result, ensure_ascii=False, indent=2))
102+
else:
103+
handle_github_api_error(response, f"create pull request in {owner}/{repo}")
104+
except InvokeError as e:
105+
yield self.create_text_message(f"❌ {str(e)}")
106+
except Exception as e:
107+
yield self.create_text_message(f"❌ GitHub API request failed: {e}")

0 commit comments

Comments
 (0)