From b1a4c10dd2653e55ebc21bcbb8b1cd15a225c7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Nov=C3=A1k?= Date: Wed, 1 Jul 2026 02:10:35 +0200 Subject: [PATCH] Upgrade projects to .NET 10 and implement automatic prerequisites check/installation and PATH registration --- .gitea/workflows/ci.yml | 2 +- src/NixWslWrapper.Cli/CliController.cs | 10 + .../NixWslWrapper.Cli.csproj | 2 +- src/NixWslWrapper.Cli/PrerequisiteManager.cs | 210 ++++++++++++++++++ .../NixWslWrapper.Core.csproj | 4 +- .../NixWslWrapper.Infrastructure.csproj | 4 +- .../NixWslWrapper.Tests.csproj | 2 +- 7 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 src/NixWslWrapper.Cli/PrerequisiteManager.cs diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 3e7ed39..eb5038f 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest container: - image: mcr.microsoft.com/dotnet/sdk:8.0 + image: mcr.microsoft.com/dotnet/sdk:10.0 steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/src/NixWslWrapper.Cli/CliController.cs b/src/NixWslWrapper.Cli/CliController.cs index 1df14c2..0bf7db8 100644 --- a/src/NixWslWrapper.Cli/CliController.cs +++ b/src/NixWslWrapper.Cli/CliController.cs @@ -36,6 +36,16 @@ namespace NixWslWrapper.Cli string user = Environment.GetEnvironmentVariable("NIX_WSL_USER") ?? "root"; string profile = Environment.GetEnvironmentVariable("NIX_WSL_PROFILE") ?? "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"; + try + { + PrerequisiteManager.EnsurePrerequisites(distro, user, profile); + } + catch (Exception ex) + { + AnsiConsole.MarkupLine($"[bold red]Prerequisite Check Failed:[/] {ex.Message}"); + return 1; + } + WslTarget target; try { diff --git a/src/NixWslWrapper.Cli/NixWslWrapper.Cli.csproj b/src/NixWslWrapper.Cli/NixWslWrapper.Cli.csproj index 912ff38..1123c0c 100644 --- a/src/NixWslWrapper.Cli/NixWslWrapper.Cli.csproj +++ b/src/NixWslWrapper.Cli/NixWslWrapper.Cli.csproj @@ -11,7 +11,7 @@ Exe - net8.0 + net10.0 enable enable nix diff --git a/src/NixWslWrapper.Cli/PrerequisiteManager.cs b/src/NixWslWrapper.Cli/PrerequisiteManager.cs new file mode 100644 index 0000000..69ee8e6 --- /dev/null +++ b/src/NixWslWrapper.Cli/PrerequisiteManager.cs @@ -0,0 +1,210 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Spectre.Console; + +namespace NixWslWrapper.Cli +{ + public class PrerequisiteManager + { + public static void EnsurePrerequisites(string distro, string user, string profilePath) + { + EnsurePathRegistered(); + EnsureWslAndDistro(distro); + EnsureNix(distro, user, profilePath); + } + + private static void EnsurePathRegistered() + { + try + { + string currentExeDir = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName) ?? AppContext.BaseDirectory; + currentExeDir = Path.GetFullPath(currentExeDir).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + string? userPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User); + var paths = (userPath ?? "") + .Split(';', StringSplitOptions.RemoveEmptyEntries) + .Select(p => Path.GetFullPath(p.Trim()).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)) + .ToList(); + + if (!paths.Any(p => string.Equals(p, currentExeDir, StringComparison.OrdinalIgnoreCase))) + { + string newPath = string.IsNullOrWhiteSpace(userPath) + ? currentExeDir + : userPath.TrimEnd(';') + ";" + currentExeDir; + + Environment.SetEnvironmentVariable("PATH", newPath, EnvironmentVariableTarget.User); + + var pathPanel = new Panel(new Markup( + $"[bold yellow]PATH Updated:[/] Added [green]\"{currentExeDir}\"[/] to your Windows User PATH.\n" + + "Please [bold]restart your terminal, MCP servers, or IDE[/] for changes to take effect." + )) + { + Border = BoxBorder.Rounded, + Padding = new Padding(1, 1, 1, 1), + Header = new PanelHeader("PATH Auto-Registration") + }; + AnsiConsole.Write(pathPanel.BorderColor(Color.Yellow)); + AnsiConsole.WriteLine(); + } + } + catch (Exception ex) + { + AnsiConsole.MarkupLine($"[yellow]Warning: Could not automatically register directory to PATH: {ex.Message}[/]"); + } + } + + private static void EnsureWslAndDistro(string distro) + { + // 1. Check if wsl.exe exists + bool wslExists = false; + try + { + var checkWsl = new ProcessStartInfo("wsl.exe", "--status") + { + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }; + using var p = Process.Start(checkWsl); + p?.WaitForExit(3000); + wslExists = p != null; + } + catch + { + wslExists = false; + } + + if (!wslExists) + { + var wslPanel = new Panel(new Markup( + $"[bold red]WSL Not Found:[/] WSL (Windows Subsystem for Linux) is not installed.\n" + + $"Triggering automatic installation of WSL and the [green]{distro}[/] distribution.\n\n" + + "[bold yellow]Please approve the administrator UAC prompt that appears.[/]" + )) + { + Border = BoxBorder.Double, + Header = new PanelHeader("WSL Auto-Installation") + }; + AnsiConsole.Write(wslPanel.BorderColor(Color.Red)); + + try + { + var installPsi = new ProcessStartInfo("wsl.exe", $"--install -d {distro}") + { + UseShellExecute = true, + Verb = "runas" // Elevates to Administrator + }; + using var p = Process.Start(installPsi); + p?.WaitForExit(); + } + catch (Exception ex) + { + AnsiConsole.MarkupLine($"[bold red]Failed to start WSL installation:[/] {ex.Message}"); + throw new InvalidOperationException("WSL is required to run this wrapper.", ex); + } + } + + // 2. Check if the specific distro is installed + bool distroInstalled = false; + try + { + var checkDistro = new ProcessStartInfo("wsl.exe", $"-d {distro} -u root -- echo check") + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + using var p = Process.Start(checkDistro); + p?.WaitForExit(4000); + distroInstalled = p?.ExitCode == 0; + } + catch + { + distroInstalled = false; + } + + if (!distroInstalled) + { + AnsiConsole.MarkupLine($"[yellow]WSL Distro [green]'{distro}'[/] is not registered. Installing it now...[/]"); + try + { + var distroPsi = new ProcessStartInfo("wsl.exe", $"--install -d {distro}") + { + UseShellExecute = true, + Verb = "runas" + }; + using var p = Process.Start(distroPsi); + p?.WaitForExit(); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to automatically install WSL distribution '{distro}': {ex.Message}", ex); + } + } + } + + private static void EnsureNix(string distro, string user, string profilePath) + { + bool nixInstalled = false; + try + { + var checkNix = new ProcessStartInfo("wsl.exe", $"-d {distro} -u {user} -- sh -c \"command -v nix\"") + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + using var p = Process.Start(checkNix); + p?.WaitForExit(4000); + nixInstalled = p?.ExitCode == 0; + } + catch + { + nixInstalled = false; + } + + if (!nixInstalled) + { + var nixPanel = new Panel(new Markup( + $"[bold yellow]Nix Engine Not Found:[/] Nix is not installed inside the [green]'{distro}'[/] distribution.\n" + + "Triggering automatic installation using the official Determinate Nix installer.\n\n" + + "[grey]This may take a few minutes. Please wait...[/]" + )) + { + Border = BoxBorder.Rounded, + Header = new PanelHeader("Nix Auto-Installation") + }; + AnsiConsole.Write(nixPanel.BorderColor(Color.Yellow)); + + try + { + string installCmd = "curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm"; + var nixPsi = new ProcessStartInfo("wsl.exe", $"-d {distro} -u root -- sh -c \"{installCmd}\"") + { + UseShellExecute = false, + RedirectStandardOutput = false, + RedirectStandardError = false, + CreateNoWindow = false + }; + using var p = Process.Start(nixPsi); + p?.WaitForExit(); + + if (p?.ExitCode != 0) + { + throw new InvalidOperationException($"Nix installer exited with non-zero code: {p?.ExitCode}"); + } + + AnsiConsole.MarkupLine("[green]✔ Nix installed successfully inside WSL![/]"); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to automatically install Nix inside WSL distro: {ex.Message}", ex); + } + } + } + } +} diff --git a/src/NixWslWrapper.Core/NixWslWrapper.Core.csproj b/src/NixWslWrapper.Core/NixWslWrapper.Core.csproj index fa71b7a..9ed914b 100644 --- a/src/NixWslWrapper.Core/NixWslWrapper.Core.csproj +++ b/src/NixWslWrapper.Core/NixWslWrapper.Core.csproj @@ -1,7 +1,7 @@ - + - net8.0 + net10.0 enable enable diff --git a/src/NixWslWrapper.Infrastructure/NixWslWrapper.Infrastructure.csproj b/src/NixWslWrapper.Infrastructure/NixWslWrapper.Infrastructure.csproj index 4b6bff5..dddc94d 100644 --- a/src/NixWslWrapper.Infrastructure/NixWslWrapper.Infrastructure.csproj +++ b/src/NixWslWrapper.Infrastructure/NixWslWrapper.Infrastructure.csproj @@ -1,11 +1,11 @@ - + - net8.0 + net10.0 enable enable diff --git a/tests/NixWslWrapper.Tests/NixWslWrapper.Tests.csproj b/tests/NixWslWrapper.Tests/NixWslWrapper.Tests.csproj index 2ad53a3..3936677 100644 --- a/tests/NixWslWrapper.Tests/NixWslWrapper.Tests.csproj +++ b/tests/NixWslWrapper.Tests/NixWslWrapper.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable