Jesús Pérez 41455c5b3e
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
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

563 lines
16 KiB
Bash
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.

#!/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 "$@"