mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-18 03:45:55 +08:00
Update Gitlab query field, add query by path (#8244)
This commit is contained in:
parent
c5b3777d93
commit
75c1a82556
@ -1,4 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
|
import urllib.parse
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Union
|
from typing import Any, Union
|
||||||
|
|
||||||
@ -13,13 +14,14 @@ class GitlabCommitsTool(BuiltinTool):
|
|||||||
self, user_id: str, tool_parameters: dict[str, Any]
|
self, user_id: str, tool_parameters: dict[str, Any]
|
||||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||||
project = tool_parameters.get("project", "")
|
project = tool_parameters.get("project", "")
|
||||||
|
repository = tool_parameters.get("repository", "")
|
||||||
employee = tool_parameters.get("employee", "")
|
employee = tool_parameters.get("employee", "")
|
||||||
start_time = tool_parameters.get("start_time", "")
|
start_time = tool_parameters.get("start_time", "")
|
||||||
end_time = tool_parameters.get("end_time", "")
|
end_time = tool_parameters.get("end_time", "")
|
||||||
change_type = tool_parameters.get("change_type", "all")
|
change_type = tool_parameters.get("change_type", "all")
|
||||||
|
|
||||||
if not project:
|
if not project and not repository:
|
||||||
return self.create_text_message("Project is required")
|
return self.create_text_message("Either project or repository is required")
|
||||||
|
|
||||||
if not start_time:
|
if not start_time:
|
||||||
start_time = (datetime.utcnow() - timedelta(days=1)).isoformat()
|
start_time = (datetime.utcnow() - timedelta(days=1)).isoformat()
|
||||||
@ -35,41 +37,53 @@ class GitlabCommitsTool(BuiltinTool):
|
|||||||
site_url = "https://gitlab.com"
|
site_url = "https://gitlab.com"
|
||||||
|
|
||||||
# Get commit content
|
# Get commit content
|
||||||
result = self.fetch(user_id, site_url, access_token, project, employee, start_time, end_time, change_type)
|
if repository:
|
||||||
|
result = self.fetch_commits(
|
||||||
|
site_url, access_token, repository, employee, start_time, end_time, change_type, is_repository=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = self.fetch_commits(
|
||||||
|
site_url, access_token, project, employee, start_time, end_time, change_type, is_repository=False
|
||||||
|
)
|
||||||
|
|
||||||
return [self.create_json_message(item) for item in result]
|
return [self.create_json_message(item) for item in result]
|
||||||
|
|
||||||
def fetch(
|
def fetch_commits(
|
||||||
self,
|
self,
|
||||||
user_id: str,
|
|
||||||
site_url: str,
|
site_url: str,
|
||||||
access_token: str,
|
access_token: str,
|
||||||
project: str,
|
identifier: str,
|
||||||
employee: str = None,
|
employee: str,
|
||||||
start_time: str = "",
|
start_time: str,
|
||||||
end_time: str = "",
|
end_time: str,
|
||||||
change_type: str = "",
|
change_type: str,
|
||||||
|
is_repository: bool,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
domain = site_url
|
domain = site_url
|
||||||
headers = {"PRIVATE-TOKEN": access_token}
|
headers = {"PRIVATE-TOKEN": access_token}
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get all of projects
|
if is_repository:
|
||||||
|
# URL encode the repository path
|
||||||
|
encoded_identifier = urllib.parse.quote(identifier, safe="")
|
||||||
|
commits_url = f"{domain}/api/v4/projects/{encoded_identifier}/repository/commits"
|
||||||
|
else:
|
||||||
|
# Get all projects
|
||||||
url = f"{domain}/api/v4/projects"
|
url = f"{domain}/api/v4/projects"
|
||||||
response = requests.get(url, headers=headers)
|
response = requests.get(url, headers=headers)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
projects = response.json()
|
projects = response.json()
|
||||||
|
|
||||||
filtered_projects = [p for p in projects if project == "*" or p["name"] == project]
|
filtered_projects = [p for p in projects if identifier == "*" or p["name"] == identifier]
|
||||||
|
|
||||||
for project in filtered_projects:
|
for project in filtered_projects:
|
||||||
project_id = project["id"]
|
project_id = project["id"]
|
||||||
project_name = project["name"]
|
project_name = project["name"]
|
||||||
print(f"Project: {project_name}")
|
print(f"Project: {project_name}")
|
||||||
|
|
||||||
# Get all of project commits
|
|
||||||
commits_url = f"{domain}/api/v4/projects/{project_id}/repository/commits"
|
commits_url = f"{domain}/api/v4/projects/{project_id}/repository/commits"
|
||||||
|
|
||||||
params = {"since": start_time, "until": end_time}
|
params = {"since": start_time, "until": end_time}
|
||||||
if employee:
|
if employee:
|
||||||
params["author"] = employee
|
params["author"] = employee
|
||||||
@ -82,13 +96,17 @@ class GitlabCommitsTool(BuiltinTool):
|
|||||||
commit_sha = commit["id"]
|
commit_sha = commit["id"]
|
||||||
author_name = commit["author_name"]
|
author_name = commit["author_name"]
|
||||||
|
|
||||||
|
if is_repository:
|
||||||
|
diff_url = f"{domain}/api/v4/projects/{encoded_identifier}/repository/commits/{commit_sha}/diff"
|
||||||
|
else:
|
||||||
diff_url = f"{domain}/api/v4/projects/{project_id}/repository/commits/{commit_sha}/diff"
|
diff_url = f"{domain}/api/v4/projects/{project_id}/repository/commits/{commit_sha}/diff"
|
||||||
|
|
||||||
diff_response = requests.get(diff_url, headers=headers)
|
diff_response = requests.get(diff_url, headers=headers)
|
||||||
diff_response.raise_for_status()
|
diff_response.raise_for_status()
|
||||||
diffs = diff_response.json()
|
diffs = diff_response.json()
|
||||||
|
|
||||||
for diff in diffs:
|
for diff in diffs:
|
||||||
# Calculate code lines of changed
|
# Calculate code lines of changes
|
||||||
added_lines = diff["diff"].count("\n+")
|
added_lines = diff["diff"].count("\n+")
|
||||||
removed_lines = diff["diff"].count("\n-")
|
removed_lines = diff["diff"].count("\n-")
|
||||||
total_changes = added_lines + removed_lines
|
total_changes = added_lines + removed_lines
|
||||||
@ -102,9 +120,7 @@ class GitlabCommitsTool(BuiltinTool):
|
|||||||
if line.startswith("+") and not line.startswith("+++")
|
if line.startswith("+") and not line.startswith("+++")
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
results.append(
|
results.append({"commit_sha": commit_sha, "author_name": author_name, "diff": final_code})
|
||||||
{"commit_sha": commit_sha, "author_name": author_name, "diff": final_code}
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if total_changes > 1:
|
if total_changes > 1:
|
||||||
final_code = "".join(
|
final_code = "".join(
|
||||||
|
@ -21,9 +21,20 @@ parameters:
|
|||||||
zh_Hans: 员工用户名
|
zh_Hans: 员工用户名
|
||||||
llm_description: User name for GitLab
|
llm_description: User name for GitLab
|
||||||
form: llm
|
form: llm
|
||||||
|
- name: repository
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
label:
|
||||||
|
en_US: repository
|
||||||
|
zh_Hans: 仓库路径
|
||||||
|
human_description:
|
||||||
|
en_US: repository
|
||||||
|
zh_Hans: 仓库路径,以namespace/project_name的形式。
|
||||||
|
llm_description: Repository path for GitLab, like namespace/project_name.
|
||||||
|
form: llm
|
||||||
- name: project
|
- name: project
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: false
|
||||||
label:
|
label:
|
||||||
en_US: project
|
en_US: project
|
||||||
zh_Hans: 项目名
|
zh_Hans: 项目名
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import urllib.parse
|
||||||
from typing import Any, Union
|
from typing import Any, Union
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@ -11,14 +12,14 @@ class GitlabFilesTool(BuiltinTool):
|
|||||||
self, user_id: str, tool_parameters: dict[str, Any]
|
self, user_id: str, tool_parameters: dict[str, Any]
|
||||||
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||||
project = tool_parameters.get("project", "")
|
project = tool_parameters.get("project", "")
|
||||||
|
repository = tool_parameters.get("repository", "")
|
||||||
branch = tool_parameters.get("branch", "")
|
branch = tool_parameters.get("branch", "")
|
||||||
path = tool_parameters.get("path", "")
|
path = tool_parameters.get("path", "")
|
||||||
|
|
||||||
if not project:
|
if not project and not repository:
|
||||||
return self.create_text_message("Project is required")
|
return self.create_text_message("Either project or repository is required")
|
||||||
if not branch:
|
if not branch:
|
||||||
return self.create_text_message("Branch is required")
|
return self.create_text_message("Branch is required")
|
||||||
|
|
||||||
if not path:
|
if not path:
|
||||||
return self.create_text_message("Path is required")
|
return self.create_text_message("Path is required")
|
||||||
|
|
||||||
@ -30,21 +31,59 @@ class GitlabFilesTool(BuiltinTool):
|
|||||||
if "site_url" not in self.runtime.credentials or not self.runtime.credentials.get("site_url"):
|
if "site_url" not in self.runtime.credentials or not self.runtime.credentials.get("site_url"):
|
||||||
site_url = "https://gitlab.com"
|
site_url = "https://gitlab.com"
|
||||||
|
|
||||||
# Get project ID from project name
|
# Get file content
|
||||||
project_id = self.get_project_id(site_url, access_token, project)
|
if repository:
|
||||||
if not project_id:
|
result = self.fetch_files(site_url, access_token, repository, branch, path, is_repository=True)
|
||||||
return self.create_text_message(f"Project '{project}' not found.")
|
else:
|
||||||
|
result = self.fetch_files(site_url, access_token, project, branch, path, is_repository=False)
|
||||||
# Get commit content
|
|
||||||
result = self.fetch(user_id, project_id, site_url, access_token, branch, path)
|
|
||||||
|
|
||||||
return [self.create_json_message(item) for item in result]
|
return [self.create_json_message(item) for item in result]
|
||||||
|
|
||||||
def extract_project_name_and_path(self, path: str) -> tuple[str, str]:
|
def fetch_files(
|
||||||
parts = path.split("/", 1)
|
self, site_url: str, access_token: str, identifier: str, branch: str, path: str, is_repository: bool
|
||||||
if len(parts) < 2:
|
) -> list[dict[str, Any]]:
|
||||||
return None, None
|
domain = site_url
|
||||||
return parts[0], parts[1]
|
headers = {"PRIVATE-TOKEN": access_token}
|
||||||
|
results = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
if is_repository:
|
||||||
|
# URL encode the repository path
|
||||||
|
encoded_identifier = urllib.parse.quote(identifier, safe="")
|
||||||
|
tree_url = f"{domain}/api/v4/projects/{encoded_identifier}/repository/tree?path={path}&ref={branch}"
|
||||||
|
else:
|
||||||
|
# Get project ID from project name
|
||||||
|
project_id = self.get_project_id(site_url, access_token, identifier)
|
||||||
|
if not project_id:
|
||||||
|
return self.create_text_message(f"Project '{identifier}' not found.")
|
||||||
|
tree_url = f"{domain}/api/v4/projects/{project_id}/repository/tree?path={path}&ref={branch}"
|
||||||
|
|
||||||
|
response = requests.get(tree_url, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
items = response.json()
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
item_path = item["path"]
|
||||||
|
if item["type"] == "tree": # It's a directory
|
||||||
|
results.extend(
|
||||||
|
self.fetch_files(site_url, access_token, identifier, branch, item_path, is_repository)
|
||||||
|
)
|
||||||
|
else: # It's a file
|
||||||
|
if is_repository:
|
||||||
|
file_url = f"{domain}/api/v4/projects/{encoded_identifier}/repository/files/{item_path}/raw?ref={branch}"
|
||||||
|
else:
|
||||||
|
file_url = (
|
||||||
|
f"{domain}/api/v4/projects/{project_id}/repository/files/{item_path}/raw?ref={branch}"
|
||||||
|
)
|
||||||
|
|
||||||
|
file_response = requests.get(file_url, headers=headers)
|
||||||
|
file_response.raise_for_status()
|
||||||
|
file_content = file_response.text
|
||||||
|
results.append({"path": item_path, "branch": branch, "content": file_content})
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f"Error fetching data from GitLab: {e}")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
def get_project_id(self, site_url: str, access_token: str, project_name: str) -> Union[str, None]:
|
def get_project_id(self, site_url: str, access_token: str, project_name: str) -> Union[str, None]:
|
||||||
headers = {"PRIVATE-TOKEN": access_token}
|
headers = {"PRIVATE-TOKEN": access_token}
|
||||||
@ -59,32 +98,3 @@ class GitlabFilesTool(BuiltinTool):
|
|||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
print(f"Error fetching project ID from GitLab: {e}")
|
print(f"Error fetching project ID from GitLab: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def fetch(
|
|
||||||
self, user_id: str, project_id: str, site_url: str, access_token: str, branch: str, path: str = None
|
|
||||||
) -> list[dict[str, Any]]:
|
|
||||||
domain = site_url
|
|
||||||
headers = {"PRIVATE-TOKEN": access_token}
|
|
||||||
results = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
# List files and directories in the given path
|
|
||||||
url = f"{domain}/api/v4/projects/{project_id}/repository/tree?path={path}&ref={branch}"
|
|
||||||
response = requests.get(url, headers=headers)
|
|
||||||
response.raise_for_status()
|
|
||||||
items = response.json()
|
|
||||||
|
|
||||||
for item in items:
|
|
||||||
item_path = item["path"]
|
|
||||||
if item["type"] == "tree": # It's a directory
|
|
||||||
results.extend(self.fetch(project_id, site_url, access_token, branch, item_path))
|
|
||||||
else: # It's a file
|
|
||||||
file_url = f"{domain}/api/v4/projects/{project_id}/repository/files/{item_path}/raw?ref={branch}"
|
|
||||||
file_response = requests.get(file_url, headers=headers)
|
|
||||||
file_response.raise_for_status()
|
|
||||||
file_content = file_response.text
|
|
||||||
results.append({"path": item_path, "branch": branch, "content": file_content})
|
|
||||||
except requests.RequestException as e:
|
|
||||||
print(f"Error fetching data from GitLab: {e}")
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
@ -10,9 +10,20 @@ description:
|
|||||||
zh_Hans: 一个用于查询 GitLab 文件的工具,输入的内容应该是分支和一个已存在文件或者文件夹路径。
|
zh_Hans: 一个用于查询 GitLab 文件的工具,输入的内容应该是分支和一个已存在文件或者文件夹路径。
|
||||||
llm: A tool for query GitLab files, Input should be a exists file or directory path.
|
llm: A tool for query GitLab files, Input should be a exists file or directory path.
|
||||||
parameters:
|
parameters:
|
||||||
|
- name: repository
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
label:
|
||||||
|
en_US: repository
|
||||||
|
zh_Hans: 仓库路径
|
||||||
|
human_description:
|
||||||
|
en_US: repository
|
||||||
|
zh_Hans: 仓库路径,以namespace/project_name的形式。
|
||||||
|
llm_description: Repository path for GitLab, like namespace/project_name.
|
||||||
|
form: llm
|
||||||
- name: project
|
- name: project
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: false
|
||||||
label:
|
label:
|
||||||
en_US: project
|
en_US: project
|
||||||
zh_Hans: 项目
|
zh_Hans: 项目
|
||||||
|
Loading…
x
Reference in New Issue
Block a user