# Universal Nushell + Plugins Bootstrap Installer for Windows # PowerShell script that installs Nushell and plugins without any prerequisites # # This script: # - Detects Windows platform (x86_64/arm64) # - Downloads or builds Nushell + plugins # - Installs to user location (%USERPROFILE%\.local\bin) or system (C:\Program Files\Nushell) # - Updates PATH in system/user environment # - Creates initial Nushell configuration # - Registers all plugins automatically # - Verifies installation param( [switch]$System, [switch]$User = $true, [switch]$NoPath, [switch]$NoConfig, [switch]$NoPlugins, [switch]$BuildFromSource, [switch]$Verify, [switch]$Uninstall, [string]$Version = "", [switch]$Help ) # Configuration $RepoUrl = "https://github.com/your-org/nushell-plugins" $BinaryRepoUrl = "$RepoUrl/releases/download" $InstallDirUser = "$env:USERPROFILE\.local\bin" $InstallDirSystem = "${env:ProgramFiles}\Nushell\bin" $ConfigDir = "$env:USERPROFILE\.config\nushell" $TempDir = "$env:TEMP\nushell-install-$(Get-Random)" # Colors for output $Colors = @{ Red = "Red" Green = "Green" Yellow = "Yellow" Blue = "Blue" Magenta = "Magenta" Cyan = "Cyan" } # Logging functions function Write-LogInfo { param([string]$Message) Write-Host "ℹ️ $Message" -ForegroundColor $Colors.Blue } function Write-LogSuccess { param([string]$Message) Write-Host "✅ $Message" -ForegroundColor $Colors.Green } function Write-LogWarn { param([string]$Message) Write-Host "⚠️ $Message" -ForegroundColor $Colors.Yellow } function Write-LogError { param([string]$Message) Write-Host "❌ $Message" -ForegroundColor $Colors.Red } function Write-LogHeader { param([string]$Message) Write-Host "" Write-Host "🚀 $Message" -ForegroundColor $Colors.Magenta Write-Host ("=" * $Message.Length) -ForegroundColor $Colors.Magenta } # Usage information function Show-Usage { @" Nushell + Plugins Bootstrap Installer for Windows USAGE: # Download and run: Invoke-WebRequest -Uri "install-url/install.ps1" | Invoke-Expression # Or download and run with parameters: .\install.ps1 [-System] [-User] [-NoPath] [-NoConfig] [-NoPlugins] [-BuildFromSource] [-Verify] [-Uninstall] [-Version ] [-Help] PARAMETERS: -System Install to system directory (C:\Program Files\Nushell, requires admin) -User Install to user directory (~\.local\bin) [default] -NoPath Don't modify PATH environment variable -NoConfig Don't create initial nushell configuration -NoPlugins Install only nushell, skip plugins -BuildFromSource Build from source instead of downloading binaries -Verify Verify installation after completion -Uninstall Remove nushell and plugins -Version Install specific version (default: latest) -Help Show this help message EXAMPLES: # Default installation (user directory, with plugins) .\install.ps1 # System installation (requires admin) .\install.ps1 -System # Install without plugins .\install.ps1 -NoPlugins # Build from source .\install.ps1 -BuildFromSource # Install specific version .\install.ps1 -Version "v0.107.1" "@ } # Platform detection function Get-Platform { $arch = $env:PROCESSOR_ARCHITECTURE switch ($arch) { "AMD64" { return "windows-x86_64" } "ARM64" { return "windows-aarch64" } default { Write-LogError "Unsupported architecture: $arch" exit 1 } } } # Check if running as administrator function Test-Admin { $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($currentUser) return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } # Check if command exists function Test-Command { param([string]$Command) try { Get-Command $Command -ErrorAction Stop | Out-Null return $true } catch { return $false } } # Check dependencies for building from source function Test-BuildDependencies { $missing = @() if (-not (Test-Command "git")) { $missing += "git" } if (-not (Test-Command "cargo")) { $missing += "cargo" } if (-not (Test-Command "rustc")) { $missing += "rust" } if ($missing.Count -gt 0) { Write-LogError "Missing build dependencies: $($missing -join ', ')" Write-LogInfo "Please install these tools or use binary installation instead" return $false } return $true } # Download file with progress function Get-FileWithProgress { param( [string]$Url, [string]$OutputPath, [string]$Description = "file" ) Write-LogInfo "Downloading $Description..." try { $webClient = New-Object System.Net.WebClient $webClient.DownloadFile($Url, $OutputPath) Write-LogSuccess "Downloaded $Description" return $true } catch { Write-LogError "Failed to download $Description from $Url" Write-LogError $_.Exception.Message return $false } } # Extract archive function Expand-Archive { param( [string]$ArchivePath, [string]$DestinationPath ) Write-LogInfo "Extracting archive..." try { if ($ArchivePath -like "*.zip") { Expand-Archive -Path $ArchivePath -DestinationPath $DestinationPath -Force } elseif ($ArchivePath -like "*.tar.gz") { # Use tar if available (Windows 10 build 17063 and later) if (Test-Command "tar") { & tar -xzf $ArchivePath -C $DestinationPath } else { throw "tar command not found for .tar.gz extraction" } } else { throw "Unsupported archive format: $ArchivePath" } Write-LogSuccess "Extracted archive" return $true } catch { Write-LogError "Failed to extract $ArchivePath" Write-LogError $_.Exception.Message return $false } } # Get latest release version function Get-LatestVersion { try { $response = Invoke-RestMethod -Uri "https://api.github.com/repos/your-org/nushell-plugins/releases/latest" return $response.tag_name } catch { Write-LogWarn "Could not detect latest version, using v0.107.1" return "v0.107.1" } } # Download and install binaries function Install-FromBinaries { param( [string]$Platform, [string]$Version, [string]$InstallDir, [bool]$IncludePlugins ) Write-LogHeader "Installing from Pre-built Binaries" # Create temporary directory New-Item -ItemType Directory -Path $TempDir -Force | Out-Null Set-Location $TempDir # Determine archive name and URL $archiveFormat = if ($Platform -like "*windows*") { "zip" } else { "tar.gz" } $archiveName = "nushell-plugins-$Platform-$Version.$archiveFormat" $downloadUrl = "$BinaryRepoUrl/$Version/$archiveName" # Download archive if (-not (Get-FileWithProgress -Url $downloadUrl -OutputPath $archiveName -Description "Nushell distribution")) { Write-LogWarn "Binary download failed, trying alternative..." # Try without version prefix $archiveName = "nushell-plugins-$Platform.$archiveFormat" $downloadUrl = "$BinaryRepoUrl/latest/$archiveName" if (-not (Get-FileWithProgress -Url $downloadUrl -OutputPath $archiveName -Description "Nushell distribution (latest)")) { Write-LogError "Failed to download binaries" return $false } } # Extract archive if (-not (Expand-Archive -ArchivePath $archiveName -DestinationPath ".")) { return $false } # Find extracted directory $extractDir = Get-ChildItem -Directory | Select-Object -First 1 if (-not $extractDir) { Write-LogError "No extracted directory found" return $false } Write-LogInfo "Installing binaries to $InstallDir..." # Create install directory New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null # Install nushell binary $nuBinary = "nu.exe" $nuSourcePath = Join-Path $extractDir.FullName $nuBinary if (Test-Path $nuSourcePath) { Copy-Item $nuSourcePath -Destination $InstallDir Write-LogSuccess "Installed nushell binary" } else { Write-LogError "Nushell binary not found in archive" return $false } # Install plugins if requested if ($IncludePlugins) { $pluginFiles = Get-ChildItem -Path $extractDir.FullName -Name "nu_plugin_*.exe" $pluginCount = 0 foreach ($pluginFile in $pluginFiles) { $pluginSourcePath = Join-Path $extractDir.FullName $pluginFile Copy-Item $pluginSourcePath -Destination $InstallDir $pluginCount++ } if ($pluginCount -gt 0) { Write-LogSuccess "Installed $pluginCount plugins" } else { Write-LogWarn "No plugins found in archive" } } # Copy configuration files if they exist $configSourceDir = Join-Path $extractDir.FullName "config" if (Test-Path $configSourceDir) { New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null Copy-Item -Path "$configSourceDir\*" -Destination $ConfigDir -Recurse -Force Write-LogSuccess "Installed configuration files" } return $true } # Build and install from source function Install-FromSource { param( [string]$InstallDir, [bool]$IncludePlugins ) Write-LogHeader "Building from Source" # Check dependencies if (-not (Test-BuildDependencies)) { return $false } # Create temporary directory New-Item -ItemType Directory -Path $TempDir -Force | Out-Null Set-Location $TempDir # Clone repository Write-LogInfo "Cloning repository..." try { & git clone --recursive $RepoUrl nushell-plugins Set-Location nushell-plugins } catch { Write-LogError "Failed to clone repository" return $false } # Build nushell Write-LogInfo "Building nushell..." try { if (Test-Command "just") { & just build-nushell } else { # Fallback to manual build Set-Location nushell & cargo build --release --features "plugin,network,sqlite,trash-support,rustls-tls" Set-Location .. } } catch { Write-LogError "Failed to build nushell" return $false } # Build plugins if requested if ($IncludePlugins) { Write-LogInfo "Building plugins..." try { if (Test-Command "just") { & just build } else { # Build plugins manually $pluginDirs = Get-ChildItem -Directory -Name "nu_plugin_*" | Where-Object { $_ -ne "nushell" } foreach ($pluginDir in $pluginDirs) { Write-LogInfo "Building $pluginDir..." Set-Location $pluginDir try { & cargo build --release Write-LogSuccess "Built $pluginDir" } catch { Write-LogWarn "Failed to build $pluginDir" } Set-Location .. } } } catch { Write-LogWarn "Failed to build some plugins" } } # Install binaries Write-LogInfo "Installing binaries to $InstallDir..." New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null # Install nushell $nuBinary = "nushell\target\release\nu.exe" if (Test-Path $nuBinary) { Copy-Item $nuBinary -Destination "$InstallDir\nu.exe" Write-LogSuccess "Installed nushell binary" } else { Write-LogError "Nushell binary not found" return $false } # Install plugins if ($IncludePlugins) { $pluginCount = 0 $pluginDirs = Get-ChildItem -Directory -Name "nu_plugin_*" | Where-Object { $_ -ne "nushell" } foreach ($pluginDir in $pluginDirs) { $pluginBinary = "$pluginDir\target\release\$pluginDir.exe" if (Test-Path $pluginBinary) { Copy-Item $pluginBinary -Destination "$InstallDir\$pluginDir.exe" $pluginCount++ } } if ($pluginCount -gt 0) { Write-LogSuccess "Installed $pluginCount plugins" } else { Write-LogWarn "No plugins were built successfully" } } return $true } # Register plugins with nushell function Register-Plugins { param( [string]$InstallDir ) $nuBinary = Join-Path $InstallDir "nu.exe" if (-not (Test-Path $nuBinary)) { Write-LogError "Nushell binary not found: $nuBinary" return $false } Write-LogHeader "Registering Plugins" # Find all plugin binaries $pluginFiles = Get-ChildItem -Path $InstallDir -Name "nu_plugin_*.exe" $pluginCount = 0 foreach ($pluginFile in $pluginFiles) { $pluginPath = Join-Path $InstallDir $pluginFile $pluginName = [System.IO.Path]::GetFileNameWithoutExtension($pluginFile) Write-LogInfo "Registering $pluginName..." try { & $nuBinary -c "plugin add '$pluginPath'" Write-LogSuccess "Registered $pluginName" $pluginCount++ } catch { Write-LogWarn "Failed to register $pluginName" } } if ($pluginCount -gt 0) { Write-LogSuccess "Successfully registered $pluginCount plugins" } else { Write-LogWarn "No plugins were registered" } return $true } # Update PATH environment variable function Update-PathEnvironment { param( [string]$InstallDir, [bool]$SystemInstall ) Write-LogHeader "Updating PATH Environment" # Determine scope $scope = if ($SystemInstall) { "Machine" } else { "User" } try { # Get current PATH $currentPath = [Environment]::GetEnvironmentVariable("PATH", $scope) # Check if already in PATH if ($currentPath -split ';' -contains $InstallDir) { Write-LogInfo "PATH already contains $InstallDir" return $true } # Add to PATH $newPath = "$InstallDir;$currentPath" [Environment]::SetEnvironmentVariable("PATH", $newPath, $scope) # Update current session $env:PATH = "$InstallDir;$env:PATH" Write-LogSuccess "Updated PATH environment variable" Write-LogInfo "Restart your terminal to apply changes globally" return $true } catch { Write-LogError "Failed to update PATH environment variable" Write-LogError $_.Exception.Message return $false } } # Create initial nushell configuration function New-NushellConfig { Write-LogHeader "Creating Nushell Configuration" # Create config directory New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null # Create basic config.nu if it doesn't exist $configFile = Join-Path $ConfigDir "config.nu" if (-not (Test-Path $configFile)) { @" # Nushell Configuration # Created by nushell-plugins installer # Set up basic configuration `$env.config = { show_banner: false edit_mode: emacs shell_integration: true table: { mode: rounded index_mode: always show_empty: true padding: { left: 1, right: 1 } } completions: { case_sensitive: false quick: true partial: true algorithm: "prefix" } history: { max_size: 10000 sync_on_enter: true file_format: "plaintext" } filesize: { metric: false format: "auto" } } # Load custom commands and aliases # Add your custom configuration below "@ | Out-File -FilePath $configFile -Encoding utf8 Write-LogSuccess "Created config.nu" } else { Write-LogInfo "config.nu already exists, skipping" } # Create basic env.nu if it doesn't exist $envFile = Join-Path $ConfigDir "env.nu" if (-not (Test-Path $envFile)) { @" # Nushell Environment Configuration # Created by nushell-plugins installer # Environment variables `$env.EDITOR = "notepad" `$env.BROWSER = "msedge" # Nushell specific environment `$env.NU_LIB_DIRS = [ (`$nu.config-path | path dirname | path join "scripts") ] `$env.NU_PLUGIN_DIRS = [ (`$nu.config-path | path dirname | path join "plugins") ] # Add your custom environment variables below "@ | Out-File -FilePath $envFile -Encoding utf8 Write-LogSuccess "Created env.nu" } else { Write-LogInfo "env.nu already exists, skipping" } # Create directories $scriptsDir = Join-Path $ConfigDir "scripts" $pluginsDir = Join-Path $ConfigDir "plugins" New-Item -ItemType Directory -Path $scriptsDir -Force | Out-Null New-Item -ItemType Directory -Path $pluginsDir -Force | Out-Null return $true } # Verify installation function Test-Installation { param( [string]$InstallDir ) $nuBinary = Join-Path $InstallDir "nu.exe" Write-LogHeader "Verifying Installation" # Check if nushell binary exists if (-not (Test-Path $nuBinary)) { Write-LogError "Nushell binary not found: $nuBinary" return $false } # Test nushell version Write-LogInfo "Testing nushell binary..." try { $versionOutput = & $nuBinary --version Write-LogSuccess "Nushell version: $versionOutput" } catch { Write-LogError "Failed to run nushell binary" Write-LogError $_.Exception.Message return $false } # Test basic nushell command Write-LogInfo "Testing basic nushell functionality..." try { $testOutput = & $nuBinary -c "echo 'Hello from Nushell'" if ($testOutput -eq "Hello from Nushell") { Write-LogSuccess "Basic nushell functionality works" } else { Write-LogWarn "Unexpected output from basic test" } } catch { Write-LogError "Basic nushell functionality failed" return $false } # List registered plugins Write-LogInfo "Checking registered plugins..." try { $pluginOutput = & $nuBinary -c "plugin list" $pluginCount = ($pluginOutput | Select-String "nu_plugin_").Count if ($pluginCount -gt 0) { Write-LogSuccess "Found $pluginCount registered plugins" } else { Write-LogWarn "No plugins are registered" } } catch { Write-LogWarn "Could not check plugin status" } # Check PATH Write-LogInfo "Checking PATH configuration..." try { $nuInPath = Get-Command nu -ErrorAction SilentlyContinue if ($nuInPath) { Write-LogSuccess "Nushell is available in PATH" } else { Write-LogWarn "Nushell is not in PATH. You may need to restart your terminal." } } catch { Write-LogWarn "Could not verify PATH configuration" } Write-LogSuccess "Installation verification complete!" return $true } # Uninstall function function Uninstall-Nushell { Write-LogHeader "Uninstalling Nushell" $removedFiles = 0 # Remove from user directory if (Test-Path $InstallDirUser) { $userBinaries = Get-ChildItem -Path $InstallDirUser -Name "nu.exe", "nu_plugin_*.exe" -ErrorAction SilentlyContinue foreach ($binary in $userBinaries) { $filePath = Join-Path $InstallDirUser $binary Remove-Item $filePath -Force -ErrorAction SilentlyContinue Write-LogSuccess "Removed $binary from $InstallDirUser" $removedFiles++ } } # Remove from system directory (if accessible) if ((Test-Admin) -and (Test-Path $InstallDirSystem)) { $systemBinaries = Get-ChildItem -Path $InstallDirSystem -Name "nu.exe", "nu_plugin_*.exe" -ErrorAction SilentlyContinue foreach ($binary in $systemBinaries) { $filePath = Join-Path $InstallDirSystem $binary Remove-Item $filePath -Force -ErrorAction SilentlyContinue Write-LogSuccess "Removed $binary from $InstallDirSystem" $removedFiles++ } } # Option to remove configuration $response = Read-Host "Remove nushell configuration directory ($ConfigDir)? [y/N]" if ($response -match "^[yY]") { if (Test-Path $ConfigDir) { Remove-Item $ConfigDir -Recurse -Force -ErrorAction SilentlyContinue Write-LogSuccess "Removed configuration directory" } } else { Write-LogInfo "Configuration directory preserved" } if ($removedFiles -gt 0) { Write-LogSuccess "Uninstallation complete ($removedFiles files removed)" Write-LogWarn "You may need to manually remove PATH entries from your environment variables" } else { Write-LogWarn "No nushell files found to remove" } } # Main installation function function Main { # Handle help if ($Help) { Show-Usage exit 0 } # Handle uninstall if ($Uninstall) { Uninstall-Nushell exit 0 } # Show header Write-LogHeader "Nushell + Plugins Installer for Windows" Write-LogInfo "Universal bootstrap installer for Nushell and plugins" Write-LogInfo "" # Detect platform $platform = Get-Platform Write-LogInfo "Detected platform: $platform" # Determine installation directory and check privileges if ($System) { $installDir = $InstallDirSystem if (-not (Test-Admin)) { Write-LogError "System installation requires administrator privileges" Write-LogInfo "Run PowerShell as Administrator or use -User for user installation" exit 1 } } else { $installDir = $InstallDirUser } Write-LogInfo "Installing to: $installDir" # Get version if not specified if (-not $Version) { $Version = Get-LatestVersion } Write-LogInfo "Version: $Version" # Cleanup function $cleanup = { if (Test-Path $TempDir) { Remove-Item $TempDir -Recurse -Force -ErrorAction SilentlyContinue } } try { # Install based on method if ($BuildFromSource) { if (-not (Install-FromSource -InstallDir $installDir -IncludePlugins (-not $NoPlugins))) { Write-LogError "Source installation failed" exit 1 } } else { if (-not (Install-FromBinaries -Platform $platform -Version $Version -InstallDir $installDir -IncludePlugins (-not $NoPlugins))) { Write-LogError "Binary installation failed" exit 1 } } # Register plugins if (-not $NoPlugins) { Register-Plugins -InstallDir $installDir } # Update PATH if (-not $NoPath) { Update-PathEnvironment -InstallDir $installDir -SystemInstall $System } # Create configuration if (-not $NoConfig) { New-NushellConfig } # Verify installation if ($Verify) { if (-not (Test-Installation -InstallDir $installDir)) { Write-LogError "Installation verification failed" exit 1 } } # Final success message Write-LogHeader "Installation Complete!" Write-LogSuccess "Nushell has been successfully installed to $installDir" if (-not $NoPlugins) { Write-LogSuccess "Plugins have been registered with Nushell" } if (-not $NoPath) { Write-LogInfo "To use Nushell, restart your terminal or start a new PowerShell session" } Write-LogInfo "" Write-LogInfo "Try running: nu --version" Write-LogInfo "Or start Nushell with: nu" if (-not $NoPlugins) { Write-LogInfo "Check plugins with: nu -c 'plugin list'" } Write-LogInfo "" Write-LogInfo "For more information, visit: https://nushell.sh" Write-LogInfo "" Write-LogSuccess "Happy shell scripting! 🚀" } finally { & $cleanup } } # Run main function Main