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
|
# Summary
feat: bootstrap installer with robust archive extraction, version mismatch handling, and improved PATH messaging
## Detailed Description
This commit 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.
## Major Changes (Session 2025-10-19)
### 0. Bootstrap Installer & Distribution Improvements (NEW)
#### 0a. Install Script Architecture - DRY Design
**Issue**: Code duplication across installation paths
- `./install.sh`, `./scripts/templates/install.sh`, `installers/bootstrap/install.sh` were separate copies
- Changes required updating multiple files
- Risk of divergence and inconsistency
**Solution**: Implemented symlink-based DRY architecture
- Single source of truth: `installers/bootstrap/install.sh` (1,247 lines)
- Symlinks created:
- `./install.sh` → `installers/bootstrap/install.sh`
- `./scripts/templates/install.sh` → `installers/bootstrap/install.sh`
- All changes automatically propagate through symlinks
- No code duplication
**Impact**:
- ✅ Single maintenance point for all install scripts
- ✅ Consistent behavior across all paths
- ✅ Reduced maintenance burden
- ✅ No divergence risk
#### 0b. Archive Extraction - Version-Agnostic Binary Detection
**Issue**: "No Nushell binary found in extracted archive"
- Script failed when extracting from `nushell-full-0.108.0-darwin-arm64.tar.gz`
- Error occurred even though binaries were present at `nushell-full-0.108.0/bin/nu`
**Root Cause**: `find` command returning parent directory itself in results
```bash
# ❌ OLD - find returns parent directory first
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" | head -1)
# Returns: /tmp/nushell-install-22919/nushell-extract (parent!)
# Should return: /tmp/nushell-install-22919/nushell-extract/nushell-full-0.108.0 (subdirectory)
```
**Solution**: Added `-not -path` to exclude starting directory
```bash
# ✅ NEW - exclude parent directory
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" -not -path "$extract_dir" | head -1)
# Now correctly returns the subdirectory
```
**Additional Improvements**:
- 4-level fallback detection strategy:
1. Check `$extracted/bin/nu` (subdirectory structure)
2. Check `$extracted/nu` (flat structure)
3. Fallback search for any `nushell-*` subdirectory with `bin/nu`
4. Last resort recursive search for any executable named `nu`
- Validates binaries exist before using them
- Clear error reporting with all search locations
**Version-Agnostic**:
- Uses `nushell-*` pattern (not hardcoded version numbers)
- Works with any Nushell version: 0.107, 0.108, 0.109, etc.
- Supports both `.tar.gz` and `.zip` archive formats
**Impact**:
- ✅ Archive extraction works reliably
- ✅ Works with any Nushell version
- ✅ Clear error messages guide users
- ✅ Multiple archive structure support
#### 0c. Plugin Registration - Version Mismatch Handling
**Issue**: Plugin registration failed with version incompatibility errors
```
Error: nu::shell::plugin_failed_to_load
× Plugin `polars` is compiled for nushell version 0.107.1, which is not compatible with version 0.108.0
```
**Solution**: Implemented intelligent error classification
- Captures both stdout and stderr from plugin add commands
- Detects version incompatibility: "is not compatible with version" or "is compiled for nushell version"
- Classifies errors into three categories:
1. **Success**: Plugin registered successfully ✅
2. **Incompatible**: Version mismatch (skipped gracefully) ⚠️
3. **Failed**: Other registration failures ❌
- Reports summary with counts of each category
- Installation continues successfully even with version mismatches
**New Error Reporting**:
```
✅ Registered nu_plugin_auth
⚠️ Skipping nu_plugin_polars: Version mismatch (built for different Nushell version)
✅ Successfully registered 13 plugins
⚠️ Skipped 1 incompatible plugins (version mismatch):
- nu_plugin_polars
```
**Impact**:
- ✅ No installation failures due to version mismatches
- ✅ Users informed of incompatible plugins
- ✅ Clear distinction between error types
- ✅ Installation completes successfully
#### 0d. Shell Configuration PATH Update - Clear Messaging
**Issue**: Confusing PATH update messages
- User sees: "PATH already updated" for all files
- Then sees: "No shell configuration files were updated" warning
- Then sees: "Please manually add to your PATH" error
- **Problem**: Contradictory messages when PATH is already configured everywhere
**Root Cause**: Script conflated two states
- State 1: "Was PATH found in files?" (skips updating if found)
- State 2: "Did we add PATH to any file?" (used for messaging)
- Both states ignored means no update was made, but PATH might already exist
**Solution**: Track two separate states
```bash
local updated=false # Was PATH ADDED to any file?
local path_found=false # Was PATH FOUND in any file?
# In loop:
if grep -q "$install_dir" "$config_file"; then
path_found=true # Found it! Mark as true
continue
fi
# After loop:
if [ "$updated" = "true" ]; then
log_success "Shell configuration updated"
elif [ "$path_found" = "true" ]; then
log_success "PATH is already configured in your shell configuration files"
else
log_warn "Could not find or update shell configuration"
fi
```
**New Clear Messages**:
- ✅ "PATH is already configured in your shell configuration files" (when found everywhere)
- ✅ "Shell configuration updated" (when just added)
- ⚠️ "Could not find or update shell configuration" (when file missing)
**Impact**:
- ✅ Non-contradictory messages
- ✅ Users understand what happened
- ✅ No false warnings when PATH already configured
- ✅ Clear guidance when manual action needed
#### 0e. Installation Features
**`--source-path` Option** (Local Installation):
- Install from local archive: `./install.sh --source-path archive.tar.gz`
- Install from local directory: `./install.sh --source-path /path/to/binaries`
- Default behavior: `./install.sh --source-path` uses `./bin_archives`
- Works with custom `--install-dir` paths
- No download needed, offline installation support
**`--uninstall` with Configuration Management**:
- Prompts user: "Remove ~/.config/nushell? [y/N]"
- Removes all installed binaries and plugins
- Preserves user choice for configuration
- Clean uninstall before fresh reinstall
#### 0f. Documentation Updates
**CLAUDE.md**:
- Added "Install Script Architecture (DRY Design)" section
- Documents source of truth and symlink structure
- Explains `--source-path` feature
- Shows version-agnostic archive detection
- Lists DRY architecture benefits
**README.md**:
- Added "Install Script Architecture (DRY Design)" subsection
- Shows symlink structure with arrows
- Provides `--source-path` usage examples
- Explains version-agnostic detection
- "How DRY Works" 3-step explanation
#### 0g. Files Modified
Core Changes:
- `installers/bootstrap/install.sh` (+3 lines for PATH messaging fix)
- Archive extraction fix with `-not -path`
- Plugin registration error classification
- Clear PATH update messaging
- Total: 1,247 lines (unified)
Auto-Updated via Symlinks:
- `./install.sh` - Auto-updated (1,247 lines)
- `./scripts/templates/install.sh` - Auto-updated (1,247 lines)
Documentation:
- `CLAUDE.md` - Added install architecture section
- `README.md` - Added install architecture subsection
- `CHANGELOG.md` - Added comprehensive entry (+100 lines)
#### 0h. Testing & Verification
All scenarios tested and verified:
- [x] Archive extraction works with version-agnostic detection
- [x] Installation to `~/.local` successful (16 binaries)
- [x] Installation to `~/.local/bin` successful (21 plugins loaded)
- [x] Plugin registration handles version mismatches gracefully
- [x] PATH messaging is clear and non-contradictory
- [x] Clean uninstall followed by fresh reinstall works perfectly
#### 0i. Impact
User-Facing Benefits:
- ✅ Users can install from any version of nushell-full archive
- ✅ Version mismatch plugins skipped without breaking installation
- ✅ Clear, honest error messages
- ✅ Non-confusing PATH update messages
- ✅ Offline installation support via `--source-path`
- ✅ Clean uninstall/reinstall workflow
Developer Benefits:
- ✅ Single source of truth eliminates code duplication
- ✅ Changes propagate automatically through symlinks
- ✅ Reduced maintenance burden
- ✅ Consistent behavior across all paths
- ✅ Production-ready installation process
---
### 1. Help System Integration (New Feature)
**Issue**: Version-update module recipes were not discoverable
- Not shown in `just help modules`
- Not referenced in `just help`
- Not included in help navigation system
- Users had to manually run `just --list` to find update commands
**Solution**:
- Added version-update module to all help outputs
- Updated `justfiles/help.just` to document all 30+ version-update recipes
- Created new `just commands` recipe as discoverable alias for `just --list`
- Integrated version-update into help-all workflow
**Impact**:
- Version-update commands now fully discoverable via help system
- Users can find update commands with: `just help modules`, `just help`, `just commands`
- Improved overall help system navigation
**Files Modified**:
- `justfiles/help.just` (+23 lines)
- Added version-update module to help sections
- Added to modules list
- Added to help-all workflow
- New `commands` recipe showing all recipes by group
### 2. Build Process Fixes (Phase 3: Bin Archives)
#### 2a. Plugin Archive Collection Bug
**Issue**: "No plugins found to package" warning in Phase 3
- Collected 26 plugin binaries but reported 0
- Archive creation skipped because count was wrong
**Root Cause**: `each` command returns null, so `| length` returned 0
```nushell
# ❌ OLD - each returns null
let plugin_count = (ls nu_plugin_*/target/release/nu_plugin_* | each {|p|
cp $p.name $"($temp_dir)/"
} | length) # Returns 0!
```
**Solution**: Separated counting from copying with proper filtering
```nushell
# ✅ NEW - count before operations
let plugins_to_copy = (ls nu_plugin_*/target/release/nu_plugin_* | where type == "file")
let plugin_count = ($plugins_to_copy | length)
```
**Impact**:
- Now correctly collects and reports 26 plugins
- Filters out .d dependency files automatically
- Warning eliminated
#### 2b. Tar Archive Path Handling
**Issue**: Tar command failing silently with relative paths in subshell
- `cd $temp_dir` changes context unpredictably
- Relative path `../$archive_name` fails in subshell
- Archive file not created despite exit code 0
**Root Cause**: Shell context and relative path issues in Nushell `do` block
**Solution**: Used `tar -C` with absolute paths instead of `cd`
```nushell
# ❌ OLD - unreliable context switching
do {
cd $temp_dir
tar -czf ../$archive_name .
}
# ✅ NEW - absolute paths, no context switching
tar -C $temp_dir -czf $archive_path .
```
**Additional Improvements**:
- Absolute path construction using `pwd | path join`
- Better error diagnostics with exit code and stderr output
- File verification after creation
**Impact**:
- Tar archives now created successfully
- Robust path handling across platforms
- Clear error messages for debugging
#### 2c. File Size Calculation Type Error
**Issue**: Runtime error when calculating archive size
```
Error: The '/' operator does not work on values of type 'list<filesize>'
```
**Root Cause**: `ls` returns list of records, so `.size` was a list
```nushell
# ❌ OLD - returns list<filesize>
(ls $archive_path).size / 1024 / 1024
# ✅ NEW - returns filesize
(ls $archive_path | get 0.size) / 1024 / 1024
```
**Impact**:
- Proper file size calculation in MB
- No more type errors
**Files Modified**:
- `scripts/create_full_distribution.nu` (+58 lines, refactored plugin collection)
- Fixed plugin counting logic
- Improved path handling with absolute paths
- Enhanced error diagnostics
### 3. Plugin Rebuild Optimization
**Issue**: All plugins marked for rebuild even when dependencies unchanged
- Step 4 (`update_all_plugins.nu`) touched all Cargo.toml files at 01:00:32
- Step 5 saw all files as "newer" than binaries
- Marked ALL plugins for rebuild, though cargo only rebuilt changed ones
**Root Cause**: Script always saved files, even when no changes made
```nushell
# ❌ OLD - always saves, touching file timestamp
$updated_content | to toml | save -f $cargo_toml
```
**Solution**: Only save if content actually changed
```nushell
# ✅ NEW - compare before writing
let original_toml = $content | to toml
let new_toml = $updated_content | to toml
if $original_toml != $new_toml {
$updated_content | to toml | save -f $cargo_toml
}
```
**Impact**:
- Unchanged files preserve original timestamps
- Only plugins with actual dependency changes are rebuilt
- Efficient rebuild process with accurate file modification detection
**Files Modified**:
- `scripts/update_all_plugins.nu` (+12 lines, added content comparison)
- Only touches files with real changes
- Preserves timestamps for efficiency
- Clearer logic and comments
### 4. Documentation
**Files Modified**:
- `CHANGELOG.md` (+56 lines)
- Added comprehensive 2025-10-19 entry
- Documented all fixes with root causes
- Listed files modified and impact summary
## Technical Details
### Nushell Patterns Used
1. **Proper List Handling**:
- `ls` returns list of records, access with `| get 0.size`
- Filter with `where type == "file"` to exclude metadata
2. **Absolute Path Construction**:
- `pwd | append "path" | path join` for cross-platform paths
- Safer than string concatenation with `/`
3. **Content Comparison**:
- Compare TOML string representation before saving
- Preserves file timestamps for efficiency
4. **Error Diagnostics**:
- Capture `stderr` from commands
- Report exit codes and error messages separately
## Testing
- [x] Help system shows version-update module
- [x] `just commands` displays all recipes by group
- [x] Phase 3 bin archive creation works
- [x] Plugin collection reports correct count (26)
- [x] Tar archives created successfully
- [x] File size calculated correctly
- [x] Plugin rebuild only touches changed files
- [x] CHANGELOG updated with all changes
## Files Changed
```
38 files changed, 2721 insertions(+), 2548 deletions(-)
Core Changes:
- justfiles/help.just (+23) Help system integration
- scripts/create_full_distribution.nu (+58) Build process fixes
- scripts/update_all_plugins.nu (+12) Rebuild optimization
- CHANGELOG.md (+56) Documentation
Dependency Updates:
- All plugin Cargo.toml and Cargo.lock files (version consistency)
```
## Breaking Changes
None. These are bug fixes and optimizations that maintain backward compatibility.
## Migration Notes
No migration needed. Improvements are transparent to users.
## Related Issues
- Help system discoverability
- Build process Phase 3 failures
- Unnecessary plugin rebuilds
- Build process reliability
## Checklist
- [x] Changes follow Rust/Nushell idioms
- [x] Code is well-commented
- [x] Error handling is comprehensive
- [x] Documentation is updated
- [x] All changes tested
- [x] No breaking changes introduced
2025-10-19 02:39:31 +01:00
|
|
|
|
--system Remove from system location (/usr/local/bin)
|
|
|
|
|
|
⚠️ Requires sudo: sudo $0 --system
|
|
|
|
|
|
--user Remove from user location (~/.local/bin) [default]
|
|
|
|
|
|
No sudo required - recommended
|
|
|
|
|
|
--uninstall-dir PATH Remove from custom directory (PATH must be writable)
|
|
|
|
|
|
Bypasses interactive prompts when supplied explicitly
|
|
|
|
|
|
Example: --uninstall-dir ~/.local/bin
|
2025-09-24 18:52:07 +01:00
|
|
|
|
--dry-run Show what would be removed without actually removing
|
|
|
|
|
|
--debug Enable debug output
|
|
|
|
|
|
|
|
|
|
|
|
EXAMPLES:
|
# Summary
feat: bootstrap installer with robust archive extraction, version mismatch handling, and improved PATH messaging
## Detailed Description
This commit 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.
## Major Changes (Session 2025-10-19)
### 0. Bootstrap Installer & Distribution Improvements (NEW)
#### 0a. Install Script Architecture - DRY Design
**Issue**: Code duplication across installation paths
- `./install.sh`, `./scripts/templates/install.sh`, `installers/bootstrap/install.sh` were separate copies
- Changes required updating multiple files
- Risk of divergence and inconsistency
**Solution**: Implemented symlink-based DRY architecture
- Single source of truth: `installers/bootstrap/install.sh` (1,247 lines)
- Symlinks created:
- `./install.sh` → `installers/bootstrap/install.sh`
- `./scripts/templates/install.sh` → `installers/bootstrap/install.sh`
- All changes automatically propagate through symlinks
- No code duplication
**Impact**:
- ✅ Single maintenance point for all install scripts
- ✅ Consistent behavior across all paths
- ✅ Reduced maintenance burden
- ✅ No divergence risk
#### 0b. Archive Extraction - Version-Agnostic Binary Detection
**Issue**: "No Nushell binary found in extracted archive"
- Script failed when extracting from `nushell-full-0.108.0-darwin-arm64.tar.gz`
- Error occurred even though binaries were present at `nushell-full-0.108.0/bin/nu`
**Root Cause**: `find` command returning parent directory itself in results
```bash
# ❌ OLD - find returns parent directory first
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" | head -1)
# Returns: /tmp/nushell-install-22919/nushell-extract (parent!)
# Should return: /tmp/nushell-install-22919/nushell-extract/nushell-full-0.108.0 (subdirectory)
```
**Solution**: Added `-not -path` to exclude starting directory
```bash
# ✅ NEW - exclude parent directory
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" -not -path "$extract_dir" | head -1)
# Now correctly returns the subdirectory
```
**Additional Improvements**:
- 4-level fallback detection strategy:
1. Check `$extracted/bin/nu` (subdirectory structure)
2. Check `$extracted/nu` (flat structure)
3. Fallback search for any `nushell-*` subdirectory with `bin/nu`
4. Last resort recursive search for any executable named `nu`
- Validates binaries exist before using them
- Clear error reporting with all search locations
**Version-Agnostic**:
- Uses `nushell-*` pattern (not hardcoded version numbers)
- Works with any Nushell version: 0.107, 0.108, 0.109, etc.
- Supports both `.tar.gz` and `.zip` archive formats
**Impact**:
- ✅ Archive extraction works reliably
- ✅ Works with any Nushell version
- ✅ Clear error messages guide users
- ✅ Multiple archive structure support
#### 0c. Plugin Registration - Version Mismatch Handling
**Issue**: Plugin registration failed with version incompatibility errors
```
Error: nu::shell::plugin_failed_to_load
× Plugin `polars` is compiled for nushell version 0.107.1, which is not compatible with version 0.108.0
```
**Solution**: Implemented intelligent error classification
- Captures both stdout and stderr from plugin add commands
- Detects version incompatibility: "is not compatible with version" or "is compiled for nushell version"
- Classifies errors into three categories:
1. **Success**: Plugin registered successfully ✅
2. **Incompatible**: Version mismatch (skipped gracefully) ⚠️
3. **Failed**: Other registration failures ❌
- Reports summary with counts of each category
- Installation continues successfully even with version mismatches
**New Error Reporting**:
```
✅ Registered nu_plugin_auth
⚠️ Skipping nu_plugin_polars: Version mismatch (built for different Nushell version)
✅ Successfully registered 13 plugins
⚠️ Skipped 1 incompatible plugins (version mismatch):
- nu_plugin_polars
```
**Impact**:
- ✅ No installation failures due to version mismatches
- ✅ Users informed of incompatible plugins
- ✅ Clear distinction between error types
- ✅ Installation completes successfully
#### 0d. Shell Configuration PATH Update - Clear Messaging
**Issue**: Confusing PATH update messages
- User sees: "PATH already updated" for all files
- Then sees: "No shell configuration files were updated" warning
- Then sees: "Please manually add to your PATH" error
- **Problem**: Contradictory messages when PATH is already configured everywhere
**Root Cause**: Script conflated two states
- State 1: "Was PATH found in files?" (skips updating if found)
- State 2: "Did we add PATH to any file?" (used for messaging)
- Both states ignored means no update was made, but PATH might already exist
**Solution**: Track two separate states
```bash
local updated=false # Was PATH ADDED to any file?
local path_found=false # Was PATH FOUND in any file?
# In loop:
if grep -q "$install_dir" "$config_file"; then
path_found=true # Found it! Mark as true
continue
fi
# After loop:
if [ "$updated" = "true" ]; then
log_success "Shell configuration updated"
elif [ "$path_found" = "true" ]; then
log_success "PATH is already configured in your shell configuration files"
else
log_warn "Could not find or update shell configuration"
fi
```
**New Clear Messages**:
- ✅ "PATH is already configured in your shell configuration files" (when found everywhere)
- ✅ "Shell configuration updated" (when just added)
- ⚠️ "Could not find or update shell configuration" (when file missing)
**Impact**:
- ✅ Non-contradictory messages
- ✅ Users understand what happened
- ✅ No false warnings when PATH already configured
- ✅ Clear guidance when manual action needed
#### 0e. Installation Features
**`--source-path` Option** (Local Installation):
- Install from local archive: `./install.sh --source-path archive.tar.gz`
- Install from local directory: `./install.sh --source-path /path/to/binaries`
- Default behavior: `./install.sh --source-path` uses `./bin_archives`
- Works with custom `--install-dir` paths
- No download needed, offline installation support
**`--uninstall` with Configuration Management**:
- Prompts user: "Remove ~/.config/nushell? [y/N]"
- Removes all installed binaries and plugins
- Preserves user choice for configuration
- Clean uninstall before fresh reinstall
#### 0f. Documentation Updates
**CLAUDE.md**:
- Added "Install Script Architecture (DRY Design)" section
- Documents source of truth and symlink structure
- Explains `--source-path` feature
- Shows version-agnostic archive detection
- Lists DRY architecture benefits
**README.md**:
- Added "Install Script Architecture (DRY Design)" subsection
- Shows symlink structure with arrows
- Provides `--source-path` usage examples
- Explains version-agnostic detection
- "How DRY Works" 3-step explanation
#### 0g. Files Modified
Core Changes:
- `installers/bootstrap/install.sh` (+3 lines for PATH messaging fix)
- Archive extraction fix with `-not -path`
- Plugin registration error classification
- Clear PATH update messaging
- Total: 1,247 lines (unified)
Auto-Updated via Symlinks:
- `./install.sh` - Auto-updated (1,247 lines)
- `./scripts/templates/install.sh` - Auto-updated (1,247 lines)
Documentation:
- `CLAUDE.md` - Added install architecture section
- `README.md` - Added install architecture subsection
- `CHANGELOG.md` - Added comprehensive entry (+100 lines)
#### 0h. Testing & Verification
All scenarios tested and verified:
- [x] Archive extraction works with version-agnostic detection
- [x] Installation to `~/.local` successful (16 binaries)
- [x] Installation to `~/.local/bin` successful (21 plugins loaded)
- [x] Plugin registration handles version mismatches gracefully
- [x] PATH messaging is clear and non-contradictory
- [x] Clean uninstall followed by fresh reinstall works perfectly
#### 0i. Impact
User-Facing Benefits:
- ✅ Users can install from any version of nushell-full archive
- ✅ Version mismatch plugins skipped without breaking installation
- ✅ Clear, honest error messages
- ✅ Non-confusing PATH update messages
- ✅ Offline installation support via `--source-path`
- ✅ Clean uninstall/reinstall workflow
Developer Benefits:
- ✅ Single source of truth eliminates code duplication
- ✅ Changes propagate automatically through symlinks
- ✅ Reduced maintenance burden
- ✅ Consistent behavior across all paths
- ✅ Production-ready installation process
---
### 1. Help System Integration (New Feature)
**Issue**: Version-update module recipes were not discoverable
- Not shown in `just help modules`
- Not referenced in `just help`
- Not included in help navigation system
- Users had to manually run `just --list` to find update commands
**Solution**:
- Added version-update module to all help outputs
- Updated `justfiles/help.just` to document all 30+ version-update recipes
- Created new `just commands` recipe as discoverable alias for `just --list`
- Integrated version-update into help-all workflow
**Impact**:
- Version-update commands now fully discoverable via help system
- Users can find update commands with: `just help modules`, `just help`, `just commands`
- Improved overall help system navigation
**Files Modified**:
- `justfiles/help.just` (+23 lines)
- Added version-update module to help sections
- Added to modules list
- Added to help-all workflow
- New `commands` recipe showing all recipes by group
### 2. Build Process Fixes (Phase 3: Bin Archives)
#### 2a. Plugin Archive Collection Bug
**Issue**: "No plugins found to package" warning in Phase 3
- Collected 26 plugin binaries but reported 0
- Archive creation skipped because count was wrong
**Root Cause**: `each` command returns null, so `| length` returned 0
```nushell
# ❌ OLD - each returns null
let plugin_count = (ls nu_plugin_*/target/release/nu_plugin_* | each {|p|
cp $p.name $"($temp_dir)/"
} | length) # Returns 0!
```
**Solution**: Separated counting from copying with proper filtering
```nushell
# ✅ NEW - count before operations
let plugins_to_copy = (ls nu_plugin_*/target/release/nu_plugin_* | where type == "file")
let plugin_count = ($plugins_to_copy | length)
```
**Impact**:
- Now correctly collects and reports 26 plugins
- Filters out .d dependency files automatically
- Warning eliminated
#### 2b. Tar Archive Path Handling
**Issue**: Tar command failing silently with relative paths in subshell
- `cd $temp_dir` changes context unpredictably
- Relative path `../$archive_name` fails in subshell
- Archive file not created despite exit code 0
**Root Cause**: Shell context and relative path issues in Nushell `do` block
**Solution**: Used `tar -C` with absolute paths instead of `cd`
```nushell
# ❌ OLD - unreliable context switching
do {
cd $temp_dir
tar -czf ../$archive_name .
}
# ✅ NEW - absolute paths, no context switching
tar -C $temp_dir -czf $archive_path .
```
**Additional Improvements**:
- Absolute path construction using `pwd | path join`
- Better error diagnostics with exit code and stderr output
- File verification after creation
**Impact**:
- Tar archives now created successfully
- Robust path handling across platforms
- Clear error messages for debugging
#### 2c. File Size Calculation Type Error
**Issue**: Runtime error when calculating archive size
```
Error: The '/' operator does not work on values of type 'list<filesize>'
```
**Root Cause**: `ls` returns list of records, so `.size` was a list
```nushell
# ❌ OLD - returns list<filesize>
(ls $archive_path).size / 1024 / 1024
# ✅ NEW - returns filesize
(ls $archive_path | get 0.size) / 1024 / 1024
```
**Impact**:
- Proper file size calculation in MB
- No more type errors
**Files Modified**:
- `scripts/create_full_distribution.nu` (+58 lines, refactored plugin collection)
- Fixed plugin counting logic
- Improved path handling with absolute paths
- Enhanced error diagnostics
### 3. Plugin Rebuild Optimization
**Issue**: All plugins marked for rebuild even when dependencies unchanged
- Step 4 (`update_all_plugins.nu`) touched all Cargo.toml files at 01:00:32
- Step 5 saw all files as "newer" than binaries
- Marked ALL plugins for rebuild, though cargo only rebuilt changed ones
**Root Cause**: Script always saved files, even when no changes made
```nushell
# ❌ OLD - always saves, touching file timestamp
$updated_content | to toml | save -f $cargo_toml
```
**Solution**: Only save if content actually changed
```nushell
# ✅ NEW - compare before writing
let original_toml = $content | to toml
let new_toml = $updated_content | to toml
if $original_toml != $new_toml {
$updated_content | to toml | save -f $cargo_toml
}
```
**Impact**:
- Unchanged files preserve original timestamps
- Only plugins with actual dependency changes are rebuilt
- Efficient rebuild process with accurate file modification detection
**Files Modified**:
- `scripts/update_all_plugins.nu` (+12 lines, added content comparison)
- Only touches files with real changes
- Preserves timestamps for efficiency
- Clearer logic and comments
### 4. Documentation
**Files Modified**:
- `CHANGELOG.md` (+56 lines)
- Added comprehensive 2025-10-19 entry
- Documented all fixes with root causes
- Listed files modified and impact summary
## Technical Details
### Nushell Patterns Used
1. **Proper List Handling**:
- `ls` returns list of records, access with `| get 0.size`
- Filter with `where type == "file"` to exclude metadata
2. **Absolute Path Construction**:
- `pwd | append "path" | path join` for cross-platform paths
- Safer than string concatenation with `/`
3. **Content Comparison**:
- Compare TOML string representation before saving
- Preserves file timestamps for efficiency
4. **Error Diagnostics**:
- Capture `stderr` from commands
- Report exit codes and error messages separately
## Testing
- [x] Help system shows version-update module
- [x] `just commands` displays all recipes by group
- [x] Phase 3 bin archive creation works
- [x] Plugin collection reports correct count (26)
- [x] Tar archives created successfully
- [x] File size calculated correctly
- [x] Plugin rebuild only touches changed files
- [x] CHANGELOG updated with all changes
## Files Changed
```
38 files changed, 2721 insertions(+), 2548 deletions(-)
Core Changes:
- justfiles/help.just (+23) Help system integration
- scripts/create_full_distribution.nu (+58) Build process fixes
- scripts/update_all_plugins.nu (+12) Rebuild optimization
- CHANGELOG.md (+56) Documentation
Dependency Updates:
- All plugin Cargo.toml and Cargo.lock files (version consistency)
```
## Breaking Changes
None. These are bug fixes and optimizations that maintain backward compatibility.
## Migration Notes
No migration needed. Improvements are transparent to users.
## Related Issues
- Help system discoverability
- Build process Phase 3 failures
- Unnecessary plugin rebuilds
- Build process reliability
## Checklist
- [x] Changes follow Rust/Nushell idioms
- [x] Code is well-commented
- [x] Error handling is comprehensive
- [x] Documentation is updated
- [x] All changes tested
- [x] No breaking changes introduced
2025-10-19 02:39:31 +01:00
|
|
|
|
# Default removal (from user location, interactive)
|
|
|
|
|
|
$0
|
|
|
|
|
|
|
|
|
|
|
|
# Non-interactive removal (from user location)
|
|
|
|
|
|
$0 -y
|
|
|
|
|
|
|
|
|
|
|
|
# Remove with config backup
|
|
|
|
|
|
$0 --backup-config -y
|
|
|
|
|
|
|
|
|
|
|
|
# Remove from custom directory
|
|
|
|
|
|
$0 --uninstall-dir ~/.local/bin -y
|
|
|
|
|
|
|
|
|
|
|
|
# Remove system installation (requires sudo)
|
|
|
|
|
|
sudo $0 --system -y
|
|
|
|
|
|
|
|
|
|
|
|
# Preview what would be removed
|
|
|
|
|
|
$0 --dry-run
|
|
|
|
|
|
|
|
|
|
|
|
TROUBLESHOOTING:
|
|
|
|
|
|
• Permission denied to /usr/local/bin?
|
|
|
|
|
|
→ Default uses ~/.local/bin (no sudo needed)
|
|
|
|
|
|
→ Or: --uninstall-dir ~/.local/bin (explicit custom path)
|
|
|
|
|
|
|
|
|
|
|
|
• Unsure what to do?
|
|
|
|
|
|
→ Run with --dry-run first to see what would be removed
|
|
|
|
|
|
→ Default removal is safest: $0 -y
|
2025-09-24 18:52:07 +01:00
|
|
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Parse command line arguments
|
|
|
|
|
|
INTERACTIVE=1
|
|
|
|
|
|
KEEP_CONFIG=0
|
|
|
|
|
|
BACKUP_CONFIG=0
|
|
|
|
|
|
SYSTEM_INSTALL=0
|
|
|
|
|
|
DRY_RUN=0
|
# Summary
feat: bootstrap installer with robust archive extraction, version mismatch handling, and improved PATH messaging
## Detailed Description
This commit 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.
## Major Changes (Session 2025-10-19)
### 0. Bootstrap Installer & Distribution Improvements (NEW)
#### 0a. Install Script Architecture - DRY Design
**Issue**: Code duplication across installation paths
- `./install.sh`, `./scripts/templates/install.sh`, `installers/bootstrap/install.sh` were separate copies
- Changes required updating multiple files
- Risk of divergence and inconsistency
**Solution**: Implemented symlink-based DRY architecture
- Single source of truth: `installers/bootstrap/install.sh` (1,247 lines)
- Symlinks created:
- `./install.sh` → `installers/bootstrap/install.sh`
- `./scripts/templates/install.sh` → `installers/bootstrap/install.sh`
- All changes automatically propagate through symlinks
- No code duplication
**Impact**:
- ✅ Single maintenance point for all install scripts
- ✅ Consistent behavior across all paths
- ✅ Reduced maintenance burden
- ✅ No divergence risk
#### 0b. Archive Extraction - Version-Agnostic Binary Detection
**Issue**: "No Nushell binary found in extracted archive"
- Script failed when extracting from `nushell-full-0.108.0-darwin-arm64.tar.gz`
- Error occurred even though binaries were present at `nushell-full-0.108.0/bin/nu`
**Root Cause**: `find` command returning parent directory itself in results
```bash
# ❌ OLD - find returns parent directory first
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" | head -1)
# Returns: /tmp/nushell-install-22919/nushell-extract (parent!)
# Should return: /tmp/nushell-install-22919/nushell-extract/nushell-full-0.108.0 (subdirectory)
```
**Solution**: Added `-not -path` to exclude starting directory
```bash
# ✅ NEW - exclude parent directory
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" -not -path "$extract_dir" | head -1)
# Now correctly returns the subdirectory
```
**Additional Improvements**:
- 4-level fallback detection strategy:
1. Check `$extracted/bin/nu` (subdirectory structure)
2. Check `$extracted/nu` (flat structure)
3. Fallback search for any `nushell-*` subdirectory with `bin/nu`
4. Last resort recursive search for any executable named `nu`
- Validates binaries exist before using them
- Clear error reporting with all search locations
**Version-Agnostic**:
- Uses `nushell-*` pattern (not hardcoded version numbers)
- Works with any Nushell version: 0.107, 0.108, 0.109, etc.
- Supports both `.tar.gz` and `.zip` archive formats
**Impact**:
- ✅ Archive extraction works reliably
- ✅ Works with any Nushell version
- ✅ Clear error messages guide users
- ✅ Multiple archive structure support
#### 0c. Plugin Registration - Version Mismatch Handling
**Issue**: Plugin registration failed with version incompatibility errors
```
Error: nu::shell::plugin_failed_to_load
× Plugin `polars` is compiled for nushell version 0.107.1, which is not compatible with version 0.108.0
```
**Solution**: Implemented intelligent error classification
- Captures both stdout and stderr from plugin add commands
- Detects version incompatibility: "is not compatible with version" or "is compiled for nushell version"
- Classifies errors into three categories:
1. **Success**: Plugin registered successfully ✅
2. **Incompatible**: Version mismatch (skipped gracefully) ⚠️
3. **Failed**: Other registration failures ❌
- Reports summary with counts of each category
- Installation continues successfully even with version mismatches
**New Error Reporting**:
```
✅ Registered nu_plugin_auth
⚠️ Skipping nu_plugin_polars: Version mismatch (built for different Nushell version)
✅ Successfully registered 13 plugins
⚠️ Skipped 1 incompatible plugins (version mismatch):
- nu_plugin_polars
```
**Impact**:
- ✅ No installation failures due to version mismatches
- ✅ Users informed of incompatible plugins
- ✅ Clear distinction between error types
- ✅ Installation completes successfully
#### 0d. Shell Configuration PATH Update - Clear Messaging
**Issue**: Confusing PATH update messages
- User sees: "PATH already updated" for all files
- Then sees: "No shell configuration files were updated" warning
- Then sees: "Please manually add to your PATH" error
- **Problem**: Contradictory messages when PATH is already configured everywhere
**Root Cause**: Script conflated two states
- State 1: "Was PATH found in files?" (skips updating if found)
- State 2: "Did we add PATH to any file?" (used for messaging)
- Both states ignored means no update was made, but PATH might already exist
**Solution**: Track two separate states
```bash
local updated=false # Was PATH ADDED to any file?
local path_found=false # Was PATH FOUND in any file?
# In loop:
if grep -q "$install_dir" "$config_file"; then
path_found=true # Found it! Mark as true
continue
fi
# After loop:
if [ "$updated" = "true" ]; then
log_success "Shell configuration updated"
elif [ "$path_found" = "true" ]; then
log_success "PATH is already configured in your shell configuration files"
else
log_warn "Could not find or update shell configuration"
fi
```
**New Clear Messages**:
- ✅ "PATH is already configured in your shell configuration files" (when found everywhere)
- ✅ "Shell configuration updated" (when just added)
- ⚠️ "Could not find or update shell configuration" (when file missing)
**Impact**:
- ✅ Non-contradictory messages
- ✅ Users understand what happened
- ✅ No false warnings when PATH already configured
- ✅ Clear guidance when manual action needed
#### 0e. Installation Features
**`--source-path` Option** (Local Installation):
- Install from local archive: `./install.sh --source-path archive.tar.gz`
- Install from local directory: `./install.sh --source-path /path/to/binaries`
- Default behavior: `./install.sh --source-path` uses `./bin_archives`
- Works with custom `--install-dir` paths
- No download needed, offline installation support
**`--uninstall` with Configuration Management**:
- Prompts user: "Remove ~/.config/nushell? [y/N]"
- Removes all installed binaries and plugins
- Preserves user choice for configuration
- Clean uninstall before fresh reinstall
#### 0f. Documentation Updates
**CLAUDE.md**:
- Added "Install Script Architecture (DRY Design)" section
- Documents source of truth and symlink structure
- Explains `--source-path` feature
- Shows version-agnostic archive detection
- Lists DRY architecture benefits
**README.md**:
- Added "Install Script Architecture (DRY Design)" subsection
- Shows symlink structure with arrows
- Provides `--source-path` usage examples
- Explains version-agnostic detection
- "How DRY Works" 3-step explanation
#### 0g. Files Modified
Core Changes:
- `installers/bootstrap/install.sh` (+3 lines for PATH messaging fix)
- Archive extraction fix with `-not -path`
- Plugin registration error classification
- Clear PATH update messaging
- Total: 1,247 lines (unified)
Auto-Updated via Symlinks:
- `./install.sh` - Auto-updated (1,247 lines)
- `./scripts/templates/install.sh` - Auto-updated (1,247 lines)
Documentation:
- `CLAUDE.md` - Added install architecture section
- `README.md` - Added install architecture subsection
- `CHANGELOG.md` - Added comprehensive entry (+100 lines)
#### 0h. Testing & Verification
All scenarios tested and verified:
- [x] Archive extraction works with version-agnostic detection
- [x] Installation to `~/.local` successful (16 binaries)
- [x] Installation to `~/.local/bin` successful (21 plugins loaded)
- [x] Plugin registration handles version mismatches gracefully
- [x] PATH messaging is clear and non-contradictory
- [x] Clean uninstall followed by fresh reinstall works perfectly
#### 0i. Impact
User-Facing Benefits:
- ✅ Users can install from any version of nushell-full archive
- ✅ Version mismatch plugins skipped without breaking installation
- ✅ Clear, honest error messages
- ✅ Non-confusing PATH update messages
- ✅ Offline installation support via `--source-path`
- ✅ Clean uninstall/reinstall workflow
Developer Benefits:
- ✅ Single source of truth eliminates code duplication
- ✅ Changes propagate automatically through symlinks
- ✅ Reduced maintenance burden
- ✅ Consistent behavior across all paths
- ✅ Production-ready installation process
---
### 1. Help System Integration (New Feature)
**Issue**: Version-update module recipes were not discoverable
- Not shown in `just help modules`
- Not referenced in `just help`
- Not included in help navigation system
- Users had to manually run `just --list` to find update commands
**Solution**:
- Added version-update module to all help outputs
- Updated `justfiles/help.just` to document all 30+ version-update recipes
- Created new `just commands` recipe as discoverable alias for `just --list`
- Integrated version-update into help-all workflow
**Impact**:
- Version-update commands now fully discoverable via help system
- Users can find update commands with: `just help modules`, `just help`, `just commands`
- Improved overall help system navigation
**Files Modified**:
- `justfiles/help.just` (+23 lines)
- Added version-update module to help sections
- Added to modules list
- Added to help-all workflow
- New `commands` recipe showing all recipes by group
### 2. Build Process Fixes (Phase 3: Bin Archives)
#### 2a. Plugin Archive Collection Bug
**Issue**: "No plugins found to package" warning in Phase 3
- Collected 26 plugin binaries but reported 0
- Archive creation skipped because count was wrong
**Root Cause**: `each` command returns null, so `| length` returned 0
```nushell
# ❌ OLD - each returns null
let plugin_count = (ls nu_plugin_*/target/release/nu_plugin_* | each {|p|
cp $p.name $"($temp_dir)/"
} | length) # Returns 0!
```
**Solution**: Separated counting from copying with proper filtering
```nushell
# ✅ NEW - count before operations
let plugins_to_copy = (ls nu_plugin_*/target/release/nu_plugin_* | where type == "file")
let plugin_count = ($plugins_to_copy | length)
```
**Impact**:
- Now correctly collects and reports 26 plugins
- Filters out .d dependency files automatically
- Warning eliminated
#### 2b. Tar Archive Path Handling
**Issue**: Tar command failing silently with relative paths in subshell
- `cd $temp_dir` changes context unpredictably
- Relative path `../$archive_name` fails in subshell
- Archive file not created despite exit code 0
**Root Cause**: Shell context and relative path issues in Nushell `do` block
**Solution**: Used `tar -C` with absolute paths instead of `cd`
```nushell
# ❌ OLD - unreliable context switching
do {
cd $temp_dir
tar -czf ../$archive_name .
}
# ✅ NEW - absolute paths, no context switching
tar -C $temp_dir -czf $archive_path .
```
**Additional Improvements**:
- Absolute path construction using `pwd | path join`
- Better error diagnostics with exit code and stderr output
- File verification after creation
**Impact**:
- Tar archives now created successfully
- Robust path handling across platforms
- Clear error messages for debugging
#### 2c. File Size Calculation Type Error
**Issue**: Runtime error when calculating archive size
```
Error: The '/' operator does not work on values of type 'list<filesize>'
```
**Root Cause**: `ls` returns list of records, so `.size` was a list
```nushell
# ❌ OLD - returns list<filesize>
(ls $archive_path).size / 1024 / 1024
# ✅ NEW - returns filesize
(ls $archive_path | get 0.size) / 1024 / 1024
```
**Impact**:
- Proper file size calculation in MB
- No more type errors
**Files Modified**:
- `scripts/create_full_distribution.nu` (+58 lines, refactored plugin collection)
- Fixed plugin counting logic
- Improved path handling with absolute paths
- Enhanced error diagnostics
### 3. Plugin Rebuild Optimization
**Issue**: All plugins marked for rebuild even when dependencies unchanged
- Step 4 (`update_all_plugins.nu`) touched all Cargo.toml files at 01:00:32
- Step 5 saw all files as "newer" than binaries
- Marked ALL plugins for rebuild, though cargo only rebuilt changed ones
**Root Cause**: Script always saved files, even when no changes made
```nushell
# ❌ OLD - always saves, touching file timestamp
$updated_content | to toml | save -f $cargo_toml
```
**Solution**: Only save if content actually changed
```nushell
# ✅ NEW - compare before writing
let original_toml = $content | to toml
let new_toml = $updated_content | to toml
if $original_toml != $new_toml {
$updated_content | to toml | save -f $cargo_toml
}
```
**Impact**:
- Unchanged files preserve original timestamps
- Only plugins with actual dependency changes are rebuilt
- Efficient rebuild process with accurate file modification detection
**Files Modified**:
- `scripts/update_all_plugins.nu` (+12 lines, added content comparison)
- Only touches files with real changes
- Preserves timestamps for efficiency
- Clearer logic and comments
### 4. Documentation
**Files Modified**:
- `CHANGELOG.md` (+56 lines)
- Added comprehensive 2025-10-19 entry
- Documented all fixes with root causes
- Listed files modified and impact summary
## Technical Details
### Nushell Patterns Used
1. **Proper List Handling**:
- `ls` returns list of records, access with `| get 0.size`
- Filter with `where type == "file"` to exclude metadata
2. **Absolute Path Construction**:
- `pwd | append "path" | path join` for cross-platform paths
- Safer than string concatenation with `/`
3. **Content Comparison**:
- Compare TOML string representation before saving
- Preserves file timestamps for efficiency
4. **Error Diagnostics**:
- Capture `stderr` from commands
- Report exit codes and error messages separately
## Testing
- [x] Help system shows version-update module
- [x] `just commands` displays all recipes by group
- [x] Phase 3 bin archive creation works
- [x] Plugin collection reports correct count (26)
- [x] Tar archives created successfully
- [x] File size calculated correctly
- [x] Plugin rebuild only touches changed files
- [x] CHANGELOG updated with all changes
## Files Changed
```
38 files changed, 2721 insertions(+), 2548 deletions(-)
Core Changes:
- justfiles/help.just (+23) Help system integration
- scripts/create_full_distribution.nu (+58) Build process fixes
- scripts/update_all_plugins.nu (+12) Rebuild optimization
- CHANGELOG.md (+56) Documentation
Dependency Updates:
- All plugin Cargo.toml and Cargo.lock files (version consistency)
```
## Breaking Changes
None. These are bug fixes and optimizations that maintain backward compatibility.
## Migration Notes
No migration needed. Improvements are transparent to users.
## Related Issues
- Help system discoverability
- Build process Phase 3 failures
- Unnecessary plugin rebuilds
- Build process reliability
## Checklist
- [x] Changes follow Rust/Nushell idioms
- [x] Code is well-commented
- [x] Error handling is comprehensive
- [x] Documentation is updated
- [x] All changes tested
- [x] No breaking changes introduced
2025-10-19 02:39:31 +01:00
|
|
|
|
CUSTOM_UNINSTALL_DIR=""
|
2025-09-24 18:52:07 +01:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
;;
|
# Summary
feat: bootstrap installer with robust archive extraction, version mismatch handling, and improved PATH messaging
## Detailed Description
This commit 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.
## Major Changes (Session 2025-10-19)
### 0. Bootstrap Installer & Distribution Improvements (NEW)
#### 0a. Install Script Architecture - DRY Design
**Issue**: Code duplication across installation paths
- `./install.sh`, `./scripts/templates/install.sh`, `installers/bootstrap/install.sh` were separate copies
- Changes required updating multiple files
- Risk of divergence and inconsistency
**Solution**: Implemented symlink-based DRY architecture
- Single source of truth: `installers/bootstrap/install.sh` (1,247 lines)
- Symlinks created:
- `./install.sh` → `installers/bootstrap/install.sh`
- `./scripts/templates/install.sh` → `installers/bootstrap/install.sh`
- All changes automatically propagate through symlinks
- No code duplication
**Impact**:
- ✅ Single maintenance point for all install scripts
- ✅ Consistent behavior across all paths
- ✅ Reduced maintenance burden
- ✅ No divergence risk
#### 0b. Archive Extraction - Version-Agnostic Binary Detection
**Issue**: "No Nushell binary found in extracted archive"
- Script failed when extracting from `nushell-full-0.108.0-darwin-arm64.tar.gz`
- Error occurred even though binaries were present at `nushell-full-0.108.0/bin/nu`
**Root Cause**: `find` command returning parent directory itself in results
```bash
# ❌ OLD - find returns parent directory first
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" | head -1)
# Returns: /tmp/nushell-install-22919/nushell-extract (parent!)
# Should return: /tmp/nushell-install-22919/nushell-extract/nushell-full-0.108.0 (subdirectory)
```
**Solution**: Added `-not -path` to exclude starting directory
```bash
# ✅ NEW - exclude parent directory
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" -not -path "$extract_dir" | head -1)
# Now correctly returns the subdirectory
```
**Additional Improvements**:
- 4-level fallback detection strategy:
1. Check `$extracted/bin/nu` (subdirectory structure)
2. Check `$extracted/nu` (flat structure)
3. Fallback search for any `nushell-*` subdirectory with `bin/nu`
4. Last resort recursive search for any executable named `nu`
- Validates binaries exist before using them
- Clear error reporting with all search locations
**Version-Agnostic**:
- Uses `nushell-*` pattern (not hardcoded version numbers)
- Works with any Nushell version: 0.107, 0.108, 0.109, etc.
- Supports both `.tar.gz` and `.zip` archive formats
**Impact**:
- ✅ Archive extraction works reliably
- ✅ Works with any Nushell version
- ✅ Clear error messages guide users
- ✅ Multiple archive structure support
#### 0c. Plugin Registration - Version Mismatch Handling
**Issue**: Plugin registration failed with version incompatibility errors
```
Error: nu::shell::plugin_failed_to_load
× Plugin `polars` is compiled for nushell version 0.107.1, which is not compatible with version 0.108.0
```
**Solution**: Implemented intelligent error classification
- Captures both stdout and stderr from plugin add commands
- Detects version incompatibility: "is not compatible with version" or "is compiled for nushell version"
- Classifies errors into three categories:
1. **Success**: Plugin registered successfully ✅
2. **Incompatible**: Version mismatch (skipped gracefully) ⚠️
3. **Failed**: Other registration failures ❌
- Reports summary with counts of each category
- Installation continues successfully even with version mismatches
**New Error Reporting**:
```
✅ Registered nu_plugin_auth
⚠️ Skipping nu_plugin_polars: Version mismatch (built for different Nushell version)
✅ Successfully registered 13 plugins
⚠️ Skipped 1 incompatible plugins (version mismatch):
- nu_plugin_polars
```
**Impact**:
- ✅ No installation failures due to version mismatches
- ✅ Users informed of incompatible plugins
- ✅ Clear distinction between error types
- ✅ Installation completes successfully
#### 0d. Shell Configuration PATH Update - Clear Messaging
**Issue**: Confusing PATH update messages
- User sees: "PATH already updated" for all files
- Then sees: "No shell configuration files were updated" warning
- Then sees: "Please manually add to your PATH" error
- **Problem**: Contradictory messages when PATH is already configured everywhere
**Root Cause**: Script conflated two states
- State 1: "Was PATH found in files?" (skips updating if found)
- State 2: "Did we add PATH to any file?" (used for messaging)
- Both states ignored means no update was made, but PATH might already exist
**Solution**: Track two separate states
```bash
local updated=false # Was PATH ADDED to any file?
local path_found=false # Was PATH FOUND in any file?
# In loop:
if grep -q "$install_dir" "$config_file"; then
path_found=true # Found it! Mark as true
continue
fi
# After loop:
if [ "$updated" = "true" ]; then
log_success "Shell configuration updated"
elif [ "$path_found" = "true" ]; then
log_success "PATH is already configured in your shell configuration files"
else
log_warn "Could not find or update shell configuration"
fi
```
**New Clear Messages**:
- ✅ "PATH is already configured in your shell configuration files" (when found everywhere)
- ✅ "Shell configuration updated" (when just added)
- ⚠️ "Could not find or update shell configuration" (when file missing)
**Impact**:
- ✅ Non-contradictory messages
- ✅ Users understand what happened
- ✅ No false warnings when PATH already configured
- ✅ Clear guidance when manual action needed
#### 0e. Installation Features
**`--source-path` Option** (Local Installation):
- Install from local archive: `./install.sh --source-path archive.tar.gz`
- Install from local directory: `./install.sh --source-path /path/to/binaries`
- Default behavior: `./install.sh --source-path` uses `./bin_archives`
- Works with custom `--install-dir` paths
- No download needed, offline installation support
**`--uninstall` with Configuration Management**:
- Prompts user: "Remove ~/.config/nushell? [y/N]"
- Removes all installed binaries and plugins
- Preserves user choice for configuration
- Clean uninstall before fresh reinstall
#### 0f. Documentation Updates
**CLAUDE.md**:
- Added "Install Script Architecture (DRY Design)" section
- Documents source of truth and symlink structure
- Explains `--source-path` feature
- Shows version-agnostic archive detection
- Lists DRY architecture benefits
**README.md**:
- Added "Install Script Architecture (DRY Design)" subsection
- Shows symlink structure with arrows
- Provides `--source-path` usage examples
- Explains version-agnostic detection
- "How DRY Works" 3-step explanation
#### 0g. Files Modified
Core Changes:
- `installers/bootstrap/install.sh` (+3 lines for PATH messaging fix)
- Archive extraction fix with `-not -path`
- Plugin registration error classification
- Clear PATH update messaging
- Total: 1,247 lines (unified)
Auto-Updated via Symlinks:
- `./install.sh` - Auto-updated (1,247 lines)
- `./scripts/templates/install.sh` - Auto-updated (1,247 lines)
Documentation:
- `CLAUDE.md` - Added install architecture section
- `README.md` - Added install architecture subsection
- `CHANGELOG.md` - Added comprehensive entry (+100 lines)
#### 0h. Testing & Verification
All scenarios tested and verified:
- [x] Archive extraction works with version-agnostic detection
- [x] Installation to `~/.local` successful (16 binaries)
- [x] Installation to `~/.local/bin` successful (21 plugins loaded)
- [x] Plugin registration handles version mismatches gracefully
- [x] PATH messaging is clear and non-contradictory
- [x] Clean uninstall followed by fresh reinstall works perfectly
#### 0i. Impact
User-Facing Benefits:
- ✅ Users can install from any version of nushell-full archive
- ✅ Version mismatch plugins skipped without breaking installation
- ✅ Clear, honest error messages
- ✅ Non-confusing PATH update messages
- ✅ Offline installation support via `--source-path`
- ✅ Clean uninstall/reinstall workflow
Developer Benefits:
- ✅ Single source of truth eliminates code duplication
- ✅ Changes propagate automatically through symlinks
- ✅ Reduced maintenance burden
- ✅ Consistent behavior across all paths
- ✅ Production-ready installation process
---
### 1. Help System Integration (New Feature)
**Issue**: Version-update module recipes were not discoverable
- Not shown in `just help modules`
- Not referenced in `just help`
- Not included in help navigation system
- Users had to manually run `just --list` to find update commands
**Solution**:
- Added version-update module to all help outputs
- Updated `justfiles/help.just` to document all 30+ version-update recipes
- Created new `just commands` recipe as discoverable alias for `just --list`
- Integrated version-update into help-all workflow
**Impact**:
- Version-update commands now fully discoverable via help system
- Users can find update commands with: `just help modules`, `just help`, `just commands`
- Improved overall help system navigation
**Files Modified**:
- `justfiles/help.just` (+23 lines)
- Added version-update module to help sections
- Added to modules list
- Added to help-all workflow
- New `commands` recipe showing all recipes by group
### 2. Build Process Fixes (Phase 3: Bin Archives)
#### 2a. Plugin Archive Collection Bug
**Issue**: "No plugins found to package" warning in Phase 3
- Collected 26 plugin binaries but reported 0
- Archive creation skipped because count was wrong
**Root Cause**: `each` command returns null, so `| length` returned 0
```nushell
# ❌ OLD - each returns null
let plugin_count = (ls nu_plugin_*/target/release/nu_plugin_* | each {|p|
cp $p.name $"($temp_dir)/"
} | length) # Returns 0!
```
**Solution**: Separated counting from copying with proper filtering
```nushell
# ✅ NEW - count before operations
let plugins_to_copy = (ls nu_plugin_*/target/release/nu_plugin_* | where type == "file")
let plugin_count = ($plugins_to_copy | length)
```
**Impact**:
- Now correctly collects and reports 26 plugins
- Filters out .d dependency files automatically
- Warning eliminated
#### 2b. Tar Archive Path Handling
**Issue**: Tar command failing silently with relative paths in subshell
- `cd $temp_dir` changes context unpredictably
- Relative path `../$archive_name` fails in subshell
- Archive file not created despite exit code 0
**Root Cause**: Shell context and relative path issues in Nushell `do` block
**Solution**: Used `tar -C` with absolute paths instead of `cd`
```nushell
# ❌ OLD - unreliable context switching
do {
cd $temp_dir
tar -czf ../$archive_name .
}
# ✅ NEW - absolute paths, no context switching
tar -C $temp_dir -czf $archive_path .
```
**Additional Improvements**:
- Absolute path construction using `pwd | path join`
- Better error diagnostics with exit code and stderr output
- File verification after creation
**Impact**:
- Tar archives now created successfully
- Robust path handling across platforms
- Clear error messages for debugging
#### 2c. File Size Calculation Type Error
**Issue**: Runtime error when calculating archive size
```
Error: The '/' operator does not work on values of type 'list<filesize>'
```
**Root Cause**: `ls` returns list of records, so `.size` was a list
```nushell
# ❌ OLD - returns list<filesize>
(ls $archive_path).size / 1024 / 1024
# ✅ NEW - returns filesize
(ls $archive_path | get 0.size) / 1024 / 1024
```
**Impact**:
- Proper file size calculation in MB
- No more type errors
**Files Modified**:
- `scripts/create_full_distribution.nu` (+58 lines, refactored plugin collection)
- Fixed plugin counting logic
- Improved path handling with absolute paths
- Enhanced error diagnostics
### 3. Plugin Rebuild Optimization
**Issue**: All plugins marked for rebuild even when dependencies unchanged
- Step 4 (`update_all_plugins.nu`) touched all Cargo.toml files at 01:00:32
- Step 5 saw all files as "newer" than binaries
- Marked ALL plugins for rebuild, though cargo only rebuilt changed ones
**Root Cause**: Script always saved files, even when no changes made
```nushell
# ❌ OLD - always saves, touching file timestamp
$updated_content | to toml | save -f $cargo_toml
```
**Solution**: Only save if content actually changed
```nushell
# ✅ NEW - compare before writing
let original_toml = $content | to toml
let new_toml = $updated_content | to toml
if $original_toml != $new_toml {
$updated_content | to toml | save -f $cargo_toml
}
```
**Impact**:
- Unchanged files preserve original timestamps
- Only plugins with actual dependency changes are rebuilt
- Efficient rebuild process with accurate file modification detection
**Files Modified**:
- `scripts/update_all_plugins.nu` (+12 lines, added content comparison)
- Only touches files with real changes
- Preserves timestamps for efficiency
- Clearer logic and comments
### 4. Documentation
**Files Modified**:
- `CHANGELOG.md` (+56 lines)
- Added comprehensive 2025-10-19 entry
- Documented all fixes with root causes
- Listed files modified and impact summary
## Technical Details
### Nushell Patterns Used
1. **Proper List Handling**:
- `ls` returns list of records, access with `| get 0.size`
- Filter with `where type == "file"` to exclude metadata
2. **Absolute Path Construction**:
- `pwd | append "path" | path join` for cross-platform paths
- Safer than string concatenation with `/`
3. **Content Comparison**:
- Compare TOML string representation before saving
- Preserves file timestamps for efficiency
4. **Error Diagnostics**:
- Capture `stderr` from commands
- Report exit codes and error messages separately
## Testing
- [x] Help system shows version-update module
- [x] `just commands` displays all recipes by group
- [x] Phase 3 bin archive creation works
- [x] Plugin collection reports correct count (26)
- [x] Tar archives created successfully
- [x] File size calculated correctly
- [x] Plugin rebuild only touches changed files
- [x] CHANGELOG updated with all changes
## Files Changed
```
38 files changed, 2721 insertions(+), 2548 deletions(-)
Core Changes:
- justfiles/help.just (+23) Help system integration
- scripts/create_full_distribution.nu (+58) Build process fixes
- scripts/update_all_plugins.nu (+12) Rebuild optimization
- CHANGELOG.md (+56) Documentation
Dependency Updates:
- All plugin Cargo.toml and Cargo.lock files (version consistency)
```
## Breaking Changes
None. These are bug fixes and optimizations that maintain backward compatibility.
## Migration Notes
No migration needed. Improvements are transparent to users.
## Related Issues
- Help system discoverability
- Build process Phase 3 failures
- Unnecessary plugin rebuilds
- Build process reliability
## Checklist
- [x] Changes follow Rust/Nushell idioms
- [x] Code is well-commented
- [x] Error handling is comprehensive
- [x] Documentation is updated
- [x] All changes tested
- [x] No breaking changes introduced
2025-10-19 02:39:31 +01:00
|
|
|
|
--uninstall-dir)
|
|
|
|
|
|
CUSTOM_UNINSTALL_DIR="$2"
|
|
|
|
|
|
shift 2
|
|
|
|
|
|
;;
|
2025-09-24 18:52:07 +01:00
|
|
|
|
--dry-run)
|
|
|
|
|
|
DRY_RUN=1
|
|
|
|
|
|
shift
|
|
|
|
|
|
;;
|
|
|
|
|
|
--debug)
|
|
|
|
|
|
DEBUG=1
|
|
|
|
|
|
shift
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
log_error "Unknown option: $1"
|
|
|
|
|
|
usage
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
# Determine installation directory
|
# Summary
feat: bootstrap installer with robust archive extraction, version mismatch handling, and improved PATH messaging
## Detailed Description
This commit 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.
## Major Changes (Session 2025-10-19)
### 0. Bootstrap Installer & Distribution Improvements (NEW)
#### 0a. Install Script Architecture - DRY Design
**Issue**: Code duplication across installation paths
- `./install.sh`, `./scripts/templates/install.sh`, `installers/bootstrap/install.sh` were separate copies
- Changes required updating multiple files
- Risk of divergence and inconsistency
**Solution**: Implemented symlink-based DRY architecture
- Single source of truth: `installers/bootstrap/install.sh` (1,247 lines)
- Symlinks created:
- `./install.sh` → `installers/bootstrap/install.sh`
- `./scripts/templates/install.sh` → `installers/bootstrap/install.sh`
- All changes automatically propagate through symlinks
- No code duplication
**Impact**:
- ✅ Single maintenance point for all install scripts
- ✅ Consistent behavior across all paths
- ✅ Reduced maintenance burden
- ✅ No divergence risk
#### 0b. Archive Extraction - Version-Agnostic Binary Detection
**Issue**: "No Nushell binary found in extracted archive"
- Script failed when extracting from `nushell-full-0.108.0-darwin-arm64.tar.gz`
- Error occurred even though binaries were present at `nushell-full-0.108.0/bin/nu`
**Root Cause**: `find` command returning parent directory itself in results
```bash
# ❌ OLD - find returns parent directory first
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" | head -1)
# Returns: /tmp/nushell-install-22919/nushell-extract (parent!)
# Should return: /tmp/nushell-install-22919/nushell-extract/nushell-full-0.108.0 (subdirectory)
```
**Solution**: Added `-not -path` to exclude starting directory
```bash
# ✅ NEW - exclude parent directory
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" -not -path "$extract_dir" | head -1)
# Now correctly returns the subdirectory
```
**Additional Improvements**:
- 4-level fallback detection strategy:
1. Check `$extracted/bin/nu` (subdirectory structure)
2. Check `$extracted/nu` (flat structure)
3. Fallback search for any `nushell-*` subdirectory with `bin/nu`
4. Last resort recursive search for any executable named `nu`
- Validates binaries exist before using them
- Clear error reporting with all search locations
**Version-Agnostic**:
- Uses `nushell-*` pattern (not hardcoded version numbers)
- Works with any Nushell version: 0.107, 0.108, 0.109, etc.
- Supports both `.tar.gz` and `.zip` archive formats
**Impact**:
- ✅ Archive extraction works reliably
- ✅ Works with any Nushell version
- ✅ Clear error messages guide users
- ✅ Multiple archive structure support
#### 0c. Plugin Registration - Version Mismatch Handling
**Issue**: Plugin registration failed with version incompatibility errors
```
Error: nu::shell::plugin_failed_to_load
× Plugin `polars` is compiled for nushell version 0.107.1, which is not compatible with version 0.108.0
```
**Solution**: Implemented intelligent error classification
- Captures both stdout and stderr from plugin add commands
- Detects version incompatibility: "is not compatible with version" or "is compiled for nushell version"
- Classifies errors into three categories:
1. **Success**: Plugin registered successfully ✅
2. **Incompatible**: Version mismatch (skipped gracefully) ⚠️
3. **Failed**: Other registration failures ❌
- Reports summary with counts of each category
- Installation continues successfully even with version mismatches
**New Error Reporting**:
```
✅ Registered nu_plugin_auth
⚠️ Skipping nu_plugin_polars: Version mismatch (built for different Nushell version)
✅ Successfully registered 13 plugins
⚠️ Skipped 1 incompatible plugins (version mismatch):
- nu_plugin_polars
```
**Impact**:
- ✅ No installation failures due to version mismatches
- ✅ Users informed of incompatible plugins
- ✅ Clear distinction between error types
- ✅ Installation completes successfully
#### 0d. Shell Configuration PATH Update - Clear Messaging
**Issue**: Confusing PATH update messages
- User sees: "PATH already updated" for all files
- Then sees: "No shell configuration files were updated" warning
- Then sees: "Please manually add to your PATH" error
- **Problem**: Contradictory messages when PATH is already configured everywhere
**Root Cause**: Script conflated two states
- State 1: "Was PATH found in files?" (skips updating if found)
- State 2: "Did we add PATH to any file?" (used for messaging)
- Both states ignored means no update was made, but PATH might already exist
**Solution**: Track two separate states
```bash
local updated=false # Was PATH ADDED to any file?
local path_found=false # Was PATH FOUND in any file?
# In loop:
if grep -q "$install_dir" "$config_file"; then
path_found=true # Found it! Mark as true
continue
fi
# After loop:
if [ "$updated" = "true" ]; then
log_success "Shell configuration updated"
elif [ "$path_found" = "true" ]; then
log_success "PATH is already configured in your shell configuration files"
else
log_warn "Could not find or update shell configuration"
fi
```
**New Clear Messages**:
- ✅ "PATH is already configured in your shell configuration files" (when found everywhere)
- ✅ "Shell configuration updated" (when just added)
- ⚠️ "Could not find or update shell configuration" (when file missing)
**Impact**:
- ✅ Non-contradictory messages
- ✅ Users understand what happened
- ✅ No false warnings when PATH already configured
- ✅ Clear guidance when manual action needed
#### 0e. Installation Features
**`--source-path` Option** (Local Installation):
- Install from local archive: `./install.sh --source-path archive.tar.gz`
- Install from local directory: `./install.sh --source-path /path/to/binaries`
- Default behavior: `./install.sh --source-path` uses `./bin_archives`
- Works with custom `--install-dir` paths
- No download needed, offline installation support
**`--uninstall` with Configuration Management**:
- Prompts user: "Remove ~/.config/nushell? [y/N]"
- Removes all installed binaries and plugins
- Preserves user choice for configuration
- Clean uninstall before fresh reinstall
#### 0f. Documentation Updates
**CLAUDE.md**:
- Added "Install Script Architecture (DRY Design)" section
- Documents source of truth and symlink structure
- Explains `--source-path` feature
- Shows version-agnostic archive detection
- Lists DRY architecture benefits
**README.md**:
- Added "Install Script Architecture (DRY Design)" subsection
- Shows symlink structure with arrows
- Provides `--source-path` usage examples
- Explains version-agnostic detection
- "How DRY Works" 3-step explanation
#### 0g. Files Modified
Core Changes:
- `installers/bootstrap/install.sh` (+3 lines for PATH messaging fix)
- Archive extraction fix with `-not -path`
- Plugin registration error classification
- Clear PATH update messaging
- Total: 1,247 lines (unified)
Auto-Updated via Symlinks:
- `./install.sh` - Auto-updated (1,247 lines)
- `./scripts/templates/install.sh` - Auto-updated (1,247 lines)
Documentation:
- `CLAUDE.md` - Added install architecture section
- `README.md` - Added install architecture subsection
- `CHANGELOG.md` - Added comprehensive entry (+100 lines)
#### 0h. Testing & Verification
All scenarios tested and verified:
- [x] Archive extraction works with version-agnostic detection
- [x] Installation to `~/.local` successful (16 binaries)
- [x] Installation to `~/.local/bin` successful (21 plugins loaded)
- [x] Plugin registration handles version mismatches gracefully
- [x] PATH messaging is clear and non-contradictory
- [x] Clean uninstall followed by fresh reinstall works perfectly
#### 0i. Impact
User-Facing Benefits:
- ✅ Users can install from any version of nushell-full archive
- ✅ Version mismatch plugins skipped without breaking installation
- ✅ Clear, honest error messages
- ✅ Non-confusing PATH update messages
- ✅ Offline installation support via `--source-path`
- ✅ Clean uninstall/reinstall workflow
Developer Benefits:
- ✅ Single source of truth eliminates code duplication
- ✅ Changes propagate automatically through symlinks
- ✅ Reduced maintenance burden
- ✅ Consistent behavior across all paths
- ✅ Production-ready installation process
---
### 1. Help System Integration (New Feature)
**Issue**: Version-update module recipes were not discoverable
- Not shown in `just help modules`
- Not referenced in `just help`
- Not included in help navigation system
- Users had to manually run `just --list` to find update commands
**Solution**:
- Added version-update module to all help outputs
- Updated `justfiles/help.just` to document all 30+ version-update recipes
- Created new `just commands` recipe as discoverable alias for `just --list`
- Integrated version-update into help-all workflow
**Impact**:
- Version-update commands now fully discoverable via help system
- Users can find update commands with: `just help modules`, `just help`, `just commands`
- Improved overall help system navigation
**Files Modified**:
- `justfiles/help.just` (+23 lines)
- Added version-update module to help sections
- Added to modules list
- Added to help-all workflow
- New `commands` recipe showing all recipes by group
### 2. Build Process Fixes (Phase 3: Bin Archives)
#### 2a. Plugin Archive Collection Bug
**Issue**: "No plugins found to package" warning in Phase 3
- Collected 26 plugin binaries but reported 0
- Archive creation skipped because count was wrong
**Root Cause**: `each` command returns null, so `| length` returned 0
```nushell
# ❌ OLD - each returns null
let plugin_count = (ls nu_plugin_*/target/release/nu_plugin_* | each {|p|
cp $p.name $"($temp_dir)/"
} | length) # Returns 0!
```
**Solution**: Separated counting from copying with proper filtering
```nushell
# ✅ NEW - count before operations
let plugins_to_copy = (ls nu_plugin_*/target/release/nu_plugin_* | where type == "file")
let plugin_count = ($plugins_to_copy | length)
```
**Impact**:
- Now correctly collects and reports 26 plugins
- Filters out .d dependency files automatically
- Warning eliminated
#### 2b. Tar Archive Path Handling
**Issue**: Tar command failing silently with relative paths in subshell
- `cd $temp_dir` changes context unpredictably
- Relative path `../$archive_name` fails in subshell
- Archive file not created despite exit code 0
**Root Cause**: Shell context and relative path issues in Nushell `do` block
**Solution**: Used `tar -C` with absolute paths instead of `cd`
```nushell
# ❌ OLD - unreliable context switching
do {
cd $temp_dir
tar -czf ../$archive_name .
}
# ✅ NEW - absolute paths, no context switching
tar -C $temp_dir -czf $archive_path .
```
**Additional Improvements**:
- Absolute path construction using `pwd | path join`
- Better error diagnostics with exit code and stderr output
- File verification after creation
**Impact**:
- Tar archives now created successfully
- Robust path handling across platforms
- Clear error messages for debugging
#### 2c. File Size Calculation Type Error
**Issue**: Runtime error when calculating archive size
```
Error: The '/' operator does not work on values of type 'list<filesize>'
```
**Root Cause**: `ls` returns list of records, so `.size` was a list
```nushell
# ❌ OLD - returns list<filesize>
(ls $archive_path).size / 1024 / 1024
# ✅ NEW - returns filesize
(ls $archive_path | get 0.size) / 1024 / 1024
```
**Impact**:
- Proper file size calculation in MB
- No more type errors
**Files Modified**:
- `scripts/create_full_distribution.nu` (+58 lines, refactored plugin collection)
- Fixed plugin counting logic
- Improved path handling with absolute paths
- Enhanced error diagnostics
### 3. Plugin Rebuild Optimization
**Issue**: All plugins marked for rebuild even when dependencies unchanged
- Step 4 (`update_all_plugins.nu`) touched all Cargo.toml files at 01:00:32
- Step 5 saw all files as "newer" than binaries
- Marked ALL plugins for rebuild, though cargo only rebuilt changed ones
**Root Cause**: Script always saved files, even when no changes made
```nushell
# ❌ OLD - always saves, touching file timestamp
$updated_content | to toml | save -f $cargo_toml
```
**Solution**: Only save if content actually changed
```nushell
# ✅ NEW - compare before writing
let original_toml = $content | to toml
let new_toml = $updated_content | to toml
if $original_toml != $new_toml {
$updated_content | to toml | save -f $cargo_toml
}
```
**Impact**:
- Unchanged files preserve original timestamps
- Only plugins with actual dependency changes are rebuilt
- Efficient rebuild process with accurate file modification detection
**Files Modified**:
- `scripts/update_all_plugins.nu` (+12 lines, added content comparison)
- Only touches files with real changes
- Preserves timestamps for efficiency
- Clearer logic and comments
### 4. Documentation
**Files Modified**:
- `CHANGELOG.md` (+56 lines)
- Added comprehensive 2025-10-19 entry
- Documented all fixes with root causes
- Listed files modified and impact summary
## Technical Details
### Nushell Patterns Used
1. **Proper List Handling**:
- `ls` returns list of records, access with `| get 0.size`
- Filter with `where type == "file"` to exclude metadata
2. **Absolute Path Construction**:
- `pwd | append "path" | path join` for cross-platform paths
- Safer than string concatenation with `/`
3. **Content Comparison**:
- Compare TOML string representation before saving
- Preserves file timestamps for efficiency
4. **Error Diagnostics**:
- Capture `stderr` from commands
- Report exit codes and error messages separately
## Testing
- [x] Help system shows version-update module
- [x] `just commands` displays all recipes by group
- [x] Phase 3 bin archive creation works
- [x] Plugin collection reports correct count (26)
- [x] Tar archives created successfully
- [x] File size calculated correctly
- [x] Plugin rebuild only touches changed files
- [x] CHANGELOG updated with all changes
## Files Changed
```
38 files changed, 2721 insertions(+), 2548 deletions(-)
Core Changes:
- justfiles/help.just (+23) Help system integration
- scripts/create_full_distribution.nu (+58) Build process fixes
- scripts/update_all_plugins.nu (+12) Rebuild optimization
- CHANGELOG.md (+56) Documentation
Dependency Updates:
- All plugin Cargo.toml and Cargo.lock files (version consistency)
```
## Breaking Changes
None. These are bug fixes and optimizations that maintain backward compatibility.
## Migration Notes
No migration needed. Improvements are transparent to users.
## Related Issues
- Help system discoverability
- Build process Phase 3 failures
- Unnecessary plugin rebuilds
- Build process reliability
## Checklist
- [x] Changes follow Rust/Nushell idioms
- [x] Code is well-commented
- [x] Error handling is comprehensive
- [x] Documentation is updated
- [x] All changes tested
- [x] No breaking changes introduced
2025-10-19 02:39:31 +01:00
|
|
|
|
if [ -n "$CUSTOM_UNINSTALL_DIR" ]; then
|
|
|
|
|
|
# Custom directory provided - use it directly (bypass all checks)
|
|
|
|
|
|
INSTALL_DIR="$CUSTOM_UNINSTALL_DIR"
|
|
|
|
|
|
log_info "Using custom uninstall directory (via --uninstall-dir): $INSTALL_DIR"
|
|
|
|
|
|
elif [ "$SYSTEM_INSTALL" = "1" ]; then
|
2025-09-24 18:52:07 +01:00
|
|
|
|
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 "$@"
|