| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- import httpx
- from app.config import settings
- import logging
- logger = logging.getLogger(__name__)
- class GogsClient:
- def __init__(self):
- self.base_url = settings.GOGS_URL.rstrip('/')
- self.token = settings.GOGS_TOKEN
- self.headers = {"Authorization": f"token {self.token}"}
- async def get_manifest(self, owner: str, repo: str, commit_id: str) -> str:
- """Fetch manifest.yaml raw content from a specific commit."""
- # Gogs raw file URL format: /{owner}/{repo}/raw/{ref}/{path}
- url = f"{self.base_url}/api/v1/repos/{owner}/{repo}/raw/{commit_id}/manifest.yaml"
- async with httpx.AsyncClient() as client:
- resp = await client.get(url, headers=self.headers)
- if resp.status_code == 404:
- return None
- resp.raise_for_status()
- return resp.text
- async def get_tree(self, owner: str, repo: str, commit_id: str, path: str = "") -> list:
- """Get the file tree of a repository."""
- url = f"{self.base_url}/api/v1/repos/{owner}/{repo}/contents/{path}?ref={commit_id}"
- async with httpx.AsyncClient() as client:
- resp = await client.get(url, headers=self.headers)
- resp.raise_for_status()
- return resp.json()
- async def get_file_info(self, owner: str, repo: str, commit_id: str, file_path: str) -> dict | None:
- """Get single file info including SHA.
- Returns dict with 'sha', 'size', 'path' or None if not found.
- """
- url = f"{self.base_url}/api/v1/repos/{owner}/{repo}/contents/{file_path}?ref={commit_id}"
- try:
- async with httpx.AsyncClient() as client:
- resp = await client.get(url, headers=self.headers)
- if resp.status_code == 404:
- return None
- resp.raise_for_status()
- data = resp.json()
- # contents API returns file info directly for single file
- if isinstance(data, dict) and data.get("type") == "file":
- return {
- "path": file_path,
- "sha": data.get("sha"),
- "size": data.get("size", 0),
- "type": "blob"
- }
- return None
- except httpx.HTTPStatusError as e:
- logger.error(f"Failed to get file info for {file_path}: {e}")
- return None
- async def get_directory_tree(self, owner: str, repo: str, commit_id: str, dir_path: str) -> list:
- """Get all files under a specific directory (recursive).
- Args:
- dir_path: Directory path without trailing slash (e.g., "data/output")
- Returns:
- List of file info dicts with 'path', 'sha', 'size', 'type'
- """
- all_files = []
- async def fetch_contents(path: str):
- """Recursively fetch directory contents using contents API."""
- url = f"{self.base_url}/api/v1/repos/{owner}/{repo}/contents/{path}?ref={commit_id}"
- try:
- async with httpx.AsyncClient() as client:
- resp = await client.get(url, headers=self.headers)
- if resp.status_code == 404:
- logger.warning(f"Directory not found: {path}")
- return
- resp.raise_for_status()
- data = resp.json()
- # contents API returns list for directories
- if isinstance(data, list):
- for item in data:
- if item.get("type") == "file":
- all_files.append({
- "path": item.get("path"),
- "sha": item.get("sha"),
- "size": item.get("size", 0),
- "type": "blob"
- })
- elif item.get("type") == "dir":
- # Recursively fetch subdirectory
- await fetch_contents(item.get("path"))
- except httpx.HTTPStatusError as e:
- logger.error(f"Failed to get contents for {path}: {e}")
- await fetch_contents(dir_path)
- return all_files
- async def get_file_content(self, owner: str, repo: str, commit_id: str, file_path: str) -> bytes:
- """Download raw file content."""
- # Gogs raw file URL format: /{owner}/{repo}/raw/{ref}/{path}
- url = f"{self.base_url}/api/v1/repos/{owner}/{repo}/raw/{commit_id}/{file_path}"
- async with httpx.AsyncClient() as client:
- resp = await client.get(url, headers=self.headers)
- resp.raise_for_status()
- return resp.content
|