Merge pull request 'Release: fix project directory detection for MCP server' (#72) from development into main

Reviewed-on: #72
This commit was merged in pull request #72.
This commit is contained in:
2026-01-21 21:41:55 +00:00
2 changed files with 107 additions and 8 deletions

View File

@@ -51,11 +51,15 @@ class GiteaConfig:
"cat > ~/.config/claude/gitea.env" "cat > ~/.config/claude/gitea.env"
) )
# Find project directory (MCP server cwd is plugin dir, not project dir)
project_dir = self._find_project_directory()
# Load project config (overrides system) # Load project config (overrides system)
project_config = Path.cwd() / '.env' if project_dir:
if project_config.exists(): project_config = project_dir / '.env'
load_dotenv(project_config, override=True) if project_config.exists():
logger.info(f"Loaded project configuration from {project_config}") load_dotenv(project_config, override=True)
logger.info(f"Loaded project configuration from {project_config}")
# Extract values # Extract values
self.api_url = os.getenv('GITEA_API_URL') self.api_url = os.getenv('GITEA_API_URL')
@@ -63,8 +67,8 @@ class GiteaConfig:
self.repo = os.getenv('GITEA_REPO') # Optional, must be owner/repo format self.repo = os.getenv('GITEA_REPO') # Optional, must be owner/repo format
# Auto-detect repo from git remote if not specified # Auto-detect repo from git remote if not specified
if not self.repo: if not self.repo and project_dir:
self.repo = self._detect_repo_from_git() self.repo = self._detect_repo_from_git(project_dir)
if self.repo: if self.repo:
logger.info(f"Auto-detected repository from git remote: {self.repo}") logger.info(f"Auto-detected repository from git remote: {self.repo}")
@@ -106,10 +110,57 @@ class GiteaConfig:
"Check your ~/.config/claude/gitea.env file" "Check your ~/.config/claude/gitea.env file"
) )
def _detect_repo_from_git(self) -> Optional[str]: def _find_project_directory(self) -> Optional[Path]:
"""
Find the user's project directory.
The MCP server runs with cwd set to the plugin directory, not the
user's project. We need to find the actual project directory using
various heuristics.
Returns:
Path to project directory, or None if not found
"""
# Strategy 1: Check CLAUDE_PROJECT_DIR environment variable
project_dir = os.getenv('CLAUDE_PROJECT_DIR')
if project_dir:
path = Path(project_dir)
if path.exists():
logger.info(f"Found project directory from CLAUDE_PROJECT_DIR: {path}")
return path
# Strategy 2: Check PWD (original working directory before cwd override)
pwd = os.getenv('PWD')
if pwd:
path = Path(pwd)
# Verify it has .git or .env (indicates a project)
if path.exists() and ((path / '.git').exists() or (path / '.env').exists()):
logger.info(f"Found project directory from PWD: {path}")
return path
# Strategy 3: Check current working directory
# This handles test scenarios and cases where cwd is actually the project
cwd = Path.cwd()
if (cwd / '.git').exists() or (cwd / '.env').exists():
logger.info(f"Found project directory from cwd: {cwd}")
return cwd
# Strategy 4: Check if GITEA_REPO is already set (user configured it)
# If so, we don't need to find the project directory for git detection
if os.getenv('GITEA_REPO'):
logger.debug("GITEA_REPO already set, skipping project directory detection")
return None
logger.debug("Could not determine project directory")
return None
def _detect_repo_from_git(self, project_dir: Optional[Path] = None) -> Optional[str]:
""" """
Auto-detect repository from git remote origin URL. Auto-detect repository from git remote origin URL.
Args:
project_dir: Directory to run git command from (defaults to cwd)
Supports URL formats: Supports URL formats:
- SSH: ssh://git@host:port/owner/repo.git - SSH: ssh://git@host:port/owner/repo.git
- SSH short: git@host:owner/repo.git - SSH short: git@host:owner/repo.git
@@ -124,7 +175,8 @@ class GiteaConfig:
['git', 'remote', 'get-url', 'origin'], ['git', 'remote', 'get-url', 'origin'],
capture_output=True, capture_output=True,
text=True, text=True,
timeout=5 timeout=5,
cwd=str(project_dir) if project_dir else None
) )
if result.returncode != 0: if result.returncode != 0:
logger.debug("No git remote 'origin' found") logger.debug("No git remote 'origin' found")

View File

@@ -211,3 +211,50 @@ def test_parse_git_url_invalid_format():
url = "not-a-valid-url" url = "not-a-valid-url"
result = config._parse_git_url(url) result = config._parse_git_url(url)
assert result is None assert result is None
def test_find_project_directory_from_env(tmp_path, monkeypatch):
"""Test finding project directory from CLAUDE_PROJECT_DIR env var"""
project_dir = tmp_path / 'my-project'
project_dir.mkdir()
(project_dir / '.git').mkdir()
monkeypatch.setenv('CLAUDE_PROJECT_DIR', str(project_dir))
config = GiteaConfig()
result = config._find_project_directory()
assert result == project_dir
def test_find_project_directory_from_cwd(tmp_path, monkeypatch):
"""Test finding project directory from cwd with .env file"""
project_dir = tmp_path / 'project'
project_dir.mkdir()
(project_dir / '.env').write_text("GITEA_REPO=test/repo")
monkeypatch.chdir(project_dir)
# Clear env vars that might interfere
monkeypatch.delenv('CLAUDE_PROJECT_DIR', raising=False)
monkeypatch.delenv('PWD', raising=False)
config = GiteaConfig()
result = config._find_project_directory()
assert result == project_dir
def test_find_project_directory_none_when_no_markers(tmp_path, monkeypatch):
"""Test returns None when no project markers found"""
empty_dir = tmp_path / 'empty'
empty_dir.mkdir()
monkeypatch.chdir(empty_dir)
monkeypatch.delenv('CLAUDE_PROJECT_DIR', raising=False)
monkeypatch.delenv('PWD', raising=False)
monkeypatch.delenv('GITEA_REPO', raising=False)
config = GiteaConfig()
result = config._find_project_directory()
assert result is None