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
316 lines
8.7 KiB
Plaintext
Executable File
316 lines
8.7 KiB
Plaintext
Executable File
#!/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
|