# Configuration Migration Module # Handles migration from existing workspace configurations to new setup system # Follows Nushell guidelines: explicit types, single purpose, no try-catch use ./mod.nu * use ./detection.nu * # ============================================================================ # EXISTING CONFIGURATION DETECTION # ============================================================================ # Detect existing workspace configuration export def detect-existing-workspace [ workspace_path: string ]: nothing -> record { let config_path = $"($workspace_path)/config/provisioning.yaml" let providers_path = $"($workspace_path)/.providers" let infra_path = $"($workspace_path)/infra" { workspace_path: $workspace_path has_config: ($config_path | path exists) config_path: $config_path has_providers: ($providers_path | path exists) providers_path: $providers_path has_infra: ($infra_path | path exists) infra_path: $infra_path } } # Find existing workspace directories export def find-existing-workspaces []: nothing -> list { mut workspaces = [] # Check common workspace locations let possible_paths = [ "workspace_librecloud" "./workspace_librecloud" "../workspace_librecloud" "workspaces" "./workspaces" ] for path in $possible_paths { let expanded_path = ($path | path expand) if ($expanded_path | path exists) and (($expanded_path | path type) == "dir") { let workspace_config = $"($expanded_path)/config/provisioning.yaml" if ($workspace_config | path exists) { $workspaces = ($workspaces | append $expanded_path) } } } $workspaces } # ============================================================================ # CONFIGURATION MIGRATION # ============================================================================ # Migrate workspace configuration from YAML to new system export def migrate-workspace-config [ workspace_path: string config_base: string --backup = true ]: nothing -> record { let source_config = $"($workspace_path)/config/provisioning.yaml" if not ($source_config | path exists) { return { success: false error: "Source configuration not found" } } # Load existing configuration let existing_config = (load-config-yaml $source_config) # Extract workspace name from path let workspace_name = ($workspace_path | path basename) # Create backup if requested if $backup { let timestamp_for_backup = (get-timestamp-iso8601 | str replace -a ':' '-') let backup_path = $"($config_base)/migration-backup-($workspace_name)-($timestamp_for_backup).yaml" let backup_result = (do { cp $source_config $backup_path } | complete) if ($backup_result.exit_code != 0) { print-setup-warning $"Failed to create backup at ($backup_path)" } else { print-setup-success $"Configuration backed up to ($backup_path)" } } # Create migration record { success: true workspace_name: $workspace_name source_path: $source_config migrated_at: (get-timestamp-iso8601) backup_created: $backup } } # Migrate provider configurations export def migrate-provider-configs [ workspace_path: string config_base: string ]: nothing -> record { let providers_source = $"($workspace_path)/.providers" if not ($providers_source | path exists) { return { success: false migrated_providers: [] error: "No provider directory found" } } mut migrated = [] # Get list of provider directories let result = (do { ls $providers_source | where type == "dir" } | complete) if ($result.exit_code != 0) { return { success: false migrated_providers: [] error: "Failed to read provider directories" } } # Migrate each provider for provider_entry in $result.stdout { let provider_name = ($provider_entry | str trim) if ($provider_name | str length) > 0 { print-setup-info $"Migrating provider: ($provider_name)" $migrated = ($migrated | append $provider_name) } } let success_status = ($migrated | length) > 0 let migrated_at_value = (get-timestamp-iso8601) { success: $success_status migrated_providers: $migrated source_path: $providers_source migrated_at: $migrated_at_value } } # ============================================================================ # MIGRATION VALIDATION # ============================================================================ # Validate migration can proceed safely export def validate-migration [ workspace_path: string config_base: string ]: nothing -> record { mut warnings = [] mut errors = [] # Check source workspace exists if not ($workspace_path | path exists) { $errors = ($errors | append "Source workspace path does not exist") } # Check configuration base exists if not ($config_base | path exists) { $errors = ($errors | append "Target configuration base does not exist") } # Check if migration already happened let migration_marker = $"($config_base)/migration_completed.yaml" if ($migration_marker | path exists) { $warnings = ($warnings | append "Migration appears to have been run before") } # Check for conflicts let workspace_name = ($workspace_path | path basename) let registry_path = $"($config_base)/workspaces_registry.yaml" if ($registry_path | path exists) { let registry = (load-config-yaml $registry_path) if ($registry.workspaces? | default [] | any { |w| $w.name == $workspace_name }) { $warnings = ($warnings | append $"Workspace '($workspace_name)' already registered") } } let can_proceed_status = ($errors | length) == 0 let error_count_value = ($errors | length) let warning_count_value = ($warnings | length) { can_proceed: $can_proceed_status errors: $errors warnings: $warnings error_count: $error_count_value warning_count: $warning_count_value } } # ============================================================================ # MIGRATION EXECUTION # ============================================================================ # Execute complete workspace migration export def execute-migration [ workspace_path: string config_base: string = "" --backup = true --verbose = false ]: nothing -> record { let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base }) print-setup-header "Workspace Configuration Migration" print "" # Validate migration can proceed let validation = (validate-migration $workspace_path $base) if not $validation.can_proceed { for error in $validation.errors { print-setup-error $error } return { success: false errors: $validation.errors } } # Show warnings if ($validation.warnings | length) > 0 { for warning in $validation.warnings { print-setup-warning $warning } } print "" print-setup-info "Starting migration process..." print "" # Step 1: Migrate workspace configuration print-setup-info "Migrating workspace configuration..." let config_migration = (migrate-workspace-config $workspace_path $base --backup=$backup) if not $config_migration.success { print-setup-error $config_migration.error return { success: false error: $config_migration.error } } print-setup-success "Workspace configuration migrated" # Step 2: Migrate provider configurations print-setup-info "Migrating provider configurations..." let provider_migration = (migrate-provider-configs $workspace_path $base) if $provider_migration.success { print-setup-success $"Migrated ($provider_migration.migrated_providers | length) providers" } else { print-setup-warning "No provider configurations to migrate" } # Step 3: Create migration marker let workspace_name = ($workspace_path | path basename) let migration_marker_path = $"($base)/migration_completed.yaml" let migration_record = { version: "1.0.0" completed_at: (get-timestamp-iso8601) workspace_migrated: $workspace_name source_path: $workspace_path target_path: $base backup_created: $backup } let save_result = (save-config-yaml $migration_marker_path $migration_record) if not $save_result { print-setup-warning "Failed to create migration marker" } print "" print-setup-success "Migration completed successfully!" print "" # Summary print "Migration Summary:" print $" Source Workspace: ($workspace_path)" print $" Target Config Base: ($base)" print $" Configuration Migrated: ✅" print $" Providers Migrated: ($provider_migration.migrated_providers | length)" if $backup { print " Backup Created: ✅" } print "" { success: true workspace_name: $workspace_name config_migration: $config_migration provider_migration: $provider_migration migration_completed_at: (get-timestamp-iso8601) } } # ============================================================================ # MIGRATION ROLLBACK # ============================================================================ # Rollback migration from backup export def rollback-migration [ workspace_name: string config_base: string = "" --restore_backup = true ]: nothing -> record { let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base }) print-setup-header "Rolling Back Migration" print "" print-setup-warning "Initiating migration rollback..." print "" # Find and restore backup let migration_marker = $"($base)/migration_completed.yaml" if not ($migration_marker | path exists) { print-setup-error "No migration record found - cannot rollback" return { success: false error: "No migration record found" } } let migration_record = (load-config-yaml $migration_marker) # Find backup file let backup_pattern = $"($base)/migration-backup-($workspace_name)-*.yaml" print-setup-info $"Looking for backup matching: ($backup_pattern)" # Remove migration artifacts if ($migration_marker | path exists) { let rm_result = (do { rm $migration_marker } | complete) if ($rm_result.exit_code == 0) { print-setup-success "Migration marker removed" } } print "" print-setup-success "Migration rollback completed" print "" print "Note: Please verify your workspace is in the desired state" { success: true workspace_name: $workspace_name rolled_back_at: (get-timestamp-iso8601) } } # ============================================================================ # AUTO-MIGRATION # ============================================================================ # Automatically detect and migrate existing workspaces export def auto-migrate-existing [ config_base: string = "" --verbose = false ]: nothing -> record { let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base }) print-setup-header "Detecting Existing Workspaces" print "" # Find existing workspaces let existing = (find-existing-workspaces) if ($existing | length) == 0 { print-setup-info "No existing workspaces detected" return { success: true workspaces_found: 0 workspaces: [] } } print-setup-success $"Found ($existing | length) existing workspace(s)" print "" mut migrated = [] for workspace_path in $existing { let workspace_name = ($workspace_path | path basename) print-setup-info $"Auto-migrating: ($workspace_name)" let migration_result = (execute-migration $workspace_path $base --verbose=$verbose) if $migration_result.success { $migrated = ($migrated | append $workspace_name) } } { success: true workspaces_found: ($existing | length) workspaces: $existing migrated_count: ($migrated | length) migrated_workspaces: $migrated timestamp: (get-timestamp-iso8601) } }