feat: implement configuration and environment management CLI (issue #18)
Some checks failed
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
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

Complete implementation of configuration management capabilities for MarkiTect CLI:

New CLI Commands:
- markitect config-show: Display current configuration with multiple output formats
- markitect config-set: Set configuration values with validation and persistence
- markitect config-init: Initialize configuration for new project with interactive setup
- markitect config-validate: Validate current configuration and show issues
- markitect config-help: Get help information for configuration keys

Core Features:
- Comprehensive configuration management with multiple sources (files, env vars, defaults)
- Support for YAML, JSON, and simple output formats
- Sensitive data masking for secure configuration display
- Interactive project initialization with intelligent defaults
- Configuration validation with path creation and URL validation
- Environment variable integration with MARKITECT_ prefix
- Nested configuration support with dot notation (e.g., gitea.url)
- Type conversion for boolean, numeric, and string values
- Project-specific configuration files (.markitect.yml/yaml/json)

Technical Implementation:
- ConfigurationManager class with robust error handling
- Integration with existing configuration system
- File-based configuration with automatic format detection
- Configuration validation and help system
- Support for custom configuration file locations
- Graceful fallback when advanced config system unavailable

Configuration Features:
- Multiple file format support (YAML, JSON)
- Environment variable precedence
- Sensitive data protection
- Directory structure validation and creation
- URL and path validation
- Interactive and non-interactive modes

Testing:
- 58 comprehensive tests covering all functionality
- CLI integration tests with isolated environments
- Edge cases: permissions, invalid paths, complex structures
- Configuration file parsing and saving tests
- Environment variable handling tests
- Validation and error handling scenarios

All acceptance criteria fulfilled:
 Configuration display and management
 Project initialization functionality
 Configuration validation
 Integration with existing config system
 Comprehensive test coverage

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-03 10:53:44 +02:00
parent 0982e771e4
commit f6c285b774
3 changed files with 1683 additions and 0 deletions

View File

@@ -29,6 +29,7 @@ from .database import DatabaseManager
from .legacy_compat import LegacyMode, emit_deprecation_warning, legacy_switch_option
from .__version__ import get_version_info, get_release_info
from .batch_processor import BatchProcessor, ProcessingMode, ErrorHandling, create_file_processor
from .config_manager import ConfigurationManager
# Import legacy system components for advanced management
try:
@@ -4744,6 +4745,301 @@ def recursive(config, directory, depth, operation, pattern, error_handling, quie
sys.exit(1)
# Configuration Management Commands - Issue #18
@cli.command(name='config-show')
@click.option('--format', 'output_format', type=click.Choice(['yaml', 'json', 'simple']),
default='yaml', help='Output format for configuration display')
@click.option('--show-sensitive', is_flag=True, help='Show sensitive values (tokens, passwords)')
@pass_config
def config_show(config, output_format, show_sensitive):
"""Display current configuration.
Shows comprehensive configuration information including current settings,
file sources, environment variables, and workspace information.
Examples:
markitect config-show
markitect config-show --format json
markitect config-show --format simple --show-sensitive
"""
try:
config_manager = ConfigurationManager()
output = config_manager.display_config(
show_sensitive=show_sensitive,
output_format=output_format
)
click.echo(output)
except Exception as e:
click.echo(f"Failed to display configuration: {e}", err=True)
if config.get('verbose'):
import traceback
click.echo(traceback.format_exc(), err=True)
sys.exit(1)
@cli.command(name='config-set')
@click.argument('key', type=str)
@click.argument('value', type=str)
@click.option('--config-file', type=click.Path(), help='Target configuration file')
@click.option('--validate/--no-validate', default=True, help='Validate configuration after setting')
@pass_config
def config_set(config, key, value, config_file, validate):
"""Set configuration values.
Sets a configuration value and persists it to a configuration file.
Supports nested keys using dot notation (e.g., 'gitea.url').
Examples:
markitect config-set gitea_url http://localhost:3000
markitect config-set repo_owner myorganization
markitect config-set api_token abc123def456
markitect config-set workspace.dir ./my_workspace
"""
try:
config_manager = ConfigurationManager()
# Set the configuration value
success = config_manager.set_config_value(key, value, config_file)
if success:
click.echo(f"✅ Configuration updated: {key} = {value}")
# Show which file was updated
target_file = config_manager._get_target_config_file(config_file)
click.echo(f"📁 Updated file: {target_file}")
# Validate configuration if requested
if validate:
validation_results = config_manager.validate_configuration()
errors = [r for r in validation_results if r['status'] == 'error']
if errors:
click.echo("⚠️ Configuration validation warnings:")
for error in errors:
click.echo(f"{error['key']}: {error['message']}")
else:
click.echo(f"❌ Failed to set configuration: {key}", err=True)
sys.exit(1)
except ValueError as e:
click.echo(f"❌ Configuration error: {e}", err=True)
sys.exit(1)
except Exception as e:
click.echo(f"❌ Failed to set configuration: {e}", err=True)
if config.get('verbose'):
import traceback
click.echo(traceback.format_exc(), err=True)
sys.exit(1)
@cli.command(name='config-init')
@click.option('--project-dir', type=click.Path(path_type=Path), help='Target project directory')
@click.option('--interactive/--no-interactive', default=True, help='Interactive configuration setup')
@click.option('--force', is_flag=True, help='Overwrite existing configuration')
@pass_config
def config_init(config, project_dir, interactive, force):
"""Initialize configuration for new project.
Creates a new configuration file with sensible defaults and sets up
the necessary directory structure for a MarkiTect project.
Examples:
markitect config-init
markitect config-init --project-dir ./my-project
markitect config-init --no-interactive --force
"""
try:
target_dir = project_dir or Path.cwd()
config_file = target_dir / '.markitect.yml'
# Check if configuration already exists
if config_file.exists() and not force:
click.echo(f"❌ Configuration file already exists: {config_file}")
click.echo(" Use --force to overwrite or choose a different directory")
sys.exit(1)
config_manager = ConfigurationManager()
# Interactive setup if requested
initial_config = {
'gitea_url': 'http://localhost:3000',
'repo_owner': '',
'repo_name': target_dir.name,
'workspace_dir': '.markitect_workspace',
'cache_dir': '.ast_cache',
'tests_dir': 'tests',
'test_file_pattern': 'test_issue_{issue_num}_{scenario}.py',
'claude_code_command': 'claude'
}
if interactive:
click.echo("🔧 Interactive MarkiTect configuration setup")
click.echo(f"📁 Target directory: {target_dir}")
click.echo()
# Prompt for each configuration value
initial_config['gitea_url'] = click.prompt(
'Gitea server URL',
default=initial_config['gitea_url']
)
initial_config['repo_owner'] = click.prompt(
'Repository owner/organization',
default=initial_config['repo_owner']
)
initial_config['repo_name'] = click.prompt(
'Repository name',
default=initial_config['repo_name']
)
if click.confirm('Configure API token now?', default=False):
initial_config['api_token'] = click.prompt(
'API token',
default='',
hide_input=True
)
initial_config['workspace_dir'] = click.prompt(
'Workspace directory',
default=initial_config['workspace_dir']
)
initial_config['tests_dir'] = click.prompt(
'Tests directory',
default=initial_config['tests_dir']
)
# Initialize the project
result = config_manager.initialize_project_config(target_dir, interactive=False)
# Update with interactive values if provided
if interactive:
config_manager._save_config_file(initial_config, config_file)
result['config'] = initial_config
click.echo("✅ MarkiTect project initialized successfully!")
click.echo(f"📄 Configuration file: {result['config_file']}")
click.echo("📁 Created directories:")
for directory in result['created_directories']:
click.echo(f"{directory}")
# Show validation results
validation_results = config_manager.validate_configuration(result['config'])
warnings = [r for r in validation_results if r['status'] == 'warning']
errors = [r for r in validation_results if r['status'] == 'error']
if warnings:
click.echo("⚠️ Configuration warnings:")
for warning in warnings:
click.echo(f"{warning['message']}")
if errors:
click.echo("❌ Configuration errors:")
for error in errors:
click.echo(f"{error['message']}")
else:
click.echo("🎉 Configuration validation passed!")
except Exception as e:
click.echo(f"❌ Failed to initialize configuration: {e}", err=True)
if config.get('verbose'):
import traceback
click.echo(traceback.format_exc(), err=True)
sys.exit(1)
@cli.command(name='config-validate')
@click.option('--verbose', '-v', is_flag=True, help='Show detailed validation information')
@pass_config
def config_validate(config, verbose):
"""Validate current configuration.
Checks the current configuration for common issues, missing required fields,
and validates paths and URLs. Provides suggestions for fixing any problems.
Examples:
markitect config-validate
markitect config-validate --verbose
"""
try:
config_manager = ConfigurationManager()
validation_results = config_manager.validate_configuration()
# Categorize results
errors = [r for r in validation_results if r['status'] == 'error']
warnings = [r for r in validation_results if r['status'] == 'warning']
ok_results = [r for r in validation_results if r['status'] == 'ok']
# Display summary
click.echo(f"📊 Configuration Validation Summary:")
click.echo(f" ✅ OK: {len(ok_results)}")
click.echo(f" ⚠️ Warnings: {len(warnings)}")
click.echo(f" ❌ Errors: {len(errors)}")
click.echo()
# Show errors
if errors:
click.echo("❌ Configuration Errors:")
for error in errors:
click.echo(f"{error['key']}: {error['message']}")
click.echo()
# Show warnings
if warnings:
click.echo("⚠️ Configuration Warnings:")
for warning in warnings:
click.echo(f"{warning['key']}: {warning['message']}")
click.echo()
# Show OK results in verbose mode
if verbose and ok_results:
click.echo("✅ Valid Configuration:")
for ok_result in ok_results:
click.echo(f"{ok_result['key']}: {ok_result['message']}")
click.echo()
# Overall status
if errors:
click.echo("❌ Configuration validation failed")
sys.exit(1)
elif warnings:
click.echo("⚠️ Configuration validation passed with warnings")
else:
click.echo("✅ Configuration validation passed")
except Exception as e:
click.echo(f"❌ Configuration validation failed: {e}", err=True)
if config.get('verbose'):
import traceback
click.echo(traceback.format_exc(), err=True)
sys.exit(1)
@cli.command(name='config-help')
@click.argument('key', required=False)
@pass_config
def config_help(config, key):
"""Get help information for configuration keys.
Provides detailed information about available configuration options,
their purposes, and example values.
Examples:
markitect config-help
markitect config-help gitea_url
markitect config-help api_token
"""
try:
config_manager = ConfigurationManager()
help_text = config_manager.get_config_help(key)
click.echo(help_text)
except Exception as e:
click.echo(f"❌ Failed to get configuration help: {e}", err=True)
sys.exit(1)
# Register issue management commands
cli.add_command(issues_group)