Files
markitect-main/gitea/config.py
tegwick fd8f792f08 refactor: Factor out Gitea interfacing into clean facade pattern
- Create new gitea/ package with clean API facade
- Establish proper separation of concerns: tddai uses gitea, not vice versa
- Replace duplicate curl+subprocess patterns with unified HTTP client
- Add rich domain models with properties (issue.priority, issue.status)
- Maintain full backwards compatibility in tddai modules
- Reduce code complexity: -373 lines, +151 lines (net -222 lines)
- Improve testability and maintainability through clean interfaces

Architecture:
- gitea.client.GiteaClient - main facade with sub-clients
- gitea.api_client - high-level API with model conversion
- gitea.http_client - low-level HTTP operations
- gitea.models - rich domain objects (Issue, Milestone, Label)
- gitea.config - gitea-specific configuration
- gitea.exceptions - clean exception hierarchy

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-26 14:25:40 +02:00

113 lines
3.5 KiB
Python

"""
Gitea-specific configuration management.
"""
import os
from pathlib import Path
from dataclasses import dataclass
from typing import Optional
from .exceptions import GiteaConfigError
def load_dotenv_file(env_file: Path) -> None:
"""Load environment variables from a .env file."""
if not env_file.exists():
return
with open(env_file, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
os.environ.setdefault(key.strip(), value.strip())
@dataclass
class GiteaConfig:
"""Configuration for Gitea API access."""
# Repository settings (required)
gitea_url: str = ""
repo_owner: str = ""
repo_name: str = ""
# Authentication (optional for read operations)
auth_token: Optional[str] = None
@property
def base_api_url(self) -> str:
"""Get the base API URL for this repository."""
return f"{self.gitea_url}/api/v1"
@property
def repo_api_url(self) -> str:
"""Get the repository API URL."""
return f"{self.base_api_url}/repos/{self.repo_owner}/{self.repo_name}"
@property
def issues_api_url(self) -> str:
"""Get the issues API URL."""
return f"{self.repo_api_url}/issues"
@property
def milestones_api_url(self) -> str:
"""Get the milestones API URL."""
return f"{self.repo_api_url}/milestones"
@property
def labels_api_url(self) -> str:
"""Get the labels API URL."""
return f"{self.repo_api_url}/labels"
@classmethod
def from_environment(cls, env_prefix: str = "GITEA") -> "GiteaConfig":
"""Create config from environment variables.
Args:
env_prefix: Environment variable prefix (default: GITEA)
Looks for {prefix}_URL, {prefix}_REPO_OWNER, etc.
"""
# Auto-load .env.gitea file if it exists
env_file = Path(".env.gitea")
load_dotenv_file(env_file)
config = cls()
# Load from environment
config.gitea_url = os.getenv(f"{env_prefix}_URL", "")
config.repo_owner = os.getenv(f"{env_prefix}_REPO_OWNER", "")
config.repo_name = os.getenv(f"{env_prefix}_REPO_NAME", "")
config.auth_token = os.getenv(f"{env_prefix}_API_TOKEN")
return config
@classmethod
def from_tddai_config(cls, tddai_config) -> "GiteaConfig":
"""Create GiteaConfig from legacy TddaiConfig for backwards compatibility."""
return cls(
gitea_url=tddai_config.gitea_url,
repo_owner=tddai_config.repo_owner,
repo_name=tddai_config.repo_name,
auth_token=os.getenv('GITEA_API_TOKEN')
)
def validate(self) -> None:
"""Validate configuration settings."""
if not self.gitea_url:
raise GiteaConfigError("gitea_url cannot be empty")
if not self.repo_owner:
raise GiteaConfigError("repo_owner cannot be empty")
if not self.repo_name:
raise GiteaConfigError("repo_name cannot be empty")
# Validate URL format
if not (self.gitea_url.startswith('http://') or self.gitea_url.startswith('https://')):
raise GiteaConfigError("gitea_url must start with http:// or https://")
def requires_auth(self, operation: str = "read") -> bool:
"""Check if operation requires authentication."""
write_operations = {"create", "update", "delete", "write"}
return operation in write_operations and not self.auth_token