From ea1d9c724281251e4d868017d81e2cfae310eca6 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 22 May 2025 15:05:25 +0800 Subject: [PATCH 1/2] feat: Add GitHub repository API support for work reports - Add GitHub repository detection and API functions - Implement repo_issues and repo_commits for GitHub repos - Update work_report command to support both GitHub and GitLab --- community/github/git_api.py | 134 +++++++++++++++++++++++++++++++ community/work_report/command.py | 25 ++++-- 2 files changed, 152 insertions(+), 7 deletions(-) diff --git a/community/github/git_api.py b/community/github/git_api.py index ce54a29..dfb941f 100644 --- a/community/github/git_api.py +++ b/community/github/git_api.py @@ -41,9 +41,12 @@ def get_current_repo(): if not current_repo_dir: selected_data = IDEService().get_selected_range().dict() current_file = selected_data.get("abspath", None) + if not current_file: return None current_dir = os.path.dirname(current_file) + if not os.path.exists(current_dir): + current_dir = os.getcwd() try: # 获取仓库根目录 current_repo_dir = ( @@ -585,3 +588,134 @@ def get_github_repo_issues( return response.json() else: return None + +def get_repo_issues( + repo: str, + assignee_username: str = None, + state: str = "open", + created_after=None, + created_before=None, +): + """ + Get issues from a GitHub repository with optional filtering. + + Args: + repo (str): Repository name in the format 'owner/repo' + assignee_username (str, optional): Filter issues by assignee username. + Special values: '*' (any assigned), 'none' (unassigned) + state (str, optional): Filter issues by state ('open', 'closed', 'all'). Default is 'open'. + created_after (str, optional): ISO 8601 formatted timestamp to filter issues created after this date + created_before (str, optional): ISO 8601 formatted timestamp to filter issues created before this date + + Returns: + list: List of issue objects from the GitHub API + """ + url = f"{GITHUB_API_URL}/repos/{repo}/issues" + + params = { + "state": state, + } + + # 处理assignee参数 + if assignee_username is not None: + # 如果提供了assignee_username,则使用它 + params["assignee"] = assignee_username + + # GitHub uses 'since' parameter for created_after + if created_after: + params["since"] = created_after + + headers = { + "Authorization": f"token {GITHUB_ACCESS_TOKEN}", + "Accept": "application/vnd.github.v3+json", + } + + response = requests.get(url, headers=headers, params=params) + + if response.status_code == 200: + issues = response.json() + + # Filter by created_before if specified + if created_before: + issues = [issue for issue in issues if issue["created_at"][:len(created_before)] <= created_before] + + return issues + else: + print(f"Failed to get issues: {response.status_code}", file=sys.stderr) + print(response.text, file=sys.stderr) + return [] + + +def get_commit_author(): + cmd = ["git", "config", "user.email"] + return subprocess_check_output(cmd).decode("utf-8").strip() + +def get_repo_commits(repo: str, author=None, since=None, until=None): + """ + Get commits from a GitHub repository with optional filtering. + + Args: + repo (str): Repository name in the format 'owner/repo' + author (str, optional): Filter commits by author + since (str, optional): ISO 8601 formatted timestamp to filter commits after this date + until (str, optional): ISO 8601 formatted timestamp to filter commits before this date + + Returns: + list: List of commit objects from the GitHub API + """ + url = f"{GITHUB_API_URL}/repos/{repo}/commits" + + params = {} + if author: + params["author"] = author + if since: + params["since"] = since + if until: + params["until"] = until + + headers = { + "Authorization": f"token {GITHUB_ACCESS_TOKEN}", + "Accept": "application/vnd.github.v3+json", + } + + response = requests.get(url, headers=headers, params=params) + + if response.status_code == 200: + return response.json() + else: + print(f"Failed to get commits: {response.status_code}") + print(response.text) + return None + +def is_github_repo(): + """ + Check if the current repository is a GitHub repository. + + Tries to determine if the current git repository is hosted on GitHub by + examining the remote URL. + + Returns: + bool: True if the repository is hosted on GitHub, False otherwise. + """ + try: + # 使用git命令获取当前仓库的URL + result = subprocess_check_output( + ["git", "remote", "get-url", "origin"], stderr=subprocess.STDOUT + ).strip() + + # 将结果从bytes转换为str + repo_url = result.decode("utf-8") + + # 检查URL是否包含github.com + is_github = "github.com" in repo_url.lower() + + IDEService().ide_logging("debug", f"Repository is GitHub: {is_github}") + return is_github + except subprocess.CalledProcessError as e: + print(e, flush=True) + # 如果发生错误,打印错误信息 + return None + except FileNotFoundError: + # 如果未找到git命令,可能是没有安装git或者不在PATH中 + print("==> File not found...", flush=True) + return None \ No newline at end of file diff --git a/community/work_report/command.py b/community/work_report/command.py index bcf2d5f..b7eb8db 100755 --- a/community/work_report/command.py +++ b/community/work_report/command.py @@ -3,13 +3,24 @@ from devchat.llm import chat, chat_json -from community.gitlab.git_api import ( - get_commit_author, - get_repo, - get_repo_commits, - get_repo_issues, - get_username, -) +from community.github.git_api import is_github_repo + +if is_github_repo(): + from community.github.git_api import ( + get_commit_author, + get_github_repo as get_repo, + get_repo_commits, + get_repo_issues, + get_github_username as get_username, + ) +else: + from community.gitlab.git_api import ( + get_commit_author, + get_repo, + get_repo_commits, + get_repo_issues, + get_username, + ) from lib.workflow.decorators import check_input From e19910ffe2ef6c86e4e86aa1a09b5fd8979e6e93 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 22 May 2025 15:13:40 +0800 Subject: [PATCH 2/2] fix lint error --- community/github/git_api.py | 59 ++++++++++++++++++-------------- community/work_report/command.py | 6 +++- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/community/github/git_api.py b/community/github/git_api.py index dfb941f..e22234b 100644 --- a/community/github/git_api.py +++ b/community/github/git_api.py @@ -589,6 +589,7 @@ def get_github_repo_issues( else: return None + def get_repo_issues( repo: str, assignee_username: str = None, @@ -598,47 +599,53 @@ def get_repo_issues( ): """ Get issues from a GitHub repository with optional filtering. - + Args: repo (str): Repository name in the format 'owner/repo' assignee_username (str, optional): Filter issues by assignee username. Special values: '*' (any assigned), 'none' (unassigned) state (str, optional): Filter issues by state ('open', 'closed', 'all'). Default is 'open'. - created_after (str, optional): ISO 8601 formatted timestamp to filter issues created after this date - created_before (str, optional): ISO 8601 formatted timestamp to filter issues created before this date - + created_after (str, optional): ISO 8601 formatted timestamp to filter issues created after + this date + created_before (str, optional): ISO 8601 formatted timestamp to filter issues created + before this date + Returns: list: List of issue objects from the GitHub API """ url = f"{GITHUB_API_URL}/repos/{repo}/issues" - + params = { "state": state, } - + # 处理assignee参数 if assignee_username is not None: # 如果提供了assignee_username,则使用它 params["assignee"] = assignee_username - + # GitHub uses 'since' parameter for created_after if created_after: params["since"] = created_after - + headers = { "Authorization": f"token {GITHUB_ACCESS_TOKEN}", "Accept": "application/vnd.github.v3+json", } - + response = requests.get(url, headers=headers, params=params) - + if response.status_code == 200: issues = response.json() - + # Filter by created_before if specified if created_before: - issues = [issue for issue in issues if issue["created_at"][:len(created_before)] <= created_before] - + issues = [ + issue + for issue in issues + if issue["created_at"][: len(created_before)] <= created_before + ] + return issues else: print(f"Failed to get issues: {response.status_code}", file=sys.stderr) @@ -650,21 +657,22 @@ def get_commit_author(): cmd = ["git", "config", "user.email"] return subprocess_check_output(cmd).decode("utf-8").strip() + def get_repo_commits(repo: str, author=None, since=None, until=None): """ Get commits from a GitHub repository with optional filtering. - + Args: repo (str): Repository name in the format 'owner/repo' author (str, optional): Filter commits by author since (str, optional): ISO 8601 formatted timestamp to filter commits after this date until (str, optional): ISO 8601 formatted timestamp to filter commits before this date - + Returns: list: List of commit objects from the GitHub API """ url = f"{GITHUB_API_URL}/repos/{repo}/commits" - + params = {} if author: params["author"] = author @@ -672,14 +680,14 @@ def get_repo_commits(repo: str, author=None, since=None, until=None): params["since"] = since if until: params["until"] = until - + headers = { "Authorization": f"token {GITHUB_ACCESS_TOKEN}", "Accept": "application/vnd.github.v3+json", } - + response = requests.get(url, headers=headers, params=params) - + if response.status_code == 200: return response.json() else: @@ -687,13 +695,14 @@ def get_repo_commits(repo: str, author=None, since=None, until=None): print(response.text) return None + def is_github_repo(): """ Check if the current repository is a GitHub repository. - + Tries to determine if the current git repository is hosted on GitHub by examining the remote URL. - + Returns: bool: True if the repository is hosted on GitHub, False otherwise. """ @@ -702,13 +711,13 @@ def is_github_repo(): result = subprocess_check_output( ["git", "remote", "get-url", "origin"], stderr=subprocess.STDOUT ).strip() - + # 将结果从bytes转换为str repo_url = result.decode("utf-8") - + # 检查URL是否包含github.com is_github = "github.com" in repo_url.lower() - + IDEService().ide_logging("debug", f"Repository is GitHub: {is_github}") return is_github except subprocess.CalledProcessError as e: @@ -718,4 +727,4 @@ def is_github_repo(): except FileNotFoundError: # 如果未找到git命令,可能是没有安装git或者不在PATH中 print("==> File not found...", flush=True) - return None \ No newline at end of file + return None diff --git a/community/work_report/command.py b/community/work_report/command.py index b7eb8db..1d67a12 100755 --- a/community/work_report/command.py +++ b/community/work_report/command.py @@ -8,9 +8,13 @@ if is_github_repo(): from community.github.git_api import ( get_commit_author, - get_github_repo as get_repo, get_repo_commits, get_repo_issues, + ) + from community.github.git_api import ( + get_github_repo as get_repo, + ) + from community.github.git_api import ( get_github_username as get_username, ) else: