#!/usr/bin/env nu # Update Installed Plugins - Remove Old & Install New Versions # # This script updates plugins that are already installed in ~/.local/bin: # 1. Detects installed plugins in ~/.local/bin # 2. Removes old plugin binaries # 3. Rebuilds them from source # 4. Installs new versions to ~/.local/bin # 5. Registers with nushell # # Usage: # update_installed_plugins.nu # Update all installed plugins # update_installed_plugins.nu --check # Check only, don't update # update_installed_plugins.nu --plugin NAME # Update specific plugin # update_installed_plugins.nu --verify # Verify registration after update use lib/common_lib.nu * # Configuration def get-install-dir []: nothing -> string { $"($env.HOME)/.local/bin" } def get-plugin-dirs []: nothing -> list { ls nu_plugin_* | where type == "dir" | each {|row| { name: $row.name path: $row.name cargo_toml: $"($row.name)/Cargo.toml" } } } # Main entry point def main [ --check (-c) # Check only, don't modify --plugin: string = "" # Update specific plugin (optional) --verify (-v) # Verify registration after update --no-register # Skip registration step --force (-f) # Force rebuild even if no changes ] { log_info "šŸ”„ Plugin Update Manager - Remove Old & Install New" log_info "==================================================================" let install_dir = (get-install-dir) # Check install directory exists if not ($install_dir | path exists) { log_error $"Install directory not found: ($install_dir)" log_info "Creating directory..." mkdir $install_dir } log_info $"šŸ“ Install directory: ($install_dir)" # Step 1: Find installed plugins log_info "\nšŸ“‹ Step 1: Detecting installed plugins..." let installed = detect_installed_plugins $install_dir if ($installed | length) == 0 { log_warn $"No installed plugins found in ($install_dir)" return } log_success $"Found ($installed | length) installed plugin\(s\):" for plugin in $installed { log_info $" • ($plugin.name) \(installed: ($plugin.install_path)\)" } # Step 2: Find source plugins log_info "\nšŸ” Step 2: Finding plugin sources..." let sources = (get-plugin-dirs) if ($sources | length) == 0 { log_error "No plugin sources found in current directory" exit 1 } log_success $"Found ($sources | length) plugin source\(s\)" # Step 3: Match installed with sources log_info "\nšŸ”— Step 3: Matching installed plugins with sources..." let to_update = match_plugins_with_sources $installed $sources $plugin if ($to_update | length) == 0 { if ($plugin | is-not-empty) { log_error $"Plugin not found or not installed: ($plugin)" } else { log_warn "No plugins to update" } return } log_success $"Ready to update ($to_update | length) plugin\(s\):" for item in $to_update { log_info $" • ($item.name)" log_info $" Source: ($item.source.path)" log_info $" Target: ($item.install_path)" } # Confirmation if not $check { print "" log_info "šŸ¤” Proceed with update? (yes/no): " let response = try { input "" } catch { "yes" } if $response != "yes" { log_info "Update cancelled" return } } # Step 4: Remove old binaries if not $check { log_info "\nšŸ—‘ļø Step 4: Removing old plugin binaries..." remove_old_plugins $to_update $install_dir } else { log_info "\nšŸ—‘ļø Step 4 \(DRY RUN\): Would remove old plugin binaries..." for item in $to_update { log_info $" Would remove: ($item.install_path)" } } # Step 5: Rebuild plugins if not $check { log_info "\nšŸ”Ø Step 5: Building updated plugins..." build_plugins $to_update $force } else { log_info "\nšŸ”Ø Step 5 \(DRY RUN\): Would build plugins..." for item in $to_update { log_info $" Would build: ($item.name) in ($item.source.path)" } } # Step 6: Install new binaries if not $check { log_info "\nšŸ“¦ Step 6: Installing new plugin binaries..." install_new_plugins $to_update $install_dir } else { log_info "\nšŸ“¦ Step 6 \(DRY RUN\): Would install new binaries..." for item in $to_update { let binary_name = if ($item.source.name | str starts-with "nu_plugin_") { $item.source.name } else { $"nu_plugin_($item.source.name)" } log_info $" Would install: ($item.source.path)/target/release/($binary_name)" } } # Step 7: Register plugins if not $check and not $no_register { log_info "\nšŸ”Œ Step 7: Registering plugins with nushell..." register_updated_plugins $to_update $install_dir $verify } else if not $check { log_info "\nšŸ”Œ Step 7: Skipped (--no-register)" } else { log_info "\nšŸ”Œ Step 7 \(DRY RUN\): Would register plugins..." } # Final summary log_info "\n==================================================================" if $check { log_info "āœ… DRY RUN COMPLETE - No changes made" } else { log_success "āœ… Plugin update complete!" } log_info "Next steps:" log_info " 1. Restart nushell: exit && nu" log_info " 2. Verify plugins: nu -c 'plugin list'" } # Detect installed plugins in ~/.local/bin def detect_installed_plugins [ install_dir: string ]: nothing -> list { try { ls $install_dir | where type == "file" | where {|row| let basename = $row.name | path basename $basename =~ "^nu_plugin_" } | each {|row| let basename = $row.name | path basename { name: $basename install_path: $row.name } } } catch { [] } } # Match installed plugins with source directories def match_plugins_with_sources [ installed: list sources: list filter_plugin: string ]: nothing -> list { let filtered_sources = if ($filter_plugin | is-not-empty) { $sources | where {|s| if $s.name == $filter_plugin { true } else { $s.name | str ends-with $filter_plugin } } } else { $sources } $installed | where {|inst| let match = $filtered_sources | where {|src| if $inst.name == $src.name { true } else if $inst.name == ($src.name | str replace "^nu_plugin_" "") { true } else { false } } | first $match != null } | each {|inst| let source = $filtered_sources | where {|src| if $inst.name == $src.name { true } else if $inst.name == ($src.name | str replace "^nu_plugin_" "") { true } else { false } } | first { name: $inst.name install_path: $inst.install_path source: $source } } } # Remove old plugin binaries def remove_old_plugins [ to_update: list install_dir: string ] { for item in $to_update { if ($item.install_path | path exists) { log_info $" Removing: ($item.name)" try { rm $item.install_path log_success $" āœ“ Deleted" } catch {|err| log_error $" āœ— Failed: ($err.msg)" } } } } # Build plugins from source def build_plugins [ to_update: list force: bool ] { for item in $to_update { let plugin_name = $item.source.name let plugin_path = $item.source.path log_info $" Building: ($plugin_name)" try { cd $plugin_path # Clean if force rebuild if $force { log_info $" Cleaning build artifacts..." cargo clean } # Build release version log_info $" Compiling..." try { cargo build --release out+err>| grep -E "(Compiling|Finished)" } catch { log_info $" Build output not parseable, continuing..." } cd - | ignore log_success $" āœ“ Built successfully" } catch {|err| log_error $" āœ— Build failed: ($err.msg)" } } } # Install new plugin binaries def install_new_plugins [ to_update: list install_dir: string ] { for item in $to_update { let plugin_name = $item.source.name let plugin_path = $item.source.path let plugin_binary = $"($plugin_path)/target/release/($plugin_name)" let target_path = $item.install_path if not ($plugin_binary | path exists) { log_error $" Build artifact not found: ($plugin_binary)" continue } try { log_info $" Installing: ($plugin_name)" cp $plugin_binary $target_path chmod +x $target_path log_success $" āœ“ Installed to ($target_path)" } catch {|err| log_error $" āœ— Installation failed: ($err.msg)" } } } # Register updated plugins with nushell def register_updated_plugins [ to_update: list install_dir: string verify: bool ] { let results = $to_update | each {|item| let plugin_path = $item.install_path let plugin_name = ($item.name | str replace "^nu_plugin_" "") log_info $" Registering: ($item.name)" let reg_result = try { # Remove old registration if exists try { nu -c $"plugin rm ($plugin_name)" out+err>| null } catch { # Ignore if plugin doesn't exist } # Add new registration nu -c $"plugin add ($plugin_path)" log_success $" āœ“ Registered" # Verify if requested if $verify { try { let found = nu -c $"plugin list | where name =~ ($plugin_name) | length" | into int if $found > 0 { log_success $" āœ“ Verified" {success: true, verified: true} } else { log_warn $" āš ļø Could not verify" {success: true, verified: false} } } catch { log_warn $" āš ļø Verification failed" {success: true, verified: false} } } else { {success: true, verified: null} } } catch {|err| log_error $" āœ— Registration failed: ($err.msg)" {success: false, verified: false} } $reg_result } let registered = ($results | where success | length) let failed = ($results | where {|r| not $r.success} | length) print "" log_success $"šŸ“Š Registration Summary:" log_success $" āœ… Registered: ($registered) plugins" if $failed > 0 { log_error $" āŒ Failed: ($failed) plugins" } } # Common library functions (fallback if not available) def log_info [msg: string] { print $"ā„¹ļø ($msg)" } def log_success [msg: string] { print $"āœ… ($msg)" } def log_error [msg: string] { print $"āŒ ($msg)" } def log_warn [msg: string] { print $"āš ļø ($msg)" } # Call main function main