563 lines
16 KiB
Bash
Raw Permalink Normal View History

feat: Add complete Nushell full distribution system ## Major Features Added - **Complete distribution infrastructure**: Build, package, and distribute Nushell binary alongside plugins - **Zero-prerequisite installation**: Bootstrap installers work on fresh systems without Rust/Cargo/Nushell - **Cross-platform support**: Linux, macOS, Windows (x86_64, ARM64) - **Self-contained packages**: Everything needed for complete Nushell environment ## New Components ### Build System - `scripts/build_nushell.nu` - Build nushell with all workspace plugins - `scripts/collect_full_binaries.nu` - Collect nu binary + all plugins - `justfiles/full_distro.just` - 40+ new recipes for distribution workflows ### Bootstrap Installers (Zero Prerequisites) - `installers/bootstrap/install.sh` - Universal POSIX installer (900+ lines) - `installers/bootstrap/install.ps1` - Windows PowerShell installer (800+ lines) - Complete platform detection, PATH setup, plugin registration ### Distribution System - `scripts/create_distribution_packages.nu` - Multi-platform package creator - `scripts/install_full_nushell.nu` - Advanced nu-based installer - `scripts/verify_installation.nu` - Installation verification suite - `scripts/lib/common_lib.nu` - Shared utilities and logging ### Configuration Management - `scripts/templates/default_config.nu` - Complete nushell configuration (500+ lines) - `scripts/templates/default_env.nu` - Cross-platform environment setup - `etc/distribution_config.toml` - Central distribution settings - `scripts/templates/uninstall.sh` & `uninstall.ps1` - Clean removal ## Key Workflows ```bash just build-full # Build nushell + all plugins just pack-full-all # Create packages for all platforms just verify-full # Verify installation just release-full-cross # Complete cross-platform release ``` ## Installation Experience - One-liner: `curl -sSf https://your-url/install.sh | sh` - Multiple modes: user, system, portable installation - Automatic plugin registration with verification - Professional uninstall capability ## Benefits - ✅ Solves bootstrap problem - no prerequisites needed - ✅ Production-ready distribution system - ✅ Complete cross-platform support - ✅ Professional installation experience - ✅ Integrates seamlessly with existing plugin workflows - ✅ Enterprise-grade verification and logging
2025-09-24 18:52:07 +01:00
#!/bin/bash
# Universal Nushell + Plugins Uninstaller
# POSIX compliant shell script that cleanly removes Nushell and plugins installation
#
# This script:
# - Detects installation locations (user ~/.local/bin or system /usr/local/bin)
# - Removes Nushell binary and plugin binaries
# - Cleans up configuration files (with backup option)
# - Removes PATH entries from shell configuration files
# - Unregisters plugins from Nushell
# - Provides detailed removal report
set -e # Exit on error
# Configuration
INSTALL_DIR_USER="$HOME/.local/bin"
INSTALL_DIR_SYSTEM="/usr/local/bin"
CONFIG_DIR="$HOME/.config/nushell"
BACKUP_SUFFIX="uninstall-backup-$(date +%Y%m%d_%H%M%S)"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
printf "${BLUE} %s${NC}\\n" "$1"
}
log_success() {
printf "${GREEN}✅ %s${NC}\\n" "$1"
}
log_warn() {
printf "${YELLOW}⚠️ %s${NC}\\n" "$1"
}
log_error() {
printf "${RED}❌ %s${NC}\\n" "$1" >&2
}
log_debug() {
if [ "$DEBUG" = "1" ]; then
printf "${PURPLE}🐛 DEBUG: %s${NC}\\n" "$1"
fi
}
# Usage information
usage() {
cat << EOF
Nushell Full Distribution Uninstaller
USAGE:
$0 [OPTIONS]
OPTIONS:
-h, --help Show this help message
-y, --yes Non-interactive mode (assume yes to prompts)
-k, --keep-config Keep configuration files (don't remove ~/.config/nushell)
-b, --backup-config Backup configuration before removal
--system Remove from system location (/usr/local/bin) - requires sudo
--user Remove from user location (~/.local/bin) - default
--dry-run Show what would be removed without actually removing
--debug Enable debug output
EXAMPLES:
$0 # Interactive removal from user location
$0 -y # Non-interactive removal
$0 --backup-config -y # Remove with config backup
$0 --system # Remove system installation (needs sudo)
$0 --dry-run # Show what would be removed
EOF
}
# Parse command line arguments
INTERACTIVE=1
KEEP_CONFIG=0
BACKUP_CONFIG=0
SYSTEM_INSTALL=0
DRY_RUN=0
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
usage
exit 0
;;
-y|--yes)
INTERACTIVE=0
shift
;;
-k|--keep-config)
KEEP_CONFIG=1
shift
;;
-b|--backup-config)
BACKUP_CONFIG=1
shift
;;
--system)
SYSTEM_INSTALL=1
shift
;;
--user)
SYSTEM_INSTALL=0
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
--debug)
DEBUG=1
shift
;;
*)
log_error "Unknown option: $1"
usage
exit 1
;;
esac
done
# Determine installation directory
if [ "$SYSTEM_INSTALL" = "1" ]; then
INSTALL_DIR="$INSTALL_DIR_SYSTEM"
log_info "Targeting system installation: $INSTALL_DIR"
# Check if we need sudo
if [ "$(id -u)" -ne 0 ] && [ "$DRY_RUN" = "0" ]; then
log_warn "System installation requires administrative privileges"
if [ "$INTERACTIVE" = "1" ]; then
printf "Continue with sudo? [y/N]: "
read -r response
case "$response" in
[yY]|[yY][eE][sS])
;;
*)
log_info "Uninstallation cancelled"
exit 0
;;
esac
fi
# Re-run with sudo if not already root
if [ "$(id -u)" -ne 0 ]; then
log_info "Re-running with sudo..."
exec sudo "$0" "$@"
fi
fi
else
INSTALL_DIR="$INSTALL_DIR_USER"
log_info "Targeting user installation: $INSTALL_DIR"
fi
# Detection functions
detect_nushell_installation() {
local install_dir="$1"
local found_items=""
log_debug "Detecting Nushell installation in $install_dir"
# Check for nu binary
if [ -f "$install_dir/nu" ]; then
found_items="$found_items nu"
log_debug "Found nu binary: $install_dir/nu"
fi
# Check for plugin binaries
for plugin_binary in "$install_dir"/nu_plugin_*; do
if [ -f "$plugin_binary" ]; then
local plugin_name=$(basename "$plugin_binary")
found_items="$found_items $plugin_name"
log_debug "Found plugin binary: $plugin_binary"
fi
done
echo "$found_items"
}
detect_config_installation() {
local config_dir="$1"
local found_items=""
log_debug "Detecting configuration in $config_dir"
if [ -d "$config_dir" ]; then
# Check for main config files
for config_file in config.nu env.nu distribution_config.toml; do
if [ -f "$config_dir/$config_file" ]; then
found_items="$found_items $config_file"
log_debug "Found config file: $config_dir/$config_file"
fi
done
# Check for plugin registration
if [ -f "$config_dir/plugin.nu" ] || [ -f "$config_dir/registry.dat" ]; then
found_items="$found_items plugin-registry"
log_debug "Found plugin registry files"
fi
fi
echo "$found_items"
}
detect_shell_profile_entries() {
local install_dir="$1"
local found_profiles=""
log_debug "Detecting shell profile entries for $install_dir"
# Check common shell profile files
for profile in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.profile" "$HOME/.bash_profile"; do
if [ -f "$profile" ]; then
if grep -q "$install_dir" "$profile" 2>/dev/null; then
found_profiles="$found_profiles $(basename "$profile")"
log_debug "Found PATH entry in: $profile"
fi
fi
done
echo "$found_profiles"
}
# Removal functions
remove_binaries() {
local install_dir="$1"
local binaries="$2"
local removed_count=0
log_info "Removing binaries from $install_dir..."
for binary in $binaries; do
local binary_path="$install_dir/$binary"
if [ -f "$binary_path" ]; then
log_info "Removing binary: $binary"
if [ "$DRY_RUN" = "0" ]; then
rm -f "$binary_path"
if [ $? -eq 0 ]; then
log_success "Removed: $binary"
removed_count=$((removed_count + 1))
else
log_error "Failed to remove: $binary"
fi
else
log_info "[DRY RUN] Would remove: $binary_path"
removed_count=$((removed_count + 1))
fi
fi
done
log_success "Removed $removed_count binaries"
}
backup_configuration() {
local config_dir="$1"
local backup_dir="$config_dir.$BACKUP_SUFFIX"
if [ -d "$config_dir" ]; then
log_info "Backing up configuration to: $backup_dir"
if [ "$DRY_RUN" = "0" ]; then
cp -r "$config_dir" "$backup_dir"
if [ $? -eq 0 ]; then
log_success "Configuration backed up successfully"
else
log_error "Failed to backup configuration"
return 1
fi
else
log_info "[DRY RUN] Would backup configuration to: $backup_dir"
fi
else
log_info "No configuration directory to backup"
fi
}
remove_configuration() {
local config_dir="$1"
local config_files="$2"
if [ "$KEEP_CONFIG" = "1" ]; then
log_info "Keeping configuration files as requested"
return 0
fi
if [ "$BACKUP_CONFIG" = "1" ]; then
backup_configuration "$config_dir"
fi
if [ -d "$config_dir" ]; then
log_info "Removing configuration directory: $config_dir"
# Ask for confirmation if interactive and not just removing empty dir
if [ "$INTERACTIVE" = "1" ] && [ -n "$config_files" ]; then
printf "Remove configuration directory $config_dir? [y/N]: "
read -r response
case "$response" in
[yY]|[yY][eE][sS])
;;
*)
log_info "Keeping configuration directory"
return 0
;;
esac
fi
if [ "$DRY_RUN" = "0" ]; then
rm -rf "$config_dir"
if [ $? -eq 0 ]; then
log_success "Configuration directory removed"
else
log_error "Failed to remove configuration directory"
fi
else
log_info "[DRY RUN] Would remove configuration directory: $config_dir"
fi
else
log_info "No configuration directory found"
fi
}
remove_shell_profile_entries() {
local install_dir="$1"
local profiles="$2"
if [ -z "$profiles" ]; then
log_info "No shell profile entries found"
return 0
fi
log_info "Removing PATH entries from shell profiles..."
for profile_name in $profiles; do
local profile_path="$HOME/.$profile_name"
if [ -f "$profile_path" ]; then
log_info "Cleaning PATH entries from: $profile_name"
if [ "$DRY_RUN" = "0" ]; then
# Create backup
cp "$profile_path" "$profile_path.$BACKUP_SUFFIX"
# Remove lines containing the install directory
grep -v "$install_dir" "$profile_path.$BACKUP_SUFFIX" > "$profile_path"
if [ $? -eq 0 ]; then
log_success "Cleaned PATH entries from: $profile_name"
else
log_error "Failed to clean PATH entries from: $profile_name"
# Restore backup
mv "$profile_path.$BACKUP_SUFFIX" "$profile_path"
fi
else
log_info "[DRY RUN] Would remove PATH entries from: $profile_path"
fi
fi
done
}
# Unregister plugins from nushell (if nu is still available)
unregister_plugins() {
local install_dir="$1"
# Only try to unregister if nu is still available somewhere
local nu_binary=""
if command -v nu >/dev/null 2>&1; then
nu_binary=$(command -v nu)
elif [ -f "$install_dir/nu" ]; then
nu_binary="$install_dir/nu"
else
log_info "Nushell not available for plugin unregistration"
return 0
fi
log_info "Attempting to unregister plugins..."
if [ "$DRY_RUN" = "0" ]; then
# Try to get list of registered plugins
local registered_plugins=""
registered_plugins=$("$nu_binary" -c "plugin list | get name" 2>/dev/null) || {
log_warn "Could not retrieve plugin list"
return 0
}
if [ -n "$registered_plugins" ]; then
log_info "Unregistering plugins from nushell..."
for plugin in $registered_plugins; do
"$nu_binary" -c "plugin rm $plugin" 2>/dev/null || {
log_warn "Could not unregister plugin: $plugin"
}
done
log_success "Plugin unregistration completed"
else
log_info "No registered plugins found"
fi
else
log_info "[DRY RUN] Would attempt to unregister plugins"
fi
}
# Main uninstallation process
main() {
log_info "🗑️ Nushell Full Distribution Uninstaller"
log_info "========================================"
if [ "$DRY_RUN" = "1" ]; then
log_warn "DRY RUN MODE - No files will be modified"
fi
# Detect current installation
log_info ""
log_info "🔍 Detecting current installation..."
local binaries=""
local config_files=""
local shell_profiles=""
# Check user installation
local user_binaries=$(detect_nushell_installation "$INSTALL_DIR_USER")
local user_config=$(detect_config_installation "$CONFIG_DIR")
local user_profiles=$(detect_shell_profile_entries "$INSTALL_DIR_USER")
# Check system installation
local system_binaries=$(detect_nushell_installation "$INSTALL_DIR_SYSTEM")
local system_profiles=$(detect_shell_profile_entries "$INSTALL_DIR_SYSTEM")
# Determine what we're removing based on target
if [ "$SYSTEM_INSTALL" = "1" ]; then
binaries="$system_binaries"
shell_profiles="$system_profiles"
# Config is always user-level
config_files="$user_config"
else
binaries="$user_binaries"
shell_profiles="$user_profiles"
config_files="$user_config"
fi
# Show detection results
log_info "Installation Status:"
if [ -n "$user_binaries" ]; then
log_info " 📁 User binaries ($INSTALL_DIR_USER): $user_binaries"
else
log_info " 📁 User binaries ($INSTALL_DIR_USER): none found"
fi
if [ -n "$system_binaries" ]; then
log_info " 📁 System binaries ($INSTALL_DIR_SYSTEM): $system_binaries"
else
log_info " 📁 System binaries ($INSTALL_DIR_SYSTEM): none found"
fi
if [ -n "$config_files" ]; then
log_info " ⚙️ Configuration ($CONFIG_DIR): $config_files"
else
log_info " ⚙️ Configuration ($CONFIG_DIR): none found"
fi
if [ -n "$shell_profiles" ]; then
log_info " 🐚 Shell profiles: $shell_profiles"
else
log_info " 🐚 Shell profiles: no PATH entries found"
fi
# Check if anything was found
if [ -z "$binaries" ] && [ -z "$config_files" ] && [ -z "$shell_profiles" ]; then
log_warn "No Nushell installation detected"
if [ "$INTERACTIVE" = "1" ]; then
printf "Continue anyway? [y/N]: "
read -r response
case "$response" in
[yY]|[yY][eE][sS])
;;
*)
log_info "Uninstallation cancelled"
exit 0
;;
esac
else
log_info "Nothing to remove"
exit 0
fi
fi
# Confirmation prompt
if [ "$INTERACTIVE" = "1" ]; then
log_info ""
log_warn "This will remove the detected Nushell installation components."
if [ "$KEEP_CONFIG" = "1" ]; then
log_info "Configuration files will be kept as requested."
elif [ "$BACKUP_CONFIG" = "1" ]; then
log_info "Configuration files will be backed up before removal."
fi
printf "Proceed with uninstallation? [y/N]: "
read -r response
case "$response" in
[yY]|[yY][eE][sS])
;;
*)
log_info "Uninstallation cancelled"
exit 0
;;
esac
fi
# Perform uninstallation
log_info ""
log_info "🗑️ Starting uninstallation..."
# Unregister plugins before removing binaries
if [ -n "$binaries" ]; then
unregister_plugins "$INSTALL_DIR"
fi
# Remove binaries
if [ -n "$binaries" ]; then
remove_binaries "$INSTALL_DIR" "$binaries"
fi
# Remove configuration
if [ -n "$config_files" ]; then
remove_configuration "$CONFIG_DIR" "$config_files"
fi
# Clean shell profiles
if [ -n "$shell_profiles" ]; then
remove_shell_profile_entries "$INSTALL_DIR" "$shell_profiles"
fi
# Final summary
log_info ""
log_success "🎉 Uninstallation completed!"
log_info "Summary:"
log_info " 🗑️ Removed from: $INSTALL_DIR"
if [ -n "$config_files" ] && [ "$KEEP_CONFIG" = "0" ]; then
log_info " 🗑️ Configuration removed: $CONFIG_DIR"
elif [ "$KEEP_CONFIG" = "1" ]; then
log_info " 💾 Configuration kept: $CONFIG_DIR"
fi
if [ "$BACKUP_CONFIG" = "1" ]; then
log_info " 💾 Configuration backed up with suffix: .$BACKUP_SUFFIX"
fi
log_info ""
log_info "🔄 To complete removal:"
log_info "1. Restart your terminal or run: source ~/.bashrc (or equivalent)"
log_info "2. Verify removal: command -v nu (should return nothing)"
if [ "$KEEP_CONFIG" = "1" ] || [ "$BACKUP_CONFIG" = "1" ]; then
log_info ""
log_info "📝 Note: Configuration files were preserved as requested"
fi
}
# Run main function
main "$@"