fix: improve prompts, error messages, and workspace concurrency
- Externalize coordinator, notification, and planning prompts to separate files - Add workspace mutex for concurrent file operations - Improve error messages across file_tools, issue_tools, and pr_tools - Add logging to tool modules for better debugging - Update tests to match new error message strings
This commit is contained in:
+31
-1
@@ -3,6 +3,7 @@ import logging
|
||||
import subprocess
|
||||
import base64
|
||||
import shutil
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
from .config import GITEA_REPOS_ROOT, GITEA_URL, GITEA_TOKEN
|
||||
@@ -12,12 +13,27 @@ logger: logging.Logger = logging.getLogger("gitea-workspace")
|
||||
|
||||
|
||||
class WorkspaceManager:
|
||||
"""Manages local workspace for Gitea repositories."""
|
||||
"""Manages local workspace for Gitea repositories.
|
||||
|
||||
Uses per-repo threading locks to prevent concurrent git operations
|
||||
on the same repository from causing race conditions.
|
||||
"""
|
||||
|
||||
_repo_locks: dict[str, threading.Lock] = {}
|
||||
_locks_lock: threading.Lock = threading.Lock()
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.root_dir: Path = Path(GITEA_REPOS_ROOT).expanduser().resolve()
|
||||
self.root_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@classmethod
|
||||
def get_repo_lock(cls, repo_full_name: str) -> threading.Lock:
|
||||
"""Get or create a thread-safe lock for a specific repository."""
|
||||
with cls._locks_lock:
|
||||
if repo_full_name not in cls._repo_locks:
|
||||
cls._repo_locks[repo_full_name] = threading.Lock()
|
||||
return cls._repo_locks[repo_full_name]
|
||||
|
||||
def _configure_repo_user(self, repo_path: Path) -> None:
|
||||
try:
|
||||
client = GiteaClient()
|
||||
@@ -70,6 +86,12 @@ class WorkspaceManager:
|
||||
return f"{parsed.scheme}://{parsed.netloc}{path}/{repo_full_name}.git"
|
||||
|
||||
def sanitize_repo(self, repo_full_name: str, repo_path: Path) -> None:
|
||||
lock = self.get_repo_lock(repo_full_name)
|
||||
with lock:
|
||||
logger.debug(f"Acquired workspace lock for {repo_full_name} (sanitize)")
|
||||
self._sanitize_repo_inner(repo_full_name, repo_path)
|
||||
|
||||
def _sanitize_repo_inner(self, repo_full_name: str, repo_path: Path) -> None:
|
||||
try:
|
||||
auth_url = self._get_authenticated_url(repo_full_name)
|
||||
subprocess.run(
|
||||
@@ -146,6 +168,14 @@ class WorkspaceManager:
|
||||
) from e
|
||||
|
||||
def clone_repo(self, repo_full_name: str, clone_url: str | None = None) -> Path:
|
||||
lock = self.get_repo_lock(repo_full_name)
|
||||
with lock:
|
||||
logger.debug(f"Acquired workspace lock for {repo_full_name} (clone)")
|
||||
return self._clone_repo_inner(repo_full_name, clone_url)
|
||||
|
||||
def _clone_repo_inner(
|
||||
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():
|
||||
|
||||
Reference in New Issue
Block a user