nushell-plugins/scripts/install_from_manifest.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

316 lines
8.7 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
# Distribution Plugin Installer - Install & Register from Manifest
#
# Reads a distribution manifest (JSON) and installs/registers selected plugins.
# Plugins are copied to ~/.local/bin/ and registered with Nushell.
#
# Usage:
# install_from_manifest.nu # Uses DISTRIBUTION_MANIFEST.json
# install_from_manifest.nu --manifest manifest.json
# install_from_manifest.nu --list # List available plugins
# install_from_manifest.nu --all # Install all plugins
# install_from_manifest.nu --preset essential # Install preset
# install_from_manifest.nu --select auth kms # Install specific plugins
# install_from_manifest.nu --check # Dry-run
def main [
--manifest: string = "DISTRIBUTION_MANIFEST.json" # Path to manifest file
--list # List available plugins only
--all # Install all plugins
--preset: string # Use preset: essential, development, full
--select: list<string> # Select specific plugins
--check (-c) # Dry-run, don't make changes
--install-only # Only install, don't register
--register-only # Only register, skip install
] {
log_info "📦 Nushell Distribution Plugin Installer"
log_info "=================================================================="
# Step 1: Load manifest
log_info $"\n📋 Step 1: Loading manifest ($manifest)..."
let manifest_data = load_manifest $manifest
if $manifest_data == null {
return
}
let plugins = $manifest_data.plugins
if ($plugins | length) == 0 {
log_error "No plugins in manifest!"
return
}
log_success $"Loaded ($plugins | length) available plugin\(s\)"
# List mode?
if $list {
display_plugin_list $plugins
return
}
# Step 2: Select plugins
log_info "\n🎯 Step 2: Selecting plugins..."
let selected = if ($preset | is-not-empty) {
get_preset_plugins $plugins $preset
} else if ($select | length) > 0 {
filter_by_name $plugins $select
} else if $all {
$plugins
} else {
interactive_select $plugins
}
if ($selected | length) == 0 {
log_warn "No plugins selected"
return
}
# Step 3: Show selection
log_info ""
log_success $"Selected: ($selected | length) plugin\(s\)"
for plugin in $selected {
log_info $" • ($plugin.name) - ($plugin.purpose)"
}
# Step 4: Dry-run?
if $check {
log_info "\n✅ DRY RUN - No changes made"
return
}
# Step 5: Confirm
print ""
let response = try {
input "Proceed with installation? (yes/no): "
} catch {
"yes"
}
if $response != "yes" {
log_info "Cancelled"
return
}
# Step 6: Install
if not $register_only {
log_info "\n📦 Step 3: Installing plugins..."
install_plugins $selected $manifest_data.source_directory
}
# Step 7: Register
if not $install_only {
log_info "\n🔌 Step 4: Registering with Nushell..."
register_plugins $selected
}
# Summary
log_info "\n=================================================================="
log_success "✅ Complete!"
log_info ""
log_info "Next steps:"
log_info " 1. Restart Nushell: exit && nu"
log_info " 2. Verify: nu -c 'plugin list'"
}
# Load manifest from JSON file
def load_manifest [path: string]: nothing -> record {
if not ($path | path exists) {
log_error $"Manifest not found: ($path)"
return null
}
try {
let content = open $path
return $content
} catch {|err|
log_error $"Failed to load manifest: ($err.msg)"
return null
}
}
# Display plugin list
def display_plugin_list [plugins: list<record>] {
print ""
log_success $"Available plugins: ($plugins | length)"
print ""
for plugin in $plugins {
print $" ✓ ($plugin.name)"
print $" ($plugin.purpose)"
print ""
}
}
# Get preset plugins
def get_preset_plugins [plugins: list<record>, preset: string]: nothing -> list<record> {
match $preset {
"essential" => {
$plugins | where {|p|
$p.name in ["nu_plugin_auth", "nu_plugin_kms", "nu_plugin_orchestrator", "nu_plugin_kcl", "nu_plugin_tera"]
}
}
"development" => {
$plugins | where {|p|
$p.name in ["nu_plugin_auth", "nu_plugin_kms", "nu_plugin_orchestrator", "nu_plugin_kcl", "nu_plugin_tera", "nu_plugin_highlight", "nu_plugin_image", "nu_plugin_clipboard"]
}
}
"full" => {
$plugins
}
_ => {
log_error $"Unknown preset: ($preset)"
log_info "Available: essential, development, full"
[]
}
}
}
# Filter by name
def filter_by_name [plugins: list<record>, names: list<string>]: nothing -> list<record> {
$plugins | where {|p|
if $p.name in $names {
true
} else {
($p.name | str replace "^nu_plugin_" "") in $names
}
}
}
# Interactive select
def interactive_select [plugins: list<record>]: nothing -> list<record> {
print ""
log_info "Available presets:"
log_info " 1. Essential (5 core plugins)"
log_info " 2. Development (8 plugins)"
log_info " 3. All ($($plugins | length) plugins)"
print ""
let choice = try {
input "Select (1-3): "
} catch {
"1"
}
match $choice {
"1" => { get_preset_plugins $plugins "essential" }
"2" => { get_preset_plugins $plugins "development" }
"3" => { $plugins }
_ => { [] }
}
}
# Install plugins to ~/.local/bin/
def install_plugins [selected: list<record>, source_dir: string] {
let install_dir = $"($env.HOME)/.local/bin"
# Ensure directory exists
if not ($install_dir | path exists) {
log_info $"Creating directory: ($install_dir)"
mkdir $install_dir
}
for plugin in $selected {
log_info $"Installing: ($plugin.name)"
try {
let source_path = $"($source_dir)/($plugin.name)"
let target_path = $"($install_dir)/($plugin.name)"
if not ($source_path | path exists) {
log_error $" ✗ Source not found: ($source_path)"
continue
}
cp $source_path $target_path
chmod +x $target_path
# Fix macOS code signing issues
fix_macos_binary $target_path
log_success $" ✓ Installed to ($target_path)"
} catch {|err|
log_error $" ✗ Failed: ($err.msg)"
}
}
}
# Fix macOS code signing (remove quarantine, ad-hoc sign)
def fix_macos_binary [binary_path: string] {
let os_type = $nu.os-info.name
if $os_type == "macos" {
# Remove quarantine attribute
try {
^xattr -d com.apple.quarantine $binary_path out+err>| null
} catch {
# Silently ignore if attribute doesn't exist
}
# Ad-hoc sign the binary
try {
^codesign -s - $binary_path out+err>| null
} catch {
# Silently ignore if codesign fails
}
}
}
# Register plugins with Nushell
def register_plugins [selected: list<record>] {
let install_dir = $"($env.HOME)/.local/bin"
let nu_cmd = $"($install_dir)/nu"
# Fix code signing on nu binary if it exists locally
if ($nu_cmd | path exists) {
fix_macos_binary $nu_cmd
}
for plugin in $selected {
let basename = $plugin.name | str replace "^nu_plugin_" ""
let plugin_path = $"($install_dir)/($plugin.name)"
log_info $"Registering: ($plugin.name)"
try {
# Remove old registration if exists
try {
if ($nu_cmd | path exists) {
^$nu_cmd -c $"plugin rm ($basename)" out+err>| null
} else {
nu -c $"plugin rm ($basename)" out+err>| null
}
} catch {}
# Register new - use local nu if available, otherwise system nu
if ($nu_cmd | path exists) {
^$nu_cmd -c $"plugin add ($plugin_path)"
} else {
nu -c $"plugin add ($plugin_path)"
}
log_success $" ✓ Registered"
} catch {|err|
log_error $" ✗ Failed: ($err.msg)"
}
}
}
# Logging
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)"
}
main