#Requires -Version 5.1 <# Windows-natives Pendant zu bootstrap/neovim.sh - nutzt winget/npm statt brew/pkg, daher eigenstaendiges Skript ohne Git-Bash-/WSL-Abhaengigkeit. Aufruf (keine Admin-Rechte noetig): .\bootstrap-win\neovim.ps1 yamlfmt, gawk und xmllint sind bewusst nicht enthalten (siehe neovim-windows-setup.md). Fehlgeschlagene Installationen werden als Warnung gemeldet, das Skript bricht nicht ab. #> $ErrorActionPreference = "Stop" $script:Failures = @() function Write-Ok($msg) { Write-Host "OK $msg" -ForegroundColor Green } function Write-Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan } function Write-Skip($msg) { Write-Host "- $msg bereits vorhanden" -ForegroundColor DarkGray } function Write-Warn($msg) { Write-Host "! $msg" -ForegroundColor Yellow $script:Failures += $msg } function Test-CommandExists([string]$Name) { return [bool](Get-Command $Name -ErrorAction SilentlyContinue) } function Find-BinaryDirectory([string]$Binary) { # Deckt den Fall ab, dass winget (v.a. --silent) korrekt installiert, # aber die PATH-Registrierung ausgelassen hat (beobachtet bei LLVM.LLVM). $searchRoots = @( $env:ProgramFiles, ${env:ProgramFiles(x86)}, (Join-Path $env:LOCALAPPDATA "Programs") ) | Where-Object { $_ -and (Test-Path $_) } foreach ($root in $searchRoots) { $found = Get-ChildItem -Path $root -Recurse -Depth 4 -Filter "$Binary.exe" -File -ErrorAction SilentlyContinue | Select-Object -First 1 if ($found) { return $found.DirectoryName } } return $null } function Add-ToUserPath([string]$Dir) { $userPath = [Environment]::GetEnvironmentVariable("PATH", "User") $entries = $userPath -split ";" if ($entries -contains $Dir) { return $false } [Environment]::SetEnvironmentVariable("PATH", "$userPath;$Dir", "User") $env:PATH = "$env:PATH;$Dir" return $true } function Install-WingetPackage([string]$Binary, [string]$Id) { if (Test-CommandExists $Binary) { Write-Skip $Binary return } Write-Step "winget install $Id" $output = winget install --id $Id -e --source winget --accept-package-agreements --accept-source-agreements --silent 2>&1 if ($LASTEXITCODE -ne 0) { $text = $output -join " " $lastLine = ($output | Select-Object -Last 3) -join " / " if ($text -match "bereits ein vorhandenes Paket|already installed|No applicable update") { # Installiert, aber nicht im PATH - Binary suchen und PATH ergaenzen. $dir = Find-BinaryDirectory $Binary if ($dir) { Add-ToUserPath $dir | Out-Null if (Test-CommandExists $Binary) { Write-Ok "$Id ('$Binary' in $dir gefunden, User-PATH ergaenzt)" } else { Write-Warn "${Id}: $dir zum User-PATH hinzugefuegt, aber '$Binary' laesst sich immer noch nicht ausfuehren - manuell pruefen." } } else { Write-Warn "$Id ist laut winget bereits installiert, aber '$Binary' wurde nicht gefunden und konnte auch nicht automatisch lokalisiert werden (durchsucht: Program Files, Program Files (x86), $env:LOCALAPPDATA\Programs). Installationsordner manuell suchen und zum PATH hinzufuegen." } } else { Write-Warn "winget install $Id fehlgeschlagen (Binary '$Binary' nicht gefunden): $lastLine -- Pruefen mit: winget search $Id" } return } Write-Ok $Id } function Test-BinaryRuns([string]$Binary, [string[]]$VerifyArgs) { # Einige npm-Pakete (z.B. tree-sitter-cli) installieren nur einen JS-Shim # und laden die eigentliche native Binary per Post-Install-Skript separat # von GitHub herunter. Schlaegt dieser Download fehl (Proxy, ignore-scripts, # Antivirus-Quarantaene), landet der Shim trotzdem im PATH und # Test-CommandExists meldet faelschlich Erfolg. Deshalb hier tatsaechlich # ausfuehren statt nur pruefen, ob der Befehlsname aufloesbar ist. try { & $Binary @VerifyArgs *> $null return $LASTEXITCODE -eq 0 } catch { return $false } } function Install-NpmPackage([string]$Binary, [string]$Package, [string[]]$VerifyArgs = $null, [switch]$AllowScripts) { if (Test-CommandExists $Binary) { if ($VerifyArgs -and -not (Test-BinaryRuns $Binary $VerifyArgs)) { Write-Warn "'$Binary' ist im PATH, laesst sich aber nicht ausfuehren - vermutlich hat der Post-Install-Download von '$Package' nicht geklappt (Proxy/ignore-scripts pruefen). 'npm uninstall -g $Package' und neu installieren." } else { Write-Skip $Binary } return } if (-not (Test-CommandExists "npm")) { Write-Warn "npm wurde nicht gefunden - kann '$Package' nicht installieren." return } # -AllowScripts: fuer Pakete wie tree-sitter-cli, deren Post-Install-Skript # die eigentliche native Binary von GitHub nachlaedt. Die globale # ignore-scripts=true in der npmrc wuerde das verhindern; --no-ignore-scripts # hebt das nur fuer diesen einen Aufruf auf, ohne die npmrc anzufassen. $npmArgs = @("install", "-g", $Package) if ($AllowScripts) { $npmArgs += "--no-ignore-scripts" } Write-Step "npm $($npmArgs -join ' ')" npm @npmArgs | Out-Null if ($LASTEXITCODE -ne 0) { Write-Warn "npm install -g $Package fehlgeschlagen." return } if ($VerifyArgs -and -not (Test-BinaryRuns $Binary $VerifyArgs)) { Write-Warn "npm install -g $Package war erfolgreich, aber '$Binary' laesst sich nicht ausfuehren - vermutlich hat der Post-Install-Download der nativen Binary nicht geklappt (Proxy/ignore-scripts pruefen)." return } Write-Ok $Package } function Test-Prerequisites { if (-not (Test-CommandExists "winget")) { Write-Host "Fehler: winget wurde nicht gefunden. 'App Installer' aus dem Microsoft Store installieren." -ForegroundColor Red exit 1 } } function Update-SessionPath { # Registry-PATH-Aenderungen durch winget/npm wirken erst nach diesem Refresh. $machine = [Environment]::GetEnvironmentVariable("PATH", "Machine") $user = [Environment]::GetEnvironmentVariable("PATH", "User") $env:PATH = "$machine;$user" } function Install-Prerequisites { Install-WingetPackage -Binary "git" -Id "Git.Git" Install-WingetPackage -Binary "node" -Id "OpenJS.NodeJS.LTS" Update-SessionPath } function Install-Formatters { Install-WingetPackage -Binary "jq" -Id "jqlang.jq" Install-NpmPackage -Binary "prettier" -Package "prettier" Install-WingetPackage -Binary "ruff" -Id "astral-sh.ruff" Install-WingetPackage -Binary "shfmt" -Id "mvdan.shfmt" Install-WingetPackage -Binary "stylua" -Id "JohnnyMorganz.StyLua" # yamlfmt, gawk, xmllint: kein winget-Paket gefunden, siehe neovim-windows-setup.md. } function Install-Tools { Install-WingetPackage -Binary "rg" -Id "BurntSushi.ripgrep.MSVC" Install-WingetPackage -Binary "fd" -Id "sharkdp.fd" Install-NpmPackage -Binary "tree-sitter" -Package "tree-sitter-cli" -VerifyArgs @("--version") -AllowScripts # MSVC (cl.exe) ist bereits vorhanden, kein winget-Paket noetig - Neovim # dafuer aber aus einer "Developer PowerShell for VS" starten. Install-WingetPackage -Binary "make" -Id "ezwinports.make" } function Install-Lsp { Install-NpmPackage -Binary "basedpyright-langserver" -Package "basedpyright" Install-NpmPackage -Binary "bash-language-server" -Package "bash-language-server" Install-WingetPackage -Binary "clangd" -Id "LLVM.LLVM" Install-WingetPackage -Binary "shellcheck" -Id "koalaman.shellcheck" Install-NpmPackage -Binary "vscode-html-language-server" -Package "vscode-langservers-extracted" Install-NpmPackage -Binary "vscode-json-language-server" -Package "vscode-langservers-extracted" Install-WingetPackage -Binary "lua-language-server" -Id "LuaLS.lua-language-server" Install-NpmPackage -Binary "prisma-language-server" -Package "@prisma/language-server" Install-NpmPackage -Binary "vtsls" -Package "@vtsls/language-server" } function Main { Test-Prerequisites Install-Prerequisites Install-Formatters Install-Tools Install-Lsp Write-Host "" if ($script:Failures.Count -eq 0) { Write-Host "Neovim-Bootstrap abgeschlossen." -ForegroundColor Green } else { Write-Host "Neovim-Bootstrap abgeschlossen, mit $($script:Failures.Count) Warnung(en):" -ForegroundColor Yellow foreach ($f in $script:Failures) { Write-Host " - $f" -ForegroundColor Yellow } } } Main