Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions src/codegen/git/clients/git_repo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@

def __init__(self, repo_config: RepoConfig, access_token: str | None = None) -> None:
self.repo_config = repo_config
self.gh_client = self._create_github_client(token=access_token or SecretsConfig().github_token)

Check failure on line 36 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Argument "token" to "_create_github_client" of "GitRepoClient" has incompatible type "str | None"; expected "str" [arg-type]
self._repo = self._create_client()

def _create_github_client(self, token: str) -> GithubClient:
return GithubClient(token=token)

def _create_client(self) -> Repository:
client = self.gh_client.get_repo_by_full_name(self.repo_config.full_name)

Check failure on line 43 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Argument 1 to "get_repo_by_full_name" of "GithubClient" has incompatible type "str | None"; expected "str" [arg-type]
if not client:
msg = f"Repo {self.repo_config.full_name} not found!"
raise ValueError(msg)
Expand Down Expand Up @@ -176,9 +176,9 @@
def get_or_create_pull(
self,
head_branch_name: str,
base_branch_name: str | None = None, # type: ignore[assignment]

Check failure on line 179 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Unused "type: ignore" comment [unused-ignore]
title: str | None = None, # type: ignore[assignment]

Check failure on line 180 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Unused "type: ignore" comment [unused-ignore]
body: str | None = None, # type: ignore[assignment]

Check failure on line 181 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Unused "type: ignore" comment [unused-ignore]
) -> PullRequest | None:
pull = self.get_pull_by_branch_and_state(head_branch_name=head_branch_name, base_branch_name=base_branch_name)
if pull:
Expand All @@ -199,19 +199,33 @@
if base_branch_name is None:
base_branch_name = self.default_branch

# draft PRs are not supported on all private repos
# TODO: check repo plan features instead of this heuristic
# First, check if we should attempt to create a draft PR
should_try_draft = draft

# Private repos may not support draft PRs, but this is just a heuristic
if self.repo.visibility == "private":
logger.info(f"Repo {self.repo.name} is private. Disabling draft PRs.")
draft = False
logger.info(f"Repo {self.repo.name} is private. Draft PRs might not be supported.")
# We'll still try if draft is requested, but we're prepared to handle failure

try:
pr = self.repo.create_pull(title=title or f"Draft PR for {head_branch_name}", body=body or "", head=head_branch_name, base=base_branch_name, draft=draft)
# First attempt - try with draft if requested
pr = self.repo.create_pull(title=title or f"PR for {head_branch_name}", body=body or "", head=head_branch_name, base=base_branch_name, draft=should_try_draft)
logger.info(f"Created pull request for head branch: {head_branch_name} at {pr.html_url}")
# NOTE: return a read-only copy to prevent people from editing it
return self.repo.get_pull(pr.number)
except GithubException as ge:
logger.warning(f"Failed to create PR got GithubException\n\t{ge}")
# Check if this is the specific error about draft PRs not being supported
if should_try_draft and "Draft pull requests are not supported in this repository" in str(ge):
logger.warning(f"Draft PRs not supported in repository {self.repo.name}. Retrying with draft=False.")
try:
# Second attempt - try without draft
pr = self.repo.create_pull(title=title or f"PR for {head_branch_name}", body=body or "", head=head_branch_name, base=base_branch_name, draft=False)
logger.info(f"Created non-draft pull request for head branch: {head_branch_name} at {pr.html_url}")
return self.repo.get_pull(pr.number)
except Exception as retry_e:
logger.warning(f"Failed to create non-draft PR after draft PR failed:\n\t{retry_e}")
else:
logger.warning(f"Failed to create PR got GithubException\n\t{ge}")
except Exception as e:
logger.warning(f"Failed to create PR:\n\t{e}")

Expand All @@ -229,7 +243,7 @@
body="",
)
# TODO: handle PR not mergeable due to merge conflicts
merge = squash_pr.merge(commit_message=squash_commit_msg, commit_title=squash_commit_title, merge_method="squash") # type: ignore[arg-type]

Check failure on line 246 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Item "None" of "PullRequest | None" has no attribute "merge" [union-attr]

def edit_pull(self, pull: PullRequest, title: Opt[str] = NotSet, body: Opt[str] = NotSet, state: Opt[str] = NotSet) -> None:
writable_pr = self.repo.get_pull(pull.number)
Expand Down Expand Up @@ -439,12 +453,34 @@
status, _, _ = self.repo._requester.requestJson("POST", f"{self.repo.url}/merge-upstream", input=post_parameters)
return status == 200

####################################################################################################################
# DRAFT PR SUPPORT
####################################################################################################################

def accepts_draft_prs(self) -> bool:
"""Check if the repository supports draft PRs.

This method uses a cached result if available to avoid making unnecessary API calls.

Returns:
bool: True if the repository supports draft PRs, False otherwise.
"""
# Private repos may not support draft PRs (this is just a heuristic)
if self.repo.visibility == "private":
logger.info(f"Repo {self.repo.name} is private. Draft PRs might not be supported.")
# We'll still return True here and let the create_pull method handle the fallback
# This avoids an extra API call just to check
return True

# For public repos, draft PRs are generally supported
return True

####################################################################################################################
# SEARCH
####################################################################################################################

def search_issues(self, query: str, **kwargs) -> list[Issue]:
return self.gh_client.client.search_issues(query, **kwargs)

Check failure on line 483 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible return value type (got "PaginatedList[Issue]", expected "list[Issue]") [return-value]

def search_prs(self, query: str, **kwargs) -> list[PullRequest]:
return self.gh_client.client.search_issues(query, **kwargs)

Check failure on line 486 in src/codegen/git/clients/git_repo_client.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible return value type (got "PaginatedList[Issue]", expected "list[PullRequest]") [return-value]
Loading