Files
markitect-main/markitect/issues/manager.py
tegwick fb968dff34 fix: resolve MarkiTect issue handling system integration problems (issue #120)
- Fix issue manager to properly read API token and repo info from main MarkiTect config
- Update Gitea plugin to use correct repository-specific API endpoints
- Correct domain model mapping to only include valid Issue model fields
- Fix presentation layer to safely access optional body attribute
- Enable full functionality for 'markitect issues show' and 'markitect issues list' commands

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-04 02:14:59 +02:00

151 lines
5.0 KiB
Python

"""
Plugin manager for issue backends.
This module handles discovery, loading, and configuration of issue management backends.
"""
import importlib
import yaml
from pathlib import Path
from typing import Dict, Any, Type, Optional
from .base import IssueBackend
from .exceptions import PluginNotFoundError, ConfigurationError
class IssuePluginManager:
"""Manages issue backend plugins and configuration."""
def __init__(self, config_path: Optional[str] = None):
"""
Initialize plugin manager.
Args:
config_path: Optional path to configuration file
"""
self.config = self._load_config(config_path)
self.plugins = self._discover_plugins()
def get_backend(self, backend_name: Optional[str] = None) -> IssueBackend:
"""
Get configured backend instance.
Args:
backend_name: Backend name to use, or None for default
Returns:
IssueBackend instance
Raises:
PluginNotFoundError: If backend not found
"""
backend_name = backend_name or self.config.get('default_backend', 'gitea')
plugin_class = self.plugins.get(backend_name)
if not plugin_class:
raise PluginNotFoundError(f"Unknown backend: {backend_name}")
backend_config = self.config.get('backends', {}).get(backend_name, {})
return plugin_class(backend_config)
def _load_config(self, config_path: Optional[str] = None) -> Dict[str, Any]:
"""
Load configuration from file or return defaults.
Args:
config_path: Path to configuration file
Returns:
Configuration dictionary
"""
from config.manager import UnifiedConfigManager
# Get main markitect configuration to extract API token and settings
try:
config_manager = UnifiedConfigManager()
markitect_config = config_manager.get_config()
main_config = markitect_config.__dict__ if hasattr(markitect_config, '__dict__') else {}
except Exception:
main_config = {}
if config_path is None:
config_path = Path('.markitect/config/issues.yml')
else:
config_path = Path(config_path)
# Extract configuration from main markitect config
gitea_url = main_config.get('gitea_url', 'http://92.205.130.254:32166')
api_token = main_config.get('api_token', '')
repo_owner = main_config.get('repo_owner', 'coulomb')
repo_name = main_config.get('repo_name', 'markitect_project')
# Default configuration using main config values
default_config = {
'default_backend': 'gitea',
'backends': {
'gitea': {
'url': gitea_url,
'token': api_token,
'repo_owner': repo_owner,
'repo_name': repo_name,
'repo': f'{repo_owner}/{repo_name}'
},
'local': {
'directory': '.markitect/issues',
'auto_git': True
}
}
}
if not config_path.exists():
return default_config
try:
with open(config_path, 'r') as f:
config = yaml.safe_load(f) or {}
# Merge with defaults
merged_config = default_config.copy()
merged_config.update(config)
# Ensure gitea backend has token from main config if not overridden
if 'backends' in merged_config and 'gitea' in merged_config['backends']:
gitea_config = merged_config['backends']['gitea']
if not gitea_config.get('token'):
gitea_config['token'] = api_token
if not gitea_config.get('repo_owner'):
gitea_config['repo_owner'] = repo_owner
if not gitea_config.get('repo_name'):
gitea_config['repo_name'] = repo_name
return merged_config
except Exception:
# Return defaults if config loading fails
return default_config
def _discover_plugins(self) -> Dict[str, Type[IssueBackend]]:
"""
Discover available backend plugins.
Returns:
Dictionary mapping backend names to plugin classes
"""
plugins = {}
# Try to import known plugins
plugin_modules = [
('gitea', 'markitect.issues.plugins.gitea', 'GiteaPlugin'),
('local', 'markitect.issues.plugins.local', 'LocalPlugin'),
]
for name, module_path, class_name in plugin_modules:
try:
module = importlib.import_module(module_path)
plugin_class = getattr(module, class_name)
if issubclass(plugin_class, IssueBackend):
plugins[name] = plugin_class
except (ImportError, AttributeError):
# Plugin not available, skip
continue
return plugins