import os import logging import subprocess from pathlib import Path from urllib.parse import urlparse from .config import GITEA_REPOS_ROOT, GITEA_URL, GITEA_TOKEN logger: logging.Logger = logging.getLogger("gitea-workspace") class WorkspaceManager: """Manages local workspace for Gitea repositories.""" def __init__(self) -> None: self.root_dir: Path = Path(GITEA_REPOS_ROOT).expanduser().resolve() self.root_dir.mkdir(parents=True, exist_ok=True) self._configure_git_credentials() def _configure_git_credentials(self) -> None: try: # Unset any global configs we might have set previously subprocess.run( ["git", "config", "--global", "--unset", "credential.helper"], capture_output=True ) subprocess.run( ["git", "config", "--global", "--unset", "user.name"], capture_output=True ) subprocess.run( ["git", "config", "--global", "--unset", "user.email"], capture_output=True ) except Exception as e: logger.error(f"Error unsetting global configs: {e}") def _configure_repo_user(self, repo_path: Path) -> None: try: # Configure credential helper locally for the repo subprocess.run( ["git", "-C", str(repo_path), "config", "credential.helper", "store"], check=True, capture_output=True ) # Write to ~/.git-credentials parsed = urlparse(GITEA_URL.rstrip("/")) cred_line = f"{parsed.scheme}://meeks-ai:{GITEA_TOKEN}@{parsed.netloc}\n" cred_file = Path("~/.git-credentials").expanduser() if cred_file.exists(): content = cred_file.read_text() if cred_line.strip() not in content: cred_file.write_text(content + cred_line) else: cred_file.write_text(cred_line) from gitea.client import GiteaClient client = GiteaClient() user = client.get_authenticated_user() if user: name = user.full_name or user.login or "meeks-ai" email = user.email or "micke_ingvarsson+ai@hotmail.com" subprocess.run( ["git", "-C", str(repo_path), "config", "user.name", name], check=True, capture_output=True ) subprocess.run( ["git", "-C", str(repo_path), "config", "user.email", email], check=True, capture_output=True ) except Exception as e: logger.error(f"Error configuring local git user: {e}") def get_repo_path(self, repo_full_name: str) -> Path: parts: list[str] = repo_full_name.split("/") return self.root_dir / parts[0] / parts[1] def _get_authenticated_url(self, repo_full_name: str) -> str: parsed = urlparse(GITEA_URL.rstrip("/")) path = parsed.path.rstrip("/") return f"{parsed.scheme}://{parsed.netloc}{path}/{repo_full_name}.git" def sanitize_repo(self, repo_full_name: str, repo_path: Path) -> None: try: auth_url = self._get_authenticated_url(repo_full_name) subprocess.run( ["git", "-C", str(repo_path), "remote", "set-url", "origin", auth_url], check=True, capture_output=True, ) self._configure_repo_user(repo_path) subprocess.run( ["git", "-C", str(repo_path), "reset", "--hard", "HEAD"], check=True, capture_output=True, ) subprocess.run( ["git", "-C", str(repo_path), "clean", "-fdx"], check=True, capture_output=True, ) try: subprocess.run( ["git", "-C", str(repo_path), "checkout", "main"], check=True, capture_output=True, ) except subprocess.CalledProcessError: subprocess.run( ["git", "-C", str(repo_path), "checkout", "master"], check=True, capture_output=True, ) try: subprocess.run( ["git", "-C", str(repo_path), "pull", "origin", "main"], check=True, capture_output=True, ) except subprocess.CalledProcessError: subprocess.run( ["git", "-C", str(repo_path), "pull", "origin", "master"], check=True, capture_output=True, ) except Exception as e: logger.error(f"Error during sanitization: {e}") def clone_repo(self, repo_full_name: str, clone_url: str | None = None) -> Path: repo_path: Path = self.get_repo_path(repo_full_name) if repo_path.exists(): if not (repo_path / ".git").exists(): new_path: Path = repo_path.parent / f"{repo_full_name.replace('/', '_')}_old" if new_path.exists(): import shutil shutil.rmtree(new_path) repo_path.rename(new_path) return repo_path logger.info(f"Cloning repository {repo_full_name} to {repo_path}...") auth_url = self._get_authenticated_url(repo_full_name) subprocess.run(["git", "clone", auth_url, str(repo_path)], check=True, capture_output=True) self._configure_repo_user(repo_path) return repo_path