feat(ui): add InstallerLocator
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.1" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.11" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
47
src/ClaudeDo.Ui/Services/InstallerLocator.cs
Normal file
47
src/ClaudeDo.Ui/Services/InstallerLocator.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
namespace ClaudeDo.Ui.Services;
|
||||||
|
|
||||||
|
public sealed class InstallerLocator
|
||||||
|
{
|
||||||
|
private const string InstallJson = "install.json";
|
||||||
|
private const string InstallerExe = "ClaudeDo.Installer.exe";
|
||||||
|
private const string UninstallerSubdir = "uninstaller";
|
||||||
|
|
||||||
|
public string? Find()
|
||||||
|
=> FindByWalkingUp(AppContext.BaseDirectory) ?? FindByRegistry();
|
||||||
|
|
||||||
|
public string? FindByWalkingUp(string startDir)
|
||||||
|
{
|
||||||
|
var dir = new DirectoryInfo(startDir);
|
||||||
|
while (dir is not null)
|
||||||
|
{
|
||||||
|
var manifest = Path.Combine(dir.FullName, InstallJson);
|
||||||
|
if (File.Exists(manifest))
|
||||||
|
{
|
||||||
|
var candidate = Path.Combine(dir.FullName, UninstallerSubdir, InstallerExe);
|
||||||
|
return File.Exists(candidate) ? candidate : null;
|
||||||
|
}
|
||||||
|
dir = dir.Parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
|
||||||
|
public string? FindByRegistry()
|
||||||
|
{
|
||||||
|
if (!OperatingSystem.IsWindows()) return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var key = Microsoft.Win32.Registry.LocalMachine
|
||||||
|
.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\ClaudeDo");
|
||||||
|
var location = key?.GetValue("InstallLocation") as string;
|
||||||
|
if (string.IsNullOrEmpty(location)) return null;
|
||||||
|
var candidate = Path.Combine(location, UninstallerSubdir, InstallerExe);
|
||||||
|
return File.Exists(candidate) ? candidate : null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
tests/ClaudeDo.Ui.Tests/Services/InstallerLocatorTests.cs
Normal file
58
tests/ClaudeDo.Ui.Tests/Services/InstallerLocatorTests.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using ClaudeDo.Ui.Services;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Ui.Tests.Services;
|
||||||
|
|
||||||
|
public class InstallerLocatorTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly string _root;
|
||||||
|
|
||||||
|
public InstallerLocatorTests()
|
||||||
|
{
|
||||||
|
_root = Path.Combine(Path.GetTempPath(), "ClaudeDo.Ui.Tests-" + Guid.NewGuid().ToString("N"));
|
||||||
|
Directory.CreateDirectory(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() { try { Directory.Delete(_root, true); } catch { } }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Find_WalkUpFromAppDir_ToInstallJsonSibling()
|
||||||
|
{
|
||||||
|
var installDir = Path.Combine(_root, "ClaudeDo");
|
||||||
|
var appDir = Path.Combine(installDir, "app");
|
||||||
|
var uninstallerDir = Path.Combine(installDir, "uninstaller");
|
||||||
|
Directory.CreateDirectory(appDir);
|
||||||
|
Directory.CreateDirectory(uninstallerDir);
|
||||||
|
|
||||||
|
File.WriteAllText(Path.Combine(installDir, "install.json"), "{}");
|
||||||
|
var installerPath = Path.Combine(uninstallerDir, "ClaudeDo.Installer.exe");
|
||||||
|
File.WriteAllText(installerPath, "x");
|
||||||
|
|
||||||
|
var locator = new InstallerLocator();
|
||||||
|
var found = locator.FindByWalkingUp(appDir);
|
||||||
|
|
||||||
|
Assert.Equal(installerPath, found);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Find_ReturnsNullWhenNoInstallJson()
|
||||||
|
{
|
||||||
|
var appDir = Path.Combine(_root, "somewhere", "app");
|
||||||
|
Directory.CreateDirectory(appDir);
|
||||||
|
|
||||||
|
var locator = new InstallerLocator();
|
||||||
|
Assert.Null(locator.FindByWalkingUp(appDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Find_ReturnsNullWhenInstallerMissingFromUninstallerDir()
|
||||||
|
{
|
||||||
|
var installDir = Path.Combine(_root, "ClaudeDo");
|
||||||
|
var appDir = Path.Combine(installDir, "app");
|
||||||
|
Directory.CreateDirectory(appDir);
|
||||||
|
Directory.CreateDirectory(Path.Combine(installDir, "uninstaller"));
|
||||||
|
File.WriteAllText(Path.Combine(installDir, "install.json"), "{}");
|
||||||
|
|
||||||
|
var locator = new InstallerLocator();
|
||||||
|
Assert.Null(locator.FindByWalkingUp(appDir));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user