nushell-plugins/scripts/update_installed_plugins.nu
Jesús Pérez 4b92aa764a
Some checks failed
Build and Test / Validate Setup (push) Has been cancelled
Build and Test / Build (darwin-amd64) (push) Has been cancelled
Build and Test / Build (darwin-arm64) (push) Has been cancelled
Build and Test / Build (linux-amd64) (push) Has been cancelled
Build and Test / Build (windows-amd64) (push) Has been cancelled
Build and Test / Build (linux-arm64) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Package Results (push) Has been cancelled
Build and Test / Quality Gate (push) Has been cancelled
implements a production-ready bootstrap installer with comprehensive error handling, version-agnostic archive extraction, and clear user messaging. All improvements follow DRY principles using symlink-based architecture for single-source-of-truth maintenance
2025-12-11 22:04:54 +00:00

414 lines
12 KiB
Plaintext
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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