Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ jobs:
timeout-minutes: 5
env:
GITHUB_WORKSPACE: $GITHUB_WORKSPACE
GITHUB_TOKEN: ${{ secrets.GHA_PAT }}
run: |
uv run pytest \
-n auto \
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ dev-dependencies = [
"isort>=5.13.2",
"emoji>=2.14.0",
"pytest-benchmark[histogram]>=5.1.0",
"pytest-asyncio<1.0.0,>=0.21.1",
"loguru>=0.7.3",
"httpx<0.28.2,>=0.28.1",
]
Expand Down
56 changes: 0 additions & 56 deletions src/codegen/git/clients/git_integration_client.py

This file was deleted.

96 changes: 44 additions & 52 deletions src/codegen/git/clients/git_repo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
from github.Tag import Tag
from github.Workflow import Workflow

from codegen.git.clients.github_client_factory import GithubClientFactory
from codegen.git.clients.types import GithubClientType
from codegen.git.schemas.github import GithubScope, GithubType
from codegen.git.clients.github_client import GithubClient
from codegen.git.schemas.repo_config import RepoConfig
from codegen.git.utils.format import format_comparison

Expand All @@ -27,33 +25,27 @@
"""Wrapper around PyGithub's Remote Repository."""

repo_config: RepoConfig
github_type: GithubType = GithubType.GithubEnterprise
gh_client: GithubClientType
read_client: Repository
access_scope: GithubScope
__write_client: Repository | None # Will not be initialized if access scope is read-only
gh_client: GithubClient
_repo: Repository

def __init__(self, repo_config: RepoConfig, github_type: GithubType = GithubType.GithubEnterprise, access_scope: GithubScope = GithubScope.READ) -> None:
def __init__(self, repo_config: RepoConfig) -> None:
self.repo_config = repo_config
self.github_type = github_type
self.gh_client = GithubClientFactory.create_from_repo(self.repo_config, github_type)
self.read_client = self._create_client(GithubScope.READ)
self.__write_client = self._create_client(GithubScope.WRITE) if access_scope == GithubScope.WRITE else None
self.access_scope = access_scope

def _create_client(self, github_scope: GithubScope = GithubScope.READ) -> Repository:
client = self.gh_client.get_repo_by_full_name(self.repo_config.full_name, github_scope=github_scope)
self.gh_client = self._create_github_client()
self._repo = self._create_client()

Check warning on line 34 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L33-L34

Added lines #L33 - L34 were not covered by tests

def _create_github_client(self) -> GithubClient:
return GithubClient()

Check warning on line 37 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L37

Added line #L37 was not covered by tests

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

Check warning on line 40 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L40

Added line #L40 was not covered by tests
if not client:
msg = f"Repo {self.repo_config.full_name} not found in {self.github_type.value}!"
msg = f"Repo {self.repo_config.full_name} not found!"

Check warning on line 42 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L42

Added line #L42 was not covered by tests
raise ValueError(msg)
return client

@property
def _write_client(self) -> Repository:
if self.__write_client is None:
msg = "Cannot perform write operations with read-only client! Try setting github_scope to GithubScope.WRITE."
raise ValueError(msg)
return self.__write_client
def repo(self) -> Repository:
return self._repo

Check warning on line 48 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L48

Added line #L48 was not covered by tests

####################################################################################################################
# PROPERTIES
Expand All @@ -65,7 +57,7 @@

@property
def default_branch(self) -> str:
return self.read_client.default_branch
return self.repo.default_branch

Check warning on line 60 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L60

Added line #L60 was not covered by tests

####################################################################################################################
# CONTENTS
Expand All @@ -76,7 +68,7 @@
if not ref:
ref = self.default_branch
try:
file = self.read_client.get_contents(file_path, ref=ref)
file = self.repo.get_contents(file_path, ref=ref)

Check warning on line 71 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L71

Added line #L71 was not covered by tests
file_contents = file.decoded_content.decode("utf-8") # type: ignore[union-attr]
return file_contents
except UnknownObjectException:
Expand All @@ -100,7 +92,7 @@
str: The last modified date of the directory in ISO format (YYYY-MM-DDTHH:MM:SSZ).

"""
commits = self.read_client.get_commits(path=path)
commits = self.repo.get_commits(path=path)

Check warning on line 95 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L95

Added line #L95 was not covered by tests
if commits.totalCount > 0:
# Get the date of the latest commit
last_modified_date = commits[0].commit.committer.date
Expand All @@ -124,7 +116,7 @@
start_line: Opt[int] = NotSet,
) -> None:
# TODO: add protections (ex: can write to PR)
writeable_pr = self._write_client.get_pull(pull.number)
writeable_pr = self.repo.get_pull(pull.number)

Check warning on line 119 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L119

Added line #L119 was not covered by tests
writeable_pr.create_review_comment(
body=body,
commit=commit,
Expand All @@ -140,7 +132,7 @@
body: str,
) -> None:
# TODO: add protections (ex: can write to PR)
writeable_pr = self._write_client.get_pull(pull.number)
writeable_pr = self.repo.get_pull(pull.number)

Check warning on line 135 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L135

Added line #L135 was not covered by tests
writeable_pr.create_issue_comment(body=body)

####################################################################################################################
Expand All @@ -163,7 +155,7 @@
head_branch_name = f"{self.repo_config.organization_name}:{head_branch_name}"

# retrieve all pulls ordered by created descending
prs = self.read_client.get_pulls(base=base_branch_name, head=head_branch_name, state=state, sort="created", direction="desc")
prs = self.repo.get_pulls(base=base_branch_name, head=head_branch_name, state=state, sort="created", direction="desc")

Check warning on line 158 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L158

Added line #L158 was not covered by tests
if prs.totalCount > 0:
return prs[0]
else:
Expand All @@ -174,7 +166,7 @@
TODO: catching UnknownObjectException is common enough to create a decorator
"""
try:
pr = self.read_client.get_pull(number)
pr = self.repo.get_pull(number)

Check warning on line 169 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L169

Added line #L169 was not covered by tests
return pr
except UnknownObjectException as e:
return None
Expand All @@ -185,8 +177,8 @@
def get_or_create_pull(
self,
head_branch_name: str,
base_branch_name: 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]
title: 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]
body: str | None = None, # type: ignore[assignment]
) -> PullRequest | None:
pull = self.get_pull_by_branch_and_state(head_branch_name=head_branch_name, base_branch_name=base_branch_name)
Expand All @@ -209,10 +201,10 @@
if base_branch_name is None:
base_branch_name = self.default_branch
try:
pr = self._write_client.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)
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)

Check warning on line 204 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L204

Added line #L204 was not covered by tests
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.read_client.get_pull(pr.number)
return self.repo.get_pull(pr.number)

Check warning on line 207 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L207

Added line #L207 was not covered by tests
except GithubException as ge:
logger.warning(f"Failed to create PR got GithubException\n\t{ge}")
except Exception as e:
Expand All @@ -235,15 +227,15 @@
merge = squash_pr.merge(commit_message=squash_commit_msg, commit_title=squash_commit_title, merge_method="squash") # type: ignore[arg-type]

def edit_pull(self, pull: PullRequest, title: Opt[str] = NotSet, body: Opt[str] = NotSet, state: Opt[str] = NotSet) -> None:
writable_pr = self._write_client.get_pull(pull.number)
writable_pr = self.repo.get_pull(pull.number)

Check warning on line 230 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L230

Added line #L230 was not covered by tests
writable_pr.edit(title=title, body=body, state=state)

def add_label_to_pull(self, pull: PullRequest, label: Label) -> None:
writeable_pr = self._write_client.get_pull(pull.number)
writeable_pr = self.repo.get_pull(pull.number)

Check warning on line 234 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L234

Added line #L234 was not covered by tests
writeable_pr.add_to_labels(label)

def remove_label_from_pull(self, pull: PullRequest, label: Label) -> None:
writeable_pr = self._write_client.get_pull(pull.number)
writeable_pr = self.repo.get_pull(pull.number)

Check warning on line 238 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L238

Added line #L238 was not covered by tests
writeable_pr.remove_from_labels(label)

####################################################################################################################
Expand All @@ -264,7 +256,7 @@
def get_branch_safe(self, branch_name: str, attempts: int = 1, wait_seconds: int = 1) -> Branch | None:
for i in range(attempts):
try:
return self.read_client.get_branch(branch_name)
return self.repo.get_branch(branch_name)

Check warning on line 259 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L259

Added line #L259 was not covered by tests
except GithubException as e:
if e.status == 404 and i < attempts - 1:
time.sleep(wait_seconds)
Expand All @@ -276,14 +268,14 @@
if base_branch_name is None:
base_branch_name = self.default_branch

base_branch = self.read_client.get_branch(base_branch_name)
base_branch = self.repo.get_branch(base_branch_name)

Check warning on line 271 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L271

Added line #L271 was not covered by tests
# TODO: also wrap git ref. low pri b/c the only write operation on refs is creating one
self._write_client.create_git_ref(sha=base_branch.commit.sha, ref=f"refs/heads/{new_branch_name}")
self.repo.create_git_ref(sha=base_branch.commit.sha, ref=f"refs/heads/{new_branch_name}")

Check warning on line 273 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L273

Added line #L273 was not covered by tests
branch = self.get_branch_safe(new_branch_name)
return branch

def create_branch_from_sha(self, new_branch_name: str, base_sha: str) -> Branch | None:
self._write_client.create_git_ref(ref=f"refs/heads/{new_branch_name}", sha=base_sha)
self.repo.create_git_ref(ref=f"refs/heads/{new_branch_name}", sha=base_sha)

Check warning on line 278 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L278

Added line #L278 was not covered by tests
branch = self.get_branch_safe(new_branch_name)
return branch

Expand All @@ -295,7 +287,7 @@

branch_to_delete = self.get_branch_safe(branch_name)
if branch_to_delete:
ref_to_delete = self._write_client.get_git_ref(f"heads/{branch_name}")
ref_to_delete = self.repo.get_git_ref(f"heads/{branch_name}")

Check warning on line 290 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L290

Added line #L290 was not covered by tests
ref_to_delete.delete()
logger.info(f"Branch: {branch_name} deleted successfully!")
else:
Expand All @@ -307,7 +299,7 @@

def get_commit_safe(self, commit_sha: str) -> Commit | None:
try:
return self.read_client.get_commit(commit_sha)
return self.repo.get_commit(commit_sha)

Check warning on line 302 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L302

Added line #L302 was not covered by tests
except UnknownObjectException as e:
logger.warning(f"Commit {commit_sha} not found:\n\t{e}")
return None
Expand Down Expand Up @@ -338,7 +330,7 @@

# NOTE: base utility that other compare functions should try to use
def compare(self, base: str, head: str, show_commits: bool = False) -> str:
comparison = self.read_client.compare(base, head)
comparison = self.repo.compare(base, head)

Check warning on line 333 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L333

Added line #L333 was not covered by tests
return format_comparison(comparison, show_commits=show_commits)

####################################################################################################################
Expand All @@ -349,7 +341,7 @@
def get_label_safe(self, label_name: str) -> Label | None:
try:
label_name = label_name.strip()
label = self.read_client.get_label(label_name)
label = self.repo.get_label(label_name)

Check warning on line 344 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L344

Added line #L344 was not covered by tests
return label
except UnknownObjectException as e:
return None
Expand All @@ -360,10 +352,10 @@
def create_label(self, label_name: str, color: str) -> Label:
# TODO: also offer description field
label_name = label_name.strip()
self._write_client.create_label(label_name, color)
self.repo.create_label(label_name, color)

Check warning on line 355 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L355

Added line #L355 was not covered by tests
# TODO: is there a way to convert new_label to a read-only label without making another API call?
# NOTE: return a read-only label to prevent people from editing it
return self.read_client.get_label(label_name)
return self.repo.get_label(label_name)

Check warning on line 358 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L358

Added line #L358 was not covered by tests

def get_or_create_label(self, label_name: str, color: str) -> Label:
existing_label = self.get_label_safe(label_name)
Expand All @@ -377,7 +369,7 @@

def get_check_suite_safe(self, check_suite_id: int) -> CheckSuite | None:
try:
return self.read_client.get_check_suite(check_suite_id)
return self.repo.get_check_suite(check_suite_id)

Check warning on line 372 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L372

Added line #L372 was not covered by tests
except UnknownObjectException as e:
return None
except Exception as e:
Expand All @@ -390,7 +382,7 @@

def get_check_run_safe(self, check_run_id: int) -> CheckRun | None:
try:
return self.read_client.get_check_run(check_run_id)
return self.repo.get_check_run(check_run_id)

Check warning on line 385 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L385

Added line #L385 was not covered by tests
except UnknownObjectException as e:
return None
except Exception as e:
Expand All @@ -406,24 +398,24 @@
conclusion: Opt[str] = NotSet,
output: Opt[dict[str, str | list[dict[str, str | int]]]] = NotSet,
) -> CheckRun:
new_check_run = self._write_client.create_check_run(name=name, head_sha=head_sha, details_url=details_url, status=status, conclusion=conclusion, output=output)
return self.read_client.get_check_run(new_check_run.id)
new_check_run = self.repo.create_check_run(name=name, head_sha=head_sha, details_url=details_url, status=status, conclusion=conclusion, output=output)
return self.repo.get_check_run(new_check_run.id)

Check warning on line 402 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L401-L402

Added lines #L401 - L402 were not covered by tests

####################################################################################################################
# WORKFLOW
####################################################################################################################

def get_workflow_safe(self, file_name: str) -> Workflow | None:
try:
return self.read_client.get_workflow(file_name)
return self.repo.get_workflow(file_name)

Check warning on line 410 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L410

Added line #L410 was not covered by tests
except UnknownObjectException as e:
return None
except Exception as e:
logger.warning(f"Error getting workflow by file name: {file_name}\n\t{e}")
return None

def create_workflow_dispatch(self, workflow: Workflow, ref: Branch | Tag | Commit | str, inputs: Opt[dict] = NotSet):
writeable_workflow = self._write_client.get_workflow(workflow.id)
writeable_workflow = self.repo.get_workflow(workflow.id)

Check warning on line 418 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L418

Added line #L418 was not covered by tests
writeable_workflow.create_dispatch(ref=ref, inputs=inputs)

####################################################################################################################
Expand All @@ -439,5 +431,5 @@
"""
assert isinstance(branch_name, str), branch_name
post_parameters = {"branch": branch_name}
status, _, _ = self._write_client._requester.requestJson("POST", f"{self._write_client.url}/merge-upstream", input=post_parameters)
status, _, _ = self.repo._requester.requestJson("POST", f"{self.repo.url}/merge-upstream", input=post_parameters)

Check warning on line 434 in src/codegen/git/clients/git_repo_client.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/git/clients/git_repo_client.py#L434

Added line #L434 was not covered by tests
return status == 200
Loading
Loading