Files
markitect-main/issue_tracker/cli/commands/config.py
tegwick 32d26e7648
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
feat: complete issue-facade capability enhancement and project cleanup
- Update TODO.md to reflect completed issue-facade capability fixes
- Archive old CLI structure files that were moved to capabilities/issue-facade
- Reorganize remaining CLI components into issue_tracker/ package
- Add test coverage for issue #166 substack theme implementation
- Update document manager and markdown command plugins with latest improvements
- Complete project reorganization following capability-based architecture

This commit finalizes the issue-facade capability enhancement project and
ensures the main repository reflects the current state of all completed work.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:53:37 +01:00

325 lines
12 KiB
Python

"""
Configuration management CLI commands.
Provides commands for configuration validation, display, and troubleshooting.
"""
import os
import sys
from pathlib import Path
from typing import Dict, Any, List, Tuple, Optional
from config import (
get_unified_config, get_config_status, MarkitectConfig,
ConfigurationError, ConfigValidationError, load_env_file
)
from ..presenters.config import ConfigPresenter
class ConfigCommands:
"""Configuration management command handlers."""
def __init__(self) -> None:
self.presenter = ConfigPresenter()
def show_config(self, show_sensitive: bool = False) -> None:
"""Display current configuration values."""
try:
config = get_unified_config()
status = get_config_status()
self.presenter.show_configuration(config, status, show_sensitive)
except ConfigurationError as e:
self.presenter.show_error(f"Configuration error: {e}")
sys.exit(1)
except Exception as e:
self.presenter.show_error(f"Unexpected error: {e}")
sys.exit(1)
def validate_config(self, verbose: bool = False) -> None:
"""Validate current configuration and show any issues."""
try:
config = get_unified_config()
validation_results = self._perform_validation_checks(config)
self.presenter.show_validation_results(validation_results, verbose)
# Exit with non-zero code if there are errors
if any(result['status'] == 'error' for result in validation_results):
sys.exit(1)
except ConfigurationError as e:
self.presenter.show_error(f"Configuration error: {e}")
sys.exit(1)
except Exception as e:
self.presenter.show_error(f"Unexpected error: {e}")
sys.exit(1)
def troubleshoot_config(self) -> None:
"""Run comprehensive configuration troubleshooting."""
try:
config = get_unified_config()
status = get_config_status()
# Perform all diagnostic checks
diagnostics = self._run_diagnostics(config)
self.presenter.show_troubleshooting_results(config, status, diagnostics)
except Exception as e:
# Even if config loading fails, we can still provide diagnostics
diagnostics = self._run_basic_diagnostics()
self.presenter.show_troubleshooting_results(None, None, diagnostics)
def check_config_files(self) -> None:
"""Check for configuration files and their status."""
file_checks = self._check_configuration_files()
self.presenter.show_config_file_status(file_checks)
def _perform_validation_checks(self, config: MarkitectConfig) -> List[Dict[str, Any]]:
"""Perform comprehensive configuration validation."""
results = []
# Check required fields
required_fields = [
('gitea_url', 'Gitea/Git platform URL'),
('repo_owner', 'Repository owner'),
('repo_name', 'Repository name'),
]
for field, description in required_fields:
value = getattr(config, field, None)
if not value or (isinstance(value, str) and not value.strip()):
results.append({
'check': f'Required field: {description}',
'status': 'error',
'message': f'{description} is required but not set',
'suggestion': f'Set {field.upper()} in environment or .env.tddai file'
})
else:
results.append({
'check': f'Required field: {description}',
'status': 'success',
'message': f'{description} is properly configured'
})
# Check URL format
if config.gitea_url:
if not (config.gitea_url.startswith('http://') or config.gitea_url.startswith('https://')):
results.append({
'check': 'URL format validation',
'status': 'error',
'message': 'Gitea URL must start with http:// or https://',
'suggestion': 'Update gitea_url to include protocol (e.g., https://github.com)'
})
else:
results.append({
'check': 'URL format validation',
'status': 'success',
'message': 'Gitea URL format is valid'
})
# Check workspace directory
workspace_path = Path(config.workspace_dir)
if workspace_path.exists() and not workspace_path.is_dir():
results.append({
'check': 'Workspace directory',
'status': 'error',
'message': f'Workspace path exists but is not a directory: {workspace_path}',
'suggestion': 'Remove the file or choose a different workspace directory'
})
else:
results.append({
'check': 'Workspace directory',
'status': 'success',
'message': f'Workspace directory is valid: {workspace_path}'
})
# Check authentication token
auth_token = os.getenv('GITEA_API_TOKEN') or os.getenv('GITHUB_TOKEN')
if not auth_token:
results.append({
'check': 'Authentication token',
'status': 'warning',
'message': 'No authentication token found',
'suggestion': 'Set GITEA_API_TOKEN or GITHUB_TOKEN environment variable for API access'
})
else:
results.append({
'check': 'Authentication token',
'status': 'success',
'message': 'Authentication token is configured'
})
return results
def _run_diagnostics(self, config: Optional[MarkitectConfig]) -> Dict[str, Any]:
"""Run comprehensive diagnostics."""
diagnostics = {}
# Environment diagnostics
diagnostics['environment'] = self._check_environment()
# File system diagnostics
diagnostics['filesystem'] = self._check_filesystem()
# Configuration files diagnostics
diagnostics['config_files'] = self._check_configuration_files()
# Git repository diagnostics
diagnostics['git_repository'] = self._check_git_repository()
# Network diagnostics (if config available)
if config:
diagnostics['network'] = self._check_network_connectivity(config)
return diagnostics
def _run_basic_diagnostics(self) -> Dict[str, Any]:
"""Run basic diagnostics when config loading fails."""
return {
'environment': self._check_environment(),
'filesystem': self._check_filesystem(),
'config_files': self._check_configuration_files(),
'git_repository': self._check_git_repository(),
}
def _check_environment(self) -> Dict[str, Any]:
"""Check environment variables and settings."""
relevant_vars = [
'TDDAI_GITEA_URL', 'TDDAI_REPO_OWNER', 'TDDAI_REPO_NAME',
'TDDAI_WORKSPACE_DIR', 'GITEA_API_TOKEN', 'GITHUB_TOKEN',
'PYTHONPATH', 'PATH'
]
env_status = {}
for var in relevant_vars:
value = os.getenv(var)
env_status[var] = {
'set': value is not None,
'value': '***HIDDEN***' if 'TOKEN' in var and value else value,
'length': len(value) if value else 0
}
return {
'python_version': sys.version,
'python_executable': sys.executable,
'current_directory': str(Path.cwd()),
'environment_variables': env_status
}
def _check_filesystem(self) -> Dict[str, Any]:
"""Check file system permissions and paths."""
current_dir = Path.cwd()
return {
'current_directory': {
'path': str(current_dir),
'exists': current_dir.exists(),
'readable': os.access(current_dir, os.R_OK),
'writable': os.access(current_dir, os.W_OK),
},
'home_directory': {
'path': str(Path.home()),
'exists': Path.home().exists(),
'readable': os.access(Path.home(), os.R_OK),
'writable': os.access(Path.home(), os.W_OK),
}
}
def _check_configuration_files(self) -> Dict[str, Any]:
"""Check for configuration files and their status."""
config_files = {
'.env.tddai': Path('.env.tddai'),
'.env': Path('.env'),
'pyproject.toml': Path('pyproject.toml'),
'tddai-setup.sh': Path('tddai-setup.sh'),
}
file_status = {}
for name, path in config_files.items():
file_status[name] = {
'path': str(path),
'exists': path.exists(),
'readable': path.exists() and os.access(path, os.R_OK),
'size': path.stat().st_size if path.exists() else 0,
'modified': path.stat().st_mtime if path.exists() else None
}
# Try to parse .env files
if name.startswith('.env') and path.exists():
try:
env_vars = load_env_file(path)
file_status[name]['parsed_variables'] = len(env_vars)
file_status[name]['parse_error'] = None
except Exception as e:
file_status[name]['parsed_variables'] = 0
file_status[name]['parse_error'] = str(e)
return file_status
def _check_git_repository(self) -> Dict[str, Any]:
"""Check git repository status."""
git_dir = Path('.git')
status = {
'is_git_repository': git_dir.exists(),
'git_directory': str(git_dir),
}
if git_dir.exists():
try:
import subprocess
# Get remote origin URL
result = subprocess.run(
['git', 'remote', 'get-url', 'origin'],
capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
status['remote_origin'] = result.stdout.strip()
# Get current branch
result = subprocess.run(
['git', 'branch', '--show-current'],
capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
status['current_branch'] = result.stdout.strip()
except (subprocess.TimeoutExpired, FileNotFoundError):
status['git_command_available'] = False
return status
def _check_network_connectivity(self, config: MarkitectConfig) -> Dict[str, Any]:
"""Check network connectivity to configured services."""
status = {}
if config.gitea_url:
try:
import urllib.request
import urllib.parse
parsed_url = urllib.parse.urlparse(config.gitea_url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
req = urllib.request.Request(base_url)
req.add_header('User-Agent', 'tddai-config-check/1.0')
with urllib.request.urlopen(req, timeout=10) as response:
status['gitea_connectivity'] = {
'url': base_url,
'status_code': response.getcode(),
'reachable': True
}
except Exception as e:
status['gitea_connectivity'] = {
'url': config.gitea_url,
'reachable': False,
'error': str(e)
}
return status