From f7a9e19a9a6c95bc05003b171db2e633805a6228 Mon Sep 17 00:00:00 2001 From: Mithun Gowda B Date: Wed, 23 Jul 2025 17:36:15 +0530 Subject: [PATCH] Fixed installer.py Fixed Update issue added missing function update_components() to installer.py Signed-off-by: Mithun Gowda B --- setup/base/installer.py | 137 +++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 58 deletions(-) diff --git a/setup/base/installer.py b/setup/base/installer.py index 4c2d2c3..7f3eb99 100644 --- a/setup/base/installer.py +++ b/setup/base/installer.py @@ -9,10 +9,13 @@ import tempfile from datetime import datetime from .component import Component + class Installer: """Main installer orchestrator""" - - def __init__(self, install_dir: Optional[Path] = None, dry_run: bool = False): + + def __init__(self, + install_dir: Optional[Path] = None, + dry_run: bool = False): """ Initialize installer @@ -25,10 +28,12 @@ class Installer: self.dry_run = dry_run self.components: Dict[str, Component] = {} self.installed_components: Set[str] = set() + self.updated_components: Set[str] = set() + self.failed_components: Set[str] = set() self.skipped_components: Set[str] = set() self.backup_path: Optional[Path] = None - + def register_component(self, component: Component) -> None: """ Register a component for installation @@ -38,7 +43,7 @@ class Installer: """ metadata = component.get_metadata() self.components[metadata['name']] = component - + def register_components(self, components: List[Component]) -> None: """ Register multiple components @@ -48,7 +53,7 @@ class Installer: """ for component in components: self.register_component(component) - + def resolve_dependencies(self, component_names: List[str]) -> List[str]: """ Resolve component dependencies in correct installation order @@ -64,32 +69,33 @@ class Installer: """ resolved = [] resolving = set() - + def resolve(name: str): if name in resolved: return - + if name in resolving: - raise ValueError(f"Circular dependency detected involving {name}") - + raise ValueError( + f"Circular dependency detected involving {name}") + if name not in self.components: raise ValueError(f"Unknown component: {name}") - + resolving.add(name) - + # Resolve dependencies first for dep in self.components[name].get_dependencies(): resolve(dep) - + resolving.remove(name) resolved.append(name) - + # Resolve each requested component for name in component_names: resolve(name) - + return resolved - + def validate_system_requirements(self) -> Tuple[bool, List[str]]: """ Validate system requirements for all registered components @@ -98,16 +104,18 @@ class Installer: Tuple of (success: bool, error_messages: List[str]) """ errors = [] - + # Check disk space (500MB minimum) try: stat = shutil.disk_usage(self.install_dir.parent) free_mb = stat.free / (1024 * 1024) if free_mb < 500: - errors.append(f"Insufficient disk space: {free_mb:.1f}MB free (500MB required)") + errors.append( + f"Insufficient disk space: {free_mb:.1f}MB free (500MB required)" + ) except Exception as e: errors.append(f"Could not check disk space: {e}") - + # Check write permissions test_file = self.install_dir / ".write_test" try: @@ -116,9 +124,9 @@ class Installer: test_file.unlink() except Exception as e: errors.append(f"No write permission to {self.install_dir}: {e}") - + return len(errors) == 0, errors - + def create_backup(self) -> Optional[Path]: """ Create backup of existing installation @@ -128,26 +136,26 @@ class Installer: """ if not self.install_dir.exists(): return None - + if self.dry_run: return self.install_dir / "backup_dryrun.tar.gz" - + # Create backup directory backup_dir = self.install_dir / "backups" backup_dir.mkdir(exist_ok=True) - + # Create timestamped backup timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") backup_name = f"superclaude_backup_{timestamp}" backup_path = backup_dir / f"{backup_name}.tar.gz" - + # Create temporary directory for backup with tempfile.TemporaryDirectory() as temp_dir: temp_backup = Path(temp_dir) / backup_name - + # Ensure temp backup directory exists temp_backup.mkdir(parents=True, exist_ok=True) - + # Copy all files except backups directory for item in self.install_dir.iterdir(): if item.name != "backups": @@ -159,24 +167,23 @@ class Installer: except Exception as e: # Log warning but continue backup process print(f"Warning: Could not backup {item.name}: {e}") - + # Create archive only if there are files to backup if any(temp_backup.iterdir()): - shutil.make_archive( - backup_path.with_suffix(''), - 'gztar', - temp_dir, - backup_name - ) + shutil.make_archive(backup_path.with_suffix(''), 'gztar', + temp_dir, backup_name) else: # Create empty backup file to indicate backup was attempted backup_path.touch() - print(f"Warning: No files to backup, created empty backup marker: {backup_path.name}") - + print( + f"Warning: No files to backup, created empty backup marker: {backup_path.name}" + ) + self.backup_path = backup_path return backup_path - - def install_component(self, component_name: str, config: Dict[str, Any]) -> bool: + + def install_component(self, component_name: str, + config: Dict[str, Any]) -> bool: """ Install a single component @@ -189,13 +196,13 @@ class Installer: """ if component_name not in self.components: raise ValueError(f"Unknown component: {component_name}") - + component = self.components[component_name] - + # Skip if already installed if component_name in self.installed_components: return True - + # Check prerequisites success, errors = component.validate_prerequisites() if not success: @@ -204,7 +211,7 @@ class Installer: print(f" - {error}") self.failed_components.add(component_name) return False - + # Perform installation try: if self.dry_run: @@ -212,20 +219,23 @@ class Installer: success = True else: success = component.install(config) - + if success: self.installed_components.add(component_name) + self.updated_components.add(component_name) else: self.failed_components.add(component_name) - + return success - + except Exception as e: print(f"Error installing {component_name}: {e}") self.failed_components.add(component_name) return False - - def install_components(self, component_names: List[str], config: Optional[Dict[str, Any]] = None) -> bool: + + def install_components(self, + component_names: List[str], + config: Optional[Dict[str, Any]] = None) -> bool: """ Install multiple components in dependency order @@ -237,14 +247,14 @@ class Installer: True if all successful, False if any failed """ config = config or {} - + # Resolve dependencies try: ordered_names = self.resolve_dependencies(component_names) except ValueError as e: print(f"Dependency resolution error: {e}") return False - + # Validate system requirements success, errors = self.validate_system_requirements() if not success: @@ -252,12 +262,12 @@ class Installer: for error in errors: print(f" - {error}") return False - + # Create backup if updating if self.install_dir.exists() and not self.dry_run: print("Creating backup of existing installation...") self.create_backup() - + # Install each component all_success = True for name in ordered_names: @@ -265,21 +275,21 @@ class Installer: if not self.install_component(name, config): all_success = False # Continue installing other components even if one fails - + if not self.dry_run: self._run_post_install_validation() - + return all_success - + def _run_post_install_validation(self) -> None: """Run post-installation validation for all installed components""" print("\nRunning post-installation validation...") - + all_valid = True for name in self.installed_components: component = self.components[name] success, errors = component.validate_installation() - + if success: print(f" ✓ {name}: Valid") else: @@ -287,16 +297,20 @@ class Installer: for error in errors: print(f" - {error}") all_valid = False - + if all_valid: print("\nAll components validated successfully!") else: print("\nSome components failed validation. Check errors above.") - + def update_components(self, component_names: List[str], config: Dict[str, Any]) -> bool: + """Alias for update operation (uses install logic)""" + return self.install_components(component_names, config) + + def get_installation_summary(self) -> Dict[str, Any]: """ Get summary of installation results - + Returns: Dict with installation statistics and results """ @@ -308,3 +322,10 @@ class Installer: 'install_dir': str(self.install_dir), 'dry_run': self.dry_run } + + def get_update_summary(self) -> Dict[str, Any]: + return { + 'updated': list(self.updated_components), + 'failed': list(self.failed_components), + 'backup_path': str(self.backup_path) if self.backup_path else None + }