feat: Implement unified configuration management system
Consolidates scattered configuration patterns across TDDAI, Gitea, and MarkiTect into a unified, maintainable system addressing issue #22. Key improvements: - Created centralized config/ module with base classes and utilities - Eliminated duplicate load_dotenv_file() functions - Standardized environment variables with MARKITECT_ prefix - Implemented comprehensive validation with helpful error messages - Maintained full backward compatibility with existing TDDAI config Architecture: - BaseConfig: Abstract base with common functionality - MarkitectConfig: Main configuration class with legacy support - Compatibility layer: TddaiConfigCompat and GiteaConfigCompat wrappers - Unified error handling: ConfigurationError hierarchy All existing tests pass without modification, ensuring seamless transition. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
169
config/compat.py
Normal file
169
config/compat.py
Normal file
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Backward compatibility layer for existing configuration systems.
|
||||
|
||||
Provides compatibility shims that allow existing TDDAI and Gitea
|
||||
configuration code to work with the unified configuration system.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from .manager import get_unified_config
|
||||
from .exceptions import ConfigurationError
|
||||
|
||||
|
||||
@dataclass
|
||||
class TddaiConfigCompat:
|
||||
"""TDDAI configuration compatibility layer.
|
||||
|
||||
Provides the same interface as the original TddaiConfig but
|
||||
backed by the unified configuration system.
|
||||
"""
|
||||
|
||||
workspace_dir: Path
|
||||
current_issue_file: str = "current_issue.json"
|
||||
gitea_url: str = ""
|
||||
repo_owner: str = ""
|
||||
repo_name: str = ""
|
||||
tests_dir: Path = Path("tests")
|
||||
test_file_pattern: str = "test_issue_{issue_num}_{scenario}.py"
|
||||
claude_code_command: str = "claude"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize from unified configuration."""
|
||||
unified = get_unified_config()
|
||||
|
||||
# Map unified config to TDDAI format
|
||||
self.workspace_dir = unified.workspace_dir
|
||||
self.gitea_url = unified.gitea_url
|
||||
self.repo_owner = unified.repo_owner
|
||||
self.repo_name = unified.repo_name
|
||||
self.tests_dir = unified.tests_dir
|
||||
self.test_file_pattern = unified.test_file_pattern
|
||||
self.claude_code_command = unified.claude_code_command
|
||||
|
||||
# Apply any explicit overrides
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
|
||||
def validate(self) -> None:
|
||||
"""Validate configuration (for compatibility)."""
|
||||
if not self.gitea_url or not self.gitea_url.strip():
|
||||
raise ConfigurationError("gitea_url cannot be empty")
|
||||
|
||||
if not self.repo_owner or not self.repo_owner.strip():
|
||||
raise ConfigurationError("repo_owner cannot be empty")
|
||||
|
||||
if not self.repo_name or not self.repo_name.strip():
|
||||
raise ConfigurationError("repo_name cannot be empty")
|
||||
|
||||
@property
|
||||
def issues_api_url(self) -> str:
|
||||
"""Get issues API URL (for compatibility)."""
|
||||
return f"{self.gitea_url}/api/v1/repos/{self.repo_owner}/{self.repo_name}/issues"
|
||||
|
||||
@property
|
||||
def issues_base_url(self) -> str:
|
||||
"""Get issues base URL (for compatibility)."""
|
||||
return f"{self.gitea_url}/{self.repo_owner}/{self.repo_name}/issues"
|
||||
|
||||
|
||||
@dataclass
|
||||
class GiteaConfigCompat:
|
||||
"""Gitea configuration compatibility layer.
|
||||
|
||||
Provides the same interface as the original GiteaConfig but
|
||||
backed by the unified configuration system.
|
||||
"""
|
||||
|
||||
base_url: str
|
||||
repo_owner: str
|
||||
repo_name: str
|
||||
auth_token: str = ""
|
||||
api_version: str = "v1"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize from unified configuration."""
|
||||
unified = get_unified_config()
|
||||
|
||||
# Map unified config to Gitea format
|
||||
self.base_url = unified.gitea_url
|
||||
self.repo_owner = unified.repo_owner
|
||||
self.repo_name = unified.repo_name
|
||||
self.auth_token = unified.api_token
|
||||
|
||||
# Apply any explicit overrides
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
|
||||
@classmethod
|
||||
def from_tddai_config(cls, tddai_config) -> 'GiteaConfigCompat':
|
||||
"""Create from TDDAI config (for compatibility)."""
|
||||
return cls(
|
||||
base_url=tddai_config.gitea_url,
|
||||
repo_owner=tddai_config.repo_owner,
|
||||
repo_name=tddai_config.repo_name
|
||||
)
|
||||
|
||||
def validate(self) -> None:
|
||||
"""Validate configuration (for compatibility)."""
|
||||
if not self.base_url or not self.base_url.strip():
|
||||
raise ConfigurationError("base_url cannot be empty")
|
||||
|
||||
if not self.repo_owner or not self.repo_owner.strip():
|
||||
raise ConfigurationError("repo_owner cannot be empty")
|
||||
|
||||
if not self.repo_name or not self.repo_name.strip():
|
||||
raise ConfigurationError("repo_name cannot be empty")
|
||||
|
||||
# URL validation
|
||||
if not (self.base_url.startswith('http://') or self.base_url.startswith('https://')):
|
||||
raise ConfigurationError("base_url must start with http:// or https://")
|
||||
|
||||
@property
|
||||
def api_base_url(self) -> str:
|
||||
"""Get API base URL."""
|
||||
return f"{self.base_url}/api/{self.api_version}"
|
||||
|
||||
@property
|
||||
def repo_api_url(self) -> str:
|
||||
"""Get repository API URL."""
|
||||
return f"{self.api_base_url}/repos/{self.repo_owner}/{self.repo_name}"
|
||||
|
||||
@property
|
||||
def issues_api_url(self) -> str:
|
||||
"""Get issues API URL."""
|
||||
return f"{self.repo_api_url}/issues"
|
||||
|
||||
|
||||
def get_tddai_config(**kwargs) -> TddaiConfigCompat:
|
||||
"""Get TDDAI-compatible configuration.
|
||||
|
||||
This function can be used as a drop-in replacement for the original
|
||||
get_config() function in tddai/config.py.
|
||||
|
||||
Args:
|
||||
**kwargs: Override values for configuration fields
|
||||
|
||||
Returns:
|
||||
TDDAI-compatible configuration object
|
||||
"""
|
||||
return TddaiConfigCompat(**kwargs)
|
||||
|
||||
|
||||
def get_gitea_config(**kwargs) -> GiteaConfigCompat:
|
||||
"""Get Gitea-compatible configuration.
|
||||
|
||||
This function can be used as a drop-in replacement for the original
|
||||
GiteaConfig creation.
|
||||
|
||||
Args:
|
||||
**kwargs: Override values for configuration fields
|
||||
|
||||
Returns:
|
||||
Gitea-compatible configuration object
|
||||
"""
|
||||
return GiteaConfigCompat(**kwargs)
|
||||
Reference in New Issue
Block a user