Merge pull request 'Release: fix user-owned repo labels' (#63) from development into main
Reviewed-on: #63
This commit was merged in pull request #63.
This commit is contained in:
@@ -110,8 +110,14 @@ class GiteaClient:
|
|||||||
|
|
||||||
def _resolve_label_ids(self, label_names: List[str], owner: str, repo: str) -> List[int]:
|
def _resolve_label_ids(self, label_names: List[str], owner: str, repo: str) -> List[int]:
|
||||||
"""Convert label names to label IDs."""
|
"""Convert label names to label IDs."""
|
||||||
org_labels = self.get_org_labels(owner)
|
full_repo = f"{owner}/{repo}"
|
||||||
repo_labels = self.get_labels(f"{owner}/{repo}")
|
|
||||||
|
# Only fetch org labels if repo belongs to an organization
|
||||||
|
org_labels = []
|
||||||
|
if self.is_org_repo(full_repo):
|
||||||
|
org_labels = self.get_org_labels(owner)
|
||||||
|
|
||||||
|
repo_labels = self.get_labels(full_repo)
|
||||||
all_labels = org_labels + repo_labels
|
all_labels = org_labels + repo_labels
|
||||||
label_map = {label['name']: label['id'] for label in all_labels}
|
label_map = {label['name']: label['id'] for label in all_labels}
|
||||||
label_ids = []
|
label_ids = []
|
||||||
|
|||||||
@@ -27,20 +27,27 @@ class LabelTools:
|
|||||||
self.gitea = gitea_client
|
self.gitea = gitea_client
|
||||||
|
|
||||||
async def get_labels(self, repo: Optional[str] = None) -> Dict[str, List[Dict]]:
|
async def get_labels(self, repo: Optional[str] = None) -> Dict[str, List[Dict]]:
|
||||||
"""Get all labels (org + repo). Repo must be 'owner/repo' format."""
|
"""Get all labels (org + repo if org-owned, repo-only if user-owned)."""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
target_repo = repo or self.gitea.repo
|
target_repo = repo or self.gitea.repo
|
||||||
if not target_repo or '/' not in target_repo:
|
if not target_repo or '/' not in target_repo:
|
||||||
raise ValueError("Use 'owner/repo' format (e.g. 'org/repo-name')")
|
raise ValueError("Use 'owner/repo' format (e.g. 'org/repo-name')")
|
||||||
|
|
||||||
org = target_repo.split('/')[0]
|
# Check if repo belongs to an organization or user
|
||||||
|
is_org = await loop.run_in_executor(
|
||||||
org_labels = await loop.run_in_executor(
|
|
||||||
None,
|
None,
|
||||||
lambda: self.gitea.get_org_labels(org)
|
lambda: self.gitea.is_org_repo(target_repo)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
org_labels = []
|
||||||
|
if is_org:
|
||||||
|
org = target_repo.split('/')[0]
|
||||||
|
org_labels = await loop.run_in_executor(
|
||||||
|
None,
|
||||||
|
lambda: self.gitea.get_org_labels(org)
|
||||||
|
)
|
||||||
|
|
||||||
repo_labels = await loop.run_in_executor(
|
repo_labels = await loop.run_in_executor(
|
||||||
None,
|
None,
|
||||||
lambda: self.gitea.get_labels(target_repo)
|
lambda: self.gitea.get_labels(target_repo)
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ from mcp_server.tools.labels import LabelTools
|
|||||||
def mock_gitea_client():
|
def mock_gitea_client():
|
||||||
"""Fixture providing mocked Gitea client"""
|
"""Fixture providing mocked Gitea client"""
|
||||||
client = Mock()
|
client = Mock()
|
||||||
client.repo = 'test_repo'
|
client.repo = 'test_org/test_repo'
|
||||||
|
client.is_org_repo = Mock(return_value=True)
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
@@ -244,3 +245,58 @@ async def test_suggest_labels_multiple_categories():
|
|||||||
|
|
||||||
# Should have Source
|
# Should have Source
|
||||||
assert any('Source/' in label for label in suggestions)
|
assert any('Source/' in label for label in suggestions)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_get_labels_org_owned_repo():
|
||||||
|
"""Test getting labels for organization-owned repository"""
|
||||||
|
mock_client = Mock()
|
||||||
|
mock_client.repo = 'myorg/myrepo'
|
||||||
|
mock_client.is_org_repo = Mock(return_value=True)
|
||||||
|
mock_client.get_org_labels = Mock(return_value=[
|
||||||
|
{'name': 'Type/Bug', 'id': 1},
|
||||||
|
{'name': 'Type/Feature', 'id': 2}
|
||||||
|
])
|
||||||
|
mock_client.get_labels = Mock(return_value=[
|
||||||
|
{'name': 'Component/Backend', 'id': 3}
|
||||||
|
])
|
||||||
|
|
||||||
|
tools = LabelTools(mock_client)
|
||||||
|
result = await tools.get_labels()
|
||||||
|
|
||||||
|
# Should fetch both org and repo labels
|
||||||
|
mock_client.is_org_repo.assert_called_once_with('myorg/myrepo')
|
||||||
|
mock_client.get_org_labels.assert_called_once_with('myorg')
|
||||||
|
mock_client.get_labels.assert_called_once_with('myorg/myrepo')
|
||||||
|
|
||||||
|
assert len(result['organization']) == 2
|
||||||
|
assert len(result['repository']) == 1
|
||||||
|
assert result['total_count'] == 3
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_get_labels_user_owned_repo():
|
||||||
|
"""Test getting labels for user-owned repository (no org labels)"""
|
||||||
|
mock_client = Mock()
|
||||||
|
mock_client.repo = 'lmiranda/personal-portfolio'
|
||||||
|
mock_client.is_org_repo = Mock(return_value=False)
|
||||||
|
mock_client.get_labels = Mock(return_value=[
|
||||||
|
{'name': 'bug', 'id': 1},
|
||||||
|
{'name': 'enhancement', 'id': 2}
|
||||||
|
])
|
||||||
|
|
||||||
|
tools = LabelTools(mock_client)
|
||||||
|
result = await tools.get_labels()
|
||||||
|
|
||||||
|
# Should check if org repo
|
||||||
|
mock_client.is_org_repo.assert_called_once_with('lmiranda/personal-portfolio')
|
||||||
|
|
||||||
|
# Should NOT call get_org_labels for user-owned repos
|
||||||
|
mock_client.get_org_labels.assert_not_called()
|
||||||
|
|
||||||
|
# Should still get repo labels
|
||||||
|
mock_client.get_labels.assert_called_once_with('lmiranda/personal-portfolio')
|
||||||
|
|
||||||
|
assert len(result['organization']) == 0
|
||||||
|
assert len(result['repository']) == 2
|
||||||
|
assert result['total_count'] == 2
|
||||||
|
|||||||
Reference in New Issue
Block a user