From 8eb291f918afffef2c579aa8ff4b0fa313d1099d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sat, 11 May 2024 10:44:52 +0800 Subject: [PATCH 1/2] feat: create_pull_url --- metagpt/tools/libs/git.py | 7 ++-- metagpt/utils/git_repository.py | 52 +++++++++++++++++++++------- tests/metagpt/tools/libs/test_git.py | 34 ++++++++++++++++++ 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/metagpt/tools/libs/git.py b/metagpt/tools/libs/git.py index aa701e49b..74d427e24 100644 --- a/metagpt/tools/libs/git.py +++ b/metagpt/tools/libs/git.py @@ -175,9 +175,12 @@ async def git_create_pull( >>> body=body, >>> access_token=access_token, >>> ) - >>> print(pr) + >>> if isinstance(pr, PullRequest): + >>> print(pr) PullRequest("feat: modify http lib") - + >>> elif isinstance(pr, str) + >>> print(f"Visit this url to create a new pull request: {pr}") + Visit this url to create a new pull request: https://github.com/geekan/MetaGPT/compare/master...iorisa:MetaGPT:dev Returns: PullRequest: The created pull request. diff --git a/metagpt/utils/git_repository.py b/metagpt/utils/git_repository.py index 3c45c4085..808ad3737 100644 --- a/metagpt/utils/git_repository.py +++ b/metagpt/utils/git_repository.py @@ -14,7 +14,7 @@ import uuid from enum import Enum from pathlib import Path from subprocess import TimeoutExpired -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Union from git.repo import Repo from git.repo.fun import is_git_dir @@ -456,7 +456,7 @@ class GitRepository: issue: Optional[Issue] = None, access_token: Optional[str] = None, auth: Optional[Auth] = None, - ) -> PullRequest: + ) -> Union[PullRequest, str]: """ Creates a pull request in the specified repository. @@ -505,17 +505,26 @@ class GitRepository: issue=issue, ) else: - head_branch = base_repo.get_branch(base) - base_branch = head_repo.get_branch(head) - pr = base_repo.create_pull( - base=base_branch.name, - head=head_branch.commit.sha, - title=title, - body=body, - maintainer_can_modify=maintainer_can_modify, - draft=draft, - issue=issue, - ) + base_branch = base_repo.get_branch(base) + head_branch = head_repo.get_branch(head) + try: + pr = base_repo.create_pull( + base=base_branch.name, + head=f"{head_repo.full_name}:{head_branch.name}", + title=title, + body=body, + maintainer_can_modify=maintainer_can_modify, + draft=draft, + issue=issue, + ) + except Exception as e: + logger.warning(f"Pull Request Error: {e}") + return GitRepository.create_pull_url( + clone_url=base_repo.clone_url, + base=base_branch.name, + head=head_branch.name, + head_repo_name=head_repo.full_name, + ) return pr @staticmethod @@ -594,3 +603,20 @@ class GitRepository: user = git.get_user() v = user.get_repos(visibility="public") return [i.full_name for i in v] + + @staticmethod + def create_pull_url(clone_url: str, base: str, head: str, head_repo_name: str = None) -> str: + """ + Create a URL for comparing changes between branches or repositories on GitHub. + + Args: + clone_url (str): The URL used for cloning the repository, ending with '.git'. + base (str): The base branch or commit. + head (str): The head branch or commit. + head_repo_name (str): The name of the repository for the head branch. If not provided, assumes the same repository. + + Returns: + str: The URL for comparing changes between the specified branches or commits. + """ + url = clone_url.removesuffix(".git") + f"/compare/{base}..." + head_repo_name.replace("/", ":") + ":" + head + return url diff --git a/tests/metagpt/tools/libs/test_git.py b/tests/metagpt/tools/libs/test_git.py index c96a3eb46..27ac3c234 100644 --- a/tests/metagpt/tools/libs/test_git.py +++ b/tests/metagpt/tools/libs/test_git.py @@ -91,6 +91,39 @@ async def test_new_pr(): assert pr +# @pytest.mark.skip +@pytest.mark.asyncio +async def test_new_pr1(): + body = """ + >>> SUMMARY + >>> Change HTTP library used to send requests + >>> + >>> TESTS + >>> - [x] Send 'GET' request + >>> - [x] Send 'POST' request with/without body + """ + pr = await GitRepository.create_pull( + base_repo_name="iorisa/MetaGPT", + base="fixbug/vscode", + head_repo_name="send18/MetaGPT", + head="dev", + title="Test pr", + body=body, + access_token=await get_env(key="access_token", app_name="github"), + ) + # pr = await GitRepository.create_pull( + # head_repo_name="iorisa/MetaGPT", + # head="fixbug/vscode", + # base_repo_name="send18/MetaGPT", + # base="dev", + # title="Test pr", + # body=body, + # access_token=await get_env(key="access_token", app_name="github"), + # ) + print(pr) + assert pr + + @pytest.mark.skip @pytest.mark.asyncio async def test_auth(): @@ -124,6 +157,7 @@ async def test_github(context): assert pr +@pytest.mark.skip @pytest.mark.asyncio @pytest.mark.parametrize( "content", From 97677f2ccaf3a74dd8274b65f912099f4810c9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sat, 11 May 2024 11:50:08 +0800 Subject: [PATCH 2/2] feat: create_github_pull_url --- metagpt/tools/libs/git.py | 12 ++-- metagpt/utils/git_repository.py | 89 +++++++++++++++++----------- tests/metagpt/tools/libs/test_git.py | 19 ++---- 3 files changed, 65 insertions(+), 55 deletions(-) diff --git a/metagpt/tools/libs/git.py b/metagpt/tools/libs/git.py index 74d427e24..44acb1bb0 100644 --- a/metagpt/tools/libs/git.py +++ b/metagpt/tools/libs/git.py @@ -154,8 +154,12 @@ async def git_create_pull( >>> body=body, >>> access_token=access_token, >>> ) - >>> print(pr) + >>> if isinstance(pr, PullRequest): + >>> print(pr) PullRequest("feat: modify http lib") + >>> if isinstance(pr, str): + >>> print(f"Visit this url to create a new pull request: '{pr}'") + Visit this url to create a new pull request: 'https://github.com/iorisa/snake-game/compare/master...feature/new' >>> # create pull request >>> base_repo_name = "geekan/MetaGPT" @@ -178,9 +182,9 @@ async def git_create_pull( >>> if isinstance(pr, PullRequest): >>> print(pr) PullRequest("feat: modify http lib") - >>> elif isinstance(pr, str) - >>> print(f"Visit this url to create a new pull request: {pr}") - Visit this url to create a new pull request: https://github.com/geekan/MetaGPT/compare/master...iorisa:MetaGPT:dev + >>> if isinstance(pr, str): + >>> print(f"Visit this url to create a new pull request: '{pr}'") + Visit this url to create a new pull request: 'https://github.com/geekan/MetaGPT/compare/master...iorisa:MetaGPT:feature/http' Returns: PullRequest: The created pull request. diff --git a/metagpt/utils/git_repository.py b/metagpt/utils/git_repository.py index 808ad3737..a078efe59 100644 --- a/metagpt/utils/git_repository.py +++ b/metagpt/utils/git_repository.py @@ -15,6 +15,7 @@ from enum import Enum from pathlib import Path from subprocess import TimeoutExpired from typing import Dict, List, Optional, Union +from urllib.parse import quote from git.repo import Repo from git.repo.fun import is_git_dir @@ -483,31 +484,26 @@ class GitRepository: issue = issue or NotSet if not auth and not access_token: raise ValueError('`access_token` is invalid. Visit: "https://github.com/settings/tokens"') - auth = auth or Auth.Token(access_token) - g = Github(auth=auth) - base_repo = g.get_repo(base_repo_name) - head_repo = g.get_repo(head_repo_name) if head_repo_name and head_repo_name != base_repo_name else None - x_ratelimit_remaining = base_repo.raw_headers.get("x-ratelimit-remaining") - if ( - x_ratelimit_remaining - and bool(re.match(r"^-?\d+$", x_ratelimit_remaining)) - and int(x_ratelimit_remaining) <= 0 - ): - raise RateLimitError() - if not head_repo: - pr = base_repo.create_pull( - base=base, - head=head, - title=title, - body=body, - maintainer_can_modify=maintainer_can_modify, - draft=draft, - issue=issue, - ) - else: - base_branch = base_repo.get_branch(base) - head_branch = head_repo.get_branch(head) - try: + clone_url = f"https://github.com/{base_repo_name}.git" + try: + auth = auth or Auth.Token(access_token) + g = Github(auth=auth) + base_repo = g.get_repo(base_repo_name) + clone_url = base_repo.clone_url + head_repo = g.get_repo(head_repo_name) if head_repo_name and head_repo_name != base_repo_name else None + if not head_repo: + pr = base_repo.create_pull( + base=base, + head=head, + title=title, + body=body, + maintainer_can_modify=maintainer_can_modify, + draft=draft, + issue=issue, + ) + else: + base_branch = base_repo.get_branch(base) + head_branch = head_repo.get_branch(head) pr = base_repo.create_pull( base=base_branch.name, head=f"{head_repo.full_name}:{head_branch.name}", @@ -517,14 +513,14 @@ class GitRepository: draft=draft, issue=issue, ) - except Exception as e: - logger.warning(f"Pull Request Error: {e}") - return GitRepository.create_pull_url( - clone_url=base_repo.clone_url, - base=base_branch.name, - head=head_branch.name, - head_repo_name=head_repo.full_name, - ) + except Exception as e: + logger.warning(f"Pull Request Error: {e}") + return GitRepository.create_github_pull_url( + clone_url=clone_url, + base=base, + head=head, + head_repo_name=head_repo_name, + ) return pr @staticmethod @@ -605,7 +601,7 @@ class GitRepository: return [i.full_name for i in v] @staticmethod - def create_pull_url(clone_url: str, base: str, head: str, head_repo_name: str = None) -> str: + def create_github_pull_url(clone_url: str, base: str, head: str, head_repo_name: Optional[str] = None) -> str: """ Create a URL for comparing changes between branches or repositories on GitHub. @@ -613,10 +609,31 @@ class GitRepository: clone_url (str): The URL used for cloning the repository, ending with '.git'. base (str): The base branch or commit. head (str): The head branch or commit. - head_repo_name (str): The name of the repository for the head branch. If not provided, assumes the same repository. + head_repo_name (str, optional): The name of the repository for the head branch. If not provided, assumes the same repository. Returns: str: The URL for comparing changes between the specified branches or commits. """ - url = clone_url.removesuffix(".git") + f"/compare/{base}..." + head_repo_name.replace("/", ":") + ":" + head + url = clone_url.removesuffix(".git") + f"/compare/{base}..." + if head_repo_name: + url += head_repo_name.replace("/", ":") + url += ":" + head return url + + @staticmethod + def create_gitlab_merge_request_url(clone_url: str, head: str) -> str: + """ + Create a URL for creating a new merge request on GitLab. + + Args: + clone_url (str): The URL used for cloning the repository, ending with '.git'. + head (str): The name of the branch to be merged. + + Returns: + str: The URL for creating a new merge request for the specified branch. + """ + return ( + clone_url.removesuffix(".git") + + "/-/merge_requests/new?merge_request%5Bsource_branch%5D=" + + quote(head, safe="") + ) diff --git a/tests/metagpt/tools/libs/test_git.py b/tests/metagpt/tools/libs/test_git.py index 27ac3c234..ad843a8a3 100644 --- a/tests/metagpt/tools/libs/test_git.py +++ b/tests/metagpt/tools/libs/test_git.py @@ -68,7 +68,6 @@ async def test_new_issue(): pass -@pytest.mark.skip @pytest.mark.asyncio async def test_new_pr(): body = """ @@ -91,7 +90,6 @@ async def test_new_pr(): assert pr -# @pytest.mark.skip @pytest.mark.asyncio async def test_new_pr1(): body = """ @@ -103,23 +101,14 @@ async def test_new_pr1(): >>> - [x] Send 'POST' request with/without body """ pr = await GitRepository.create_pull( - base_repo_name="iorisa/MetaGPT", - base="fixbug/vscode", - head_repo_name="send18/MetaGPT", - head="dev", + head_repo_name="iorisa/MetaGPT", + head="fixbug/vscode", + base_repo_name="send18/MetaGPT", + base="dev", title="Test pr", body=body, access_token=await get_env(key="access_token", app_name="github"), ) - # pr = await GitRepository.create_pull( - # head_repo_name="iorisa/MetaGPT", - # head="fixbug/vscode", - # base_repo_name="send18/MetaGPT", - # base="dev", - # title="Test pr", - # body=body, - # access_token=await get_env(key="access_token", app_name="github"), - # ) print(pr) assert pr