# Installs lsh, the LogiShell command line, for this user on Windows: # irm https://cli.lsh.dev/install.ps1 | iex # # $env:LSH_VERSION a release to install (0.2.0); default: the latest # $env:LSH_INSTALL_DIR where lsh.exe goes; default: %LOCALAPPDATA%\Programs\lsh # $env:LSH_DL where releases live; default: https://cli.lsh.dev/cli # # No administrator rights, PATH is not changed silently: the script prints the command that # adds the directory. Layout of the release server: harness/cli/dist/README.md. & { Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' function Fail([string]$Message, [string]$Hint) { Write-Host "error: $Message" -ForegroundColor Red if ($Hint) { Write-Host " hint: $Hint" -ForegroundColor DarkGray } throw "lsh install: $Message" } # TLS 1.2 for Windows PowerShell 5.1 (PowerShell 7 already has it). try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } $base = if ($env:LSH_DL) { $env:LSH_DL.TrimEnd('/') } else { 'https://cli.lsh.dev/cli' } $dir = if ($env:LSH_INSTALL_DIR) { $env:LSH_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'Programs\lsh' } $version = if ($env:LSH_VERSION) { $env:LSH_VERSION.TrimStart('v') } else { '' } # The architecture of the OS, not of this process (a 32 bit or emulated shell lies). $arch = $env:PROCESSOR_ARCHITEW6432 if (-not $arch) { $arch = $env:PROCESSOR_ARCHITECTURE } switch ($arch) { 'AMD64' { $target = 'x86_64-pc-windows-msvc' } 'ARM64' { # No native ARM64 build yet: the x64 build runs under the emulation of Windows 11. $target = 'x86_64-pc-windows-msvc' Write-Host '! no native ARM64 build yet, installing the x64 build (runs under emulation)' -ForegroundColor Yellow } default { Fail "no lsh build for Windows on $arch" 'builds: https://lsh.dev/dl' } } $name = "lsh-$target.exe" if ($version) { $url = "$base/v$version/$name" try { $sumText = [string](Invoke-RestMethod -UseBasicParsing -Uri "$base/v$version/lsh-$target.sha256") } catch { Fail "no lsh $version for $target at $base/v$version/" 'see the releases at https://lsh.dev/dl' } $sum = ($sumText.Trim() -split '\s+')[0].ToLowerInvariant() } else { try { $manifest = Invoke-RestMethod -UseBasicParsing -Uri "$base/latest.json" } catch { Fail "could not read $base/latest.json: $($_.Exception.Message)" 'check the network, or set $env:LSH_DL to a mirror' } $version = ([string]$manifest.version).TrimStart('v') if (-not $version) { Fail "$base/latest.json names no version" 'report it at https://lsh.dev/gh' } $asset = $manifest.assets.PSObject.Properties[$target] if (-not $asset) { Fail "lsh $version has no build for $target" 'see the builds at https://lsh.dev/dl' } $url = [string]$asset.Value.url $sum = ([string]$asset.Value.sha256).ToLowerInvariant() } if ($sum -notmatch '^[0-9a-f]{64}$') { Fail "no valid sha256 for $name" 'report it at https://lsh.dev/gh' } Write-Host "installing lsh $version for $target" try { New-Item -ItemType Directory -Force -Path $dir | Out-Null } catch { Fail "cannot create $dir" 'pick another directory: $env:LSH_INSTALL_DIR = ""' } $tmp = Join-Path $dir ".lsh-install-$PID.exe" $exe = Join-Path $dir 'lsh.exe' try { try { Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $tmp } catch { Fail "download of $url failed: $($_.Exception.Message)" 'check the network and run the installer again' } $got = (Get-FileHash -Algorithm SHA256 -Path $tmp).Hash.ToLowerInvariant() if ($got -ne $sum) { Fail "checksum mismatch for ${name}: expected $sum, got $got" 'nothing was installed; run the installer again' } try { Move-Item -Force -Path $tmp -Destination $exe } catch { Fail "cannot write $exe (is lsh running?)" 'stop the daemon (lsh daemon stop) and run the installer again' } } finally { if (Test-Path $tmp) { Remove-Item -Force $tmp -ErrorAction SilentlyContinue } } # Marks as code points: `irm | iex` in Windows PowerShell 5.1 may not decode the script as UTF-8. $check = [char]0x2713 $arrow = [char]0x2192 Write-Host "$check lsh $version installed to $exe" -ForegroundColor Green try { & $exe version } catch { } $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') $onPath = @($env:Path -split ';') + @("$userPath" -split ';') | Where-Object { $_.TrimEnd('\') -ieq $dir.TrimEnd('\') } if ($onPath) { $cmd = 'lsh' } else { $cmd = "& '$exe'" Write-Host "! $dir is not on your PATH; to add it for your user run:" -ForegroundColor Yellow Write-Host " [Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';$dir', 'User')" Write-Host ' then open a new terminal.' } Write-Host "$arrow next: $cmd onboard" -ForegroundColor Cyan }