SOLID refactoring: Abstract prerequisite checks into IPrerequisiteVerifier to bypass physical OS checks and fix Gitea CI/CD test run
Build and Test / build (push) Successful in 1m14s

This commit is contained in:
Marek Novák
2026-07-02 00:02:37 +02:00
parent dd7f1cdaa4
commit 8784ba7b57
7 changed files with 29 additions and 11 deletions
@@ -4,6 +4,10 @@
<ProjectReference Include="..\NixWslWrapper.Core\NixWslWrapper.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Spectre.Console" Version="0.57.1" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
@@ -0,0 +1,211 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using NixWslWrapper.Core.Interfaces;
using Spectre.Console;
namespace NixWslWrapper.Infrastructure.Services
{
public class WslPrerequisiteVerifier : IPrerequisiteVerifier
{
public void EnsurePrerequisites(string distro, string user, string profilePath)
{
EnsurePathRegistered();
EnsureWslAndDistro(distro);
EnsureNix(distro, user, profilePath);
}
private 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 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 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);
}
}
}
}
}