180 lines
6.4 KiB
Python
180 lines
6.4 KiB
Python
import os
|
|
import logging
|
|
import requests
|
|
from typing import Dict, Any, List, Optional, Tuple
|
|
from urllib.parse import urlparse
|
|
from fastapi import HTTPException
|
|
|
|
# Configure logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class GiteaClient:
|
|
"""Client for interacting with Gitea API."""
|
|
|
|
def __init__(self):
|
|
"""Initialize Gitea client with configuration from environment variables."""
|
|
self.api_base_url = os.getenv("GITEA_API_URL", "").rstrip("/")
|
|
self.token = os.getenv("GITEA_API_TOKEN")
|
|
self.username = os.getenv("GITEA_USERNAME")
|
|
self.verify_ssl = os.getenv("GITEA_VERIFY_SSL", "true").lower() == "true"
|
|
|
|
if not self.api_base_url:
|
|
logger.warning("GITEA_API_URL is not configured. Gitea integration will not work.")
|
|
|
|
if not self.token and (self.username and os.getenv("GITEA_PASSWORD")):
|
|
self.token = self._get_token_from_credentials()
|
|
|
|
def _get_token_from_credentials(self) -> Optional[str]:
|
|
"""Get a token using username and password if provided."""
|
|
try:
|
|
response = requests.post(
|
|
f"{self.api_base_url}/users/{self.username}/tokens",
|
|
auth=(self.username, os.getenv("GITEA_PASSWORD", "")),
|
|
json={
|
|
"name": "nomad-mcp-service",
|
|
"scopes": ["repo", "read:org"]
|
|
},
|
|
verify=self.verify_ssl
|
|
)
|
|
|
|
if response.status_code == 201:
|
|
return response.json().get("sha1")
|
|
else:
|
|
logger.error(f"Failed to get Gitea token: {response.text}")
|
|
return None
|
|
except Exception as e:
|
|
logger.error(f"Failed to get Gitea token: {str(e)}")
|
|
return None
|
|
|
|
def _get_headers(self) -> Dict[str, str]:
|
|
"""Get request headers with authentication."""
|
|
headers = {
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/json"
|
|
}
|
|
|
|
if self.token:
|
|
headers["Authorization"] = f"token {self.token}"
|
|
|
|
return headers
|
|
|
|
def parse_repo_url(self, repo_url: str) -> Tuple[str, str]:
|
|
"""
|
|
Parse a Gitea repository URL to extract owner and repo name.
|
|
|
|
Examples:
|
|
- http://gitea.internal.example.com/username/repo-name -> (username, repo-name)
|
|
- https://gitea.example.com/org/project -> (org, project)
|
|
"""
|
|
try:
|
|
# Parse the URL
|
|
parsed_url = urlparse(repo_url)
|
|
|
|
# Get the path and remove leading/trailing slashes
|
|
path = parsed_url.path.strip("/")
|
|
|
|
# Split the path
|
|
parts = path.split("/")
|
|
|
|
if len(parts) < 2:
|
|
raise ValueError(f"Invalid repository URL: {repo_url}")
|
|
|
|
# Extract owner and repo
|
|
owner = parts[0]
|
|
repo = parts[1]
|
|
|
|
return owner, repo
|
|
except Exception as e:
|
|
logger.error(f"Failed to parse repository URL: {repo_url}, error: {str(e)}")
|
|
raise ValueError(f"Invalid repository URL: {repo_url}")
|
|
|
|
def check_repository_exists(self, repo_url: str) -> bool:
|
|
"""Check if a repository exists in Gitea."""
|
|
if not self.api_base_url:
|
|
# No Gitea integration configured, assume repository exists
|
|
return True
|
|
|
|
try:
|
|
owner, repo = self.parse_repo_url(repo_url)
|
|
|
|
response = requests.get(
|
|
f"{self.api_base_url}/repos/{owner}/{repo}",
|
|
headers=self._get_headers(),
|
|
verify=self.verify_ssl
|
|
)
|
|
|
|
return response.status_code == 200
|
|
except Exception as e:
|
|
logger.error(f"Failed to check repository: {repo_url}, error: {str(e)}")
|
|
return False
|
|
|
|
def get_repository_info(self, repo_url: str) -> Optional[Dict[str, Any]]:
|
|
"""Get repository information from Gitea."""
|
|
if not self.api_base_url:
|
|
# No Gitea integration configured
|
|
return None
|
|
|
|
try:
|
|
owner, repo = self.parse_repo_url(repo_url)
|
|
|
|
response = requests.get(
|
|
f"{self.api_base_url}/repos/{owner}/{repo}",
|
|
headers=self._get_headers(),
|
|
verify=self.verify_ssl
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
return response.json()
|
|
else:
|
|
logger.error(f"Failed to get repository info: {response.text}")
|
|
return None
|
|
except Exception as e:
|
|
logger.error(f"Failed to get repository info: {repo_url}, error: {str(e)}")
|
|
return None
|
|
|
|
def list_repositories(self, limit: int = 100) -> List[Dict[str, Any]]:
|
|
"""List available repositories from Gitea."""
|
|
if not self.api_base_url:
|
|
# No Gitea integration configured
|
|
return []
|
|
|
|
try:
|
|
response = requests.get(
|
|
f"{self.api_base_url}/user/repos",
|
|
headers=self._get_headers(),
|
|
params={"limit": limit},
|
|
verify=self.verify_ssl
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
return response.json()
|
|
else:
|
|
logger.error(f"Failed to list repositories: {response.text}")
|
|
return []
|
|
except Exception as e:
|
|
logger.error(f"Failed to list repositories: {str(e)}")
|
|
return []
|
|
|
|
def get_repository_branches(self, repo_url: str) -> List[Dict[str, Any]]:
|
|
"""Get branches for a repository."""
|
|
if not self.api_base_url:
|
|
# No Gitea integration configured
|
|
return []
|
|
|
|
try:
|
|
owner, repo = self.parse_repo_url(repo_url)
|
|
|
|
response = requests.get(
|
|
f"{self.api_base_url}/repos/{owner}/{repo}/branches",
|
|
headers=self._get_headers(),
|
|
verify=self.verify_ssl
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
return response.json()
|
|
else:
|
|
logger.error(f"Failed to get repository branches: {response.text}")
|
|
return []
|
|
except Exception as e:
|
|
logger.error(f"Failed to get repository branches: {repo_url}, error: {str(e)}")
|
|
return [] |