Add base_domain parameter for GitHub Enterprise support (#7754)

Co-authored-by: Tom Deckers <tdeckers@cisco.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Rohit Malhotra <rohitvinodmalhotra@gmail.com>
This commit is contained in:
Tom Deckers
2025-04-16 02:00:32 +02:00
committed by GitHub
parent d7e8f843ad
commit 7e14a512e0
8 changed files with 203 additions and 49 deletions

View File

@@ -12,11 +12,21 @@ from openhands.resolver.utils import extract_issue_references
class GithubIssueHandler(IssueHandlerInterface):
def __init__(self, owner: str, repo: str, token: str, username: str | None = None):
def __init__(self, owner: str, repo: str, token: str, username: str | None = None, base_domain: str = "github.com"):
"""Initialize a GitHub issue handler.
Args:
owner: The owner of the repository
repo: The name of the repository
token: The GitHub personal access token
username: Optional GitHub username
base_domain: The domain for GitHub Enterprise (default: "github.com")
"""
self.owner = owner
self.repo = repo
self.token = token
self.username = username
self.base_domain = base_domain
self.base_url = self.get_base_url()
self.download_url = self.get_download_url()
self.clone_url = self.get_clone_url()
@@ -32,10 +42,13 @@ class GithubIssueHandler(IssueHandlerInterface):
}
def get_base_url(self) -> str:
return f'https://api.github.com/repos/{self.owner}/{self.repo}'
if self.base_domain == "github.com":
return f'https://api.github.com/repos/{self.owner}/{self.repo}'
else:
return f'https://{self.base_domain}/api/v3/repos/{self.owner}/{self.repo}'
def get_authorize_url(self) -> str:
return f'https://{self.username}:{self.token}@github.com/'
return f'https://{self.username}:{self.token}@{self.base_domain}/'
def get_branch_url(self, branch_name: str) -> str:
return self.get_base_url() + f'/branches/{branch_name}'
@@ -49,13 +62,16 @@ class GithubIssueHandler(IssueHandlerInterface):
if self.username
else f'x-auth-token:{self.token}'
)
return f'https://{username_and_token}@github.com/{self.owner}/{self.repo}.git'
return f'https://{username_and_token}@{self.base_domain}/{self.owner}/{self.repo}.git'
def get_graphql_url(self) -> str:
return 'https://api.github.com/graphql'
if self.base_domain == "github.com":
return 'https://api.github.com/graphql'
else:
return f'https://{self.base_domain}/api/v3/graphql'
def get_compare_url(self, branch_name: str) -> str:
return f'https://github.com/{self.owner}/{self.repo}/compare/{branch_name}?expand=1'
return f'https://{self.base_domain}/{self.owner}/{self.repo}/compare/{branch_name}?expand=1'
def get_converted_issues(
self, issue_numbers: list[int] | None = None, comment_id: int | None = None
@@ -220,7 +236,7 @@ class GithubIssueHandler(IssueHandlerInterface):
response.raise_for_status()
def get_pull_url(self, pr_number: int) -> str:
return f'https://github.com/{self.owner}/{self.repo}/pull/{pr_number}'
return f'https://{self.base_domain}/{self.owner}/{self.repo}/pull/{pr_number}'
def get_default_branch_name(self) -> str:
response = httpx.get(f'{self.base_url}', headers=self.headers)
@@ -286,11 +302,21 @@ class GithubIssueHandler(IssueHandlerInterface):
class GithubPRHandler(GithubIssueHandler):
def __init__(self, owner: str, repo: str, token: str, username: str | None = None):
super().__init__(owner, repo, token, username)
self.download_url = (
f'https://api.github.com/repos/{self.owner}/{self.repo}/pulls'
)
def __init__(self, owner: str, repo: str, token: str, username: str | None = None, base_domain: str = "github.com"):
"""Initialize a GitHub PR handler.
Args:
owner: The owner of the repository
repo: The name of the repository
token: The GitHub personal access token
username: Optional GitHub username
base_domain: The domain for GitHub Enterprise (default: "github.com")
"""
super().__init__(owner, repo, token, username, base_domain)
if self.base_domain == "github.com":
self.download_url = f'https://api.github.com/repos/{self.owner}/{self.repo}/pulls'
else:
self.download_url = f'https://{self.base_domain}/api/v3/repos/{self.owner}/{self.repo}/pulls'
def download_pr_metadata(
self, pull_number: int, comment_id: int | None = None
@@ -356,7 +382,7 @@ class GithubPRHandler(GithubIssueHandler):
variables = {'owner': self.owner, 'repo': self.repo, 'pr': pull_number}
url = 'https://api.github.com/graphql'
url = self.get_graphql_url()
headers = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json',
@@ -444,7 +470,10 @@ class GithubPRHandler(GithubIssueHandler):
self, pr_number: int, comment_id: int | None = None
) -> list[str] | None:
"""Download comments for a specific pull request from Github."""
url = f'https://api.github.com/repos/{self.owner}/{self.repo}/issues/{pr_number}/comments'
if self.base_domain == "github.com":
url = f'https://api.github.com/repos/{self.owner}/{self.repo}/issues/{pr_number}/comments'
else:
url = f'https://{self.base_domain}/api/v3/repos/{self.owner}/{self.repo}/issues/{pr_number}/comments'
headers = {
'Authorization': f'token {self.token}',
'Accept': 'application/vnd.github.v3+json',
@@ -513,7 +542,10 @@ class GithubPRHandler(GithubIssueHandler):
for issue_number in unique_issue_references:
try:
url = f'https://api.github.com/repos/{self.owner}/{self.repo}/issues/{issue_number}'
if self.base_domain == "github.com":
url = f'https://api.github.com/repos/{self.owner}/{self.repo}/issues/{issue_number}'
else:
url = f'https://{self.base_domain}/api/v3/repos/{self.owner}/{self.repo}/issues/{issue_number}'
headers = {
'Authorization': f'Bearer {self.token}',
'Accept': 'application/vnd.github.v3+json',

View File

@@ -13,11 +13,28 @@ from openhands.resolver.utils import extract_issue_references
class GitlabIssueHandler(IssueHandlerInterface):
def __init__(self, owner: str, repo: str, token: str, username: str | None = None):
def __init__(
self,
owner: str,
repo: str,
token: str,
username: str | None = None,
base_domain: str = 'gitlab.com',
):
"""Initialize a GitLab issue handler.
Args:
owner: The owner of the repository
repo: The name of the repository
token: The GitLab personal access token
username: Optional GitLab username
base_domain: The domain for GitLab Enterprise (default: "gitlab.com")
"""
self.owner = owner
self.repo = repo
self.token = token
self.username = username
self.base_domain = base_domain
self.base_url = self.get_base_url()
self.download_url = self.get_download_url()
self.clone_url = self.get_clone_url()
@@ -34,10 +51,10 @@ class GitlabIssueHandler(IssueHandlerInterface):
def get_base_url(self) -> str:
project_path = quote(f'{self.owner}/{self.repo}', safe='')
return f'https://gitlab.com/api/v4/projects/{project_path}'
return f'https://{self.base_domain}/api/v4/projects/{project_path}'
def get_authorize_url(self) -> str:
return f'https://{self.username}:{self.token}@gitlab.com/'
return f'https://{self.username}:{self.token}@{self.base_domain}/'
def get_branch_url(self, branch_name: str) -> str:
return self.get_base_url() + f'/repository/branches/{branch_name}'
@@ -49,13 +66,13 @@ class GitlabIssueHandler(IssueHandlerInterface):
username_and_token = self.token
if self.username:
username_and_token = f'{self.username}:{self.token}'
return f'https://{username_and_token}@gitlab.com/{self.owner}/{self.repo}.git'
return f'https://{username_and_token}@{self.base_domain}/{self.owner}/{self.repo}.git'
def get_graphql_url(self) -> str:
return 'https://gitlab.com/api/graphql'
return f'https://{self.base_domain}/api/graphql'
def get_compare_url(self, branch_name: str) -> str:
return f'https://gitlab.com/{self.owner}/{self.repo}/-/compare/{self.get_default_branch_name()}...{branch_name}'
return f'https://{self.base_domain}/{self.owner}/{self.repo}/-/compare/{self.get_default_branch_name()}...{branch_name}'
def get_converted_issues(
self, issue_numbers: list[int] | None = None, comment_id: int | None = None
@@ -215,9 +232,7 @@ class GitlabIssueHandler(IssueHandlerInterface):
response.raise_for_status()
def get_pull_url(self, pr_number: int) -> str:
return (
f'https://gitlab.com/{self.owner}/{self.repo}/-/merge_requests/{pr_number}'
)
return f'https://{self.base_domain}/{self.owner}/{self.repo}/-/merge_requests/{pr_number}'
def get_default_branch_name(self) -> str:
response = httpx.get(f'{self.base_url}', headers=self.headers)
@@ -248,7 +263,7 @@ class GitlabIssueHandler(IssueHandlerInterface):
def request_reviewers(self, reviewer: str, pr_number: int) -> None:
response = httpx.get(
f'https://gitlab.com/api/v4/users?username={reviewer}',
f'https://{self.base_domain}/api/v4/users?username={reviewer}',
headers=self.headers,
)
response.raise_for_status()
@@ -298,8 +313,24 @@ class GitlabIssueHandler(IssueHandlerInterface):
class GitlabPRHandler(GitlabIssueHandler):
def __init__(self, owner: str, repo: str, token: str, username: str | None = None):
super().__init__(owner, repo, token, username)
def __init__(
self,
owner: str,
repo: str,
token: str,
username: str | None = None,
base_domain: str = 'gitlab.com',
):
"""Initialize a GitLab PR handler.
Args:
owner: The owner of the repository
repo: The name of the repository
token: The GitLab personal access token
username: Optional GitLab username
base_domain: The domain for GitLab Enterprise (default: "gitlab.com")
"""
super().__init__(owner, repo, token, username, base_domain)
self.download_url = f'{self.base_url}/merge_requests'
def download_pr_metadata(