""" Base provider interface for git hosting platforms. Defines the abstract interface that all git providers must implement. """ from abc import ABC, abstractmethod from typing import Any from models import ( CreatePRResult, GetPRResult, ListPRsResult, MergePRResult, MergeStrategy, PRState, UpdatePRResult, ) class BaseProvider(ABC): """ Abstract base class for git hosting providers. All providers (Gitea, GitHub, GitLab) must implement this interface. """ @property @abstractmethod def name(self) -> str: """Return the provider name (e.g., 'gitea', 'github').""" ... @abstractmethod async def is_connected(self) -> bool: """Check if the provider is connected and authenticated.""" ... @abstractmethod async def get_authenticated_user(self) -> str | None: """Get the username of the authenticated user.""" ... # Repository operations @abstractmethod async def get_repo_info(self, owner: str, repo: str) -> dict[str, Any]: """ Get repository information. Args: owner: Repository owner/organization repo: Repository name Returns: Repository info dict """ ... @abstractmethod async def get_default_branch(self, owner: str, repo: str) -> str: """ Get the default branch for a repository. Args: owner: Repository owner/organization repo: Repository name Returns: Default branch name """ ... # Pull Request operations @abstractmethod async def create_pr( self, owner: str, repo: str, title: str, body: str, source_branch: str, target_branch: str, draft: bool = False, labels: list[str] | None = None, assignees: list[str] | None = None, reviewers: list[str] | None = None, ) -> CreatePRResult: """ Create a pull request. Args: owner: Repository owner repo: Repository name title: PR title body: PR description source_branch: Source branch name target_branch: Target branch name draft: Whether to create as draft labels: Labels to add assignees: Users to assign reviewers: Users to request review from Returns: CreatePRResult with PR number and URL """ ... @abstractmethod async def get_pr(self, owner: str, repo: str, pr_number: int) -> GetPRResult: """ Get a pull request by number. Args: owner: Repository owner repo: Repository name pr_number: Pull request number Returns: GetPRResult with PR details """ ... @abstractmethod async def list_prs( self, owner: str, repo: str, state: PRState | None = None, author: str | None = None, limit: int = 20, ) -> ListPRsResult: """ List pull requests. Args: owner: Repository owner repo: Repository name state: Filter by state (open, closed, merged) author: Filter by author limit: Maximum PRs to return Returns: ListPRsResult with list of PRs """ ... @abstractmethod async def merge_pr( self, owner: str, repo: str, pr_number: int, merge_strategy: MergeStrategy = MergeStrategy.MERGE, commit_message: str | None = None, delete_branch: bool = True, ) -> MergePRResult: """ Merge a pull request. Args: owner: Repository owner repo: Repository name pr_number: Pull request number merge_strategy: Merge strategy to use commit_message: Custom merge commit message delete_branch: Whether to delete source branch Returns: MergePRResult with merge status """ ... @abstractmethod async def update_pr( self, owner: str, repo: str, pr_number: int, title: str | None = None, body: str | None = None, state: PRState | None = None, labels: list[str] | None = None, assignees: list[str] | None = None, ) -> UpdatePRResult: """ Update a pull request. Args: owner: Repository owner repo: Repository name pr_number: Pull request number title: New title body: New description state: New state (open, closed) labels: Replace labels assignees: Replace assignees Returns: UpdatePRResult with updated PR info """ ... @abstractmethod async def close_pr(self, owner: str, repo: str, pr_number: int) -> UpdatePRResult: """ Close a pull request without merging. Args: owner: Repository owner repo: Repository name pr_number: Pull request number Returns: UpdatePRResult with updated PR info """ ... # Branch operations via API (for operations that need to bypass local git) @abstractmethod async def delete_remote_branch(self, owner: str, repo: str, branch: str) -> bool: """ Delete a remote branch via API. Args: owner: Repository owner repo: Repository name branch: Branch name to delete Returns: True if deleted, False otherwise """ ... @abstractmethod async def get_branch( self, owner: str, repo: str, branch: str ) -> dict[str, Any] | None: """ Get branch information via API. Args: owner: Repository owner repo: Repository name branch: Branch name Returns: Branch info dict or None if not found """ ... # Comment operations @abstractmethod async def add_pr_comment( self, owner: str, repo: str, pr_number: int, body: str ) -> dict[str, Any]: """ Add a comment to a pull request. Args: owner: Repository owner repo: Repository name pr_number: Pull request number body: Comment body Returns: Created comment info """ ... @abstractmethod async def list_pr_comments( self, owner: str, repo: str, pr_number: int ) -> list[dict[str, Any]]: """ List comments on a pull request. Args: owner: Repository owner repo: Repository name pr_number: Pull request number Returns: List of comments """ ... # Label operations @abstractmethod async def add_labels( self, owner: str, repo: str, pr_number: int, labels: list[str] ) -> list[str]: """ Add labels to a pull request. Args: owner: Repository owner repo: Repository name pr_number: Pull request number labels: Labels to add Returns: Updated list of labels """ ... @abstractmethod async def remove_label( self, owner: str, repo: str, pr_number: int, label: str ) -> list[str]: """ Remove a label from a pull request. Args: owner: Repository owner repo: Repository name pr_number: Pull request number label: Label to remove Returns: Updated list of labels """ ... # Reviewer operations @abstractmethod async def request_review( self, owner: str, repo: str, pr_number: int, reviewers: list[str] ) -> list[str]: """ Request review from users. Args: owner: Repository owner repo: Repository name pr_number: Pull request number reviewers: Usernames to request review from Returns: List of reviewers requested """ ... # Utility methods def parse_repo_url(self, repo_url: str) -> tuple[str, str]: """ Parse repository URL to extract owner and repo name. Args: repo_url: Repository URL (HTTPS or SSH) Returns: Tuple of (owner, repo) Raises: ValueError: If URL cannot be parsed """ import re # Handle SSH URLs: git@host:owner/repo.git ssh_match = re.match(r"git@[^:]+:([^/]+)/([^/]+?)(?:\.git)?$", repo_url) if ssh_match: return ssh_match.group(1), ssh_match.group(2) # Handle HTTPS URLs: https://host/owner/repo.git https_match = re.match(r"https?://[^/]+/([^/]+)/([^/]+?)(?:\.git)?$", repo_url) if https_match: return https_match.group(1), https_match.group(2) raise ValueError(f"Unable to parse repository URL: {repo_url}")