414 lines
12 KiB
Plaintext
414 lines
12 KiB
Plaintext
|
|
#!/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<record> {
|
|||
|
|
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<record> {
|
|||
|
|
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<record>
|
|||
|
|
sources: list<record>
|
|||
|
|
filter_plugin: string
|
|||
|
|
]: nothing -> list<record> {
|
|||
|
|
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<record>
|
|||
|
|
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<record>
|
|||
|
|
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<record>
|
|||
|
|
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<record>
|
|||
|
|
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
|