feat(installer): add InstallManifest + json-backed store
This commit is contained in:
48
src/ClaudeDo.Installer/Core/InstallManifest.cs
Normal file
48
src/ClaudeDo.Installer/Core/InstallManifest.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Installer.Core;
|
||||||
|
|
||||||
|
public sealed record InstallManifest(
|
||||||
|
string Version,
|
||||||
|
string InstallDir,
|
||||||
|
string WorkerDir,
|
||||||
|
DateTimeOffset InstalledAt);
|
||||||
|
|
||||||
|
public static class InstallManifestStore
|
||||||
|
{
|
||||||
|
public const string FileName = "install.json";
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string ManifestPath(string installDir) => Path.Combine(installDir, FileName);
|
||||||
|
|
||||||
|
public static InstallManifest? TryRead(string installDir)
|
||||||
|
{
|
||||||
|
var path = ManifestPath(installDir);
|
||||||
|
if (!File.Exists(path)) return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(path);
|
||||||
|
return JsonSerializer.Deserialize<InstallManifest>(json, JsonOptions);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(string installDir, InstallManifest manifest)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(installDir);
|
||||||
|
var json = JsonSerializer.Serialize(manifest, JsonOptions);
|
||||||
|
File.WriteAllText(ManifestPath(installDir), json);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
tests/ClaudeDo.Installer.Tests/InstallManifestStoreTests.cs
Normal file
66
tests/ClaudeDo.Installer.Tests/InstallManifestStoreTests.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.IO;
|
||||||
|
using ClaudeDo.Installer.Core;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Installer.Tests;
|
||||||
|
|
||||||
|
public sealed class InstallManifestStoreTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly string _tempDir;
|
||||||
|
|
||||||
|
public InstallManifestStoreTests()
|
||||||
|
{
|
||||||
|
_tempDir = Path.Combine(Path.GetTempPath(), "ClaudeDoInstallerTests-" + Guid.NewGuid().ToString("N"));
|
||||||
|
Directory.CreateDirectory(_tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
try { Directory.Delete(_tempDir, recursive: true); } catch { /* best effort */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryRead_ReturnsNull_WhenFileMissing()
|
||||||
|
{
|
||||||
|
var result = InstallManifestStore.TryRead(_tempDir);
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Write_Then_Read_RoundTripsAllFields()
|
||||||
|
{
|
||||||
|
var manifest = new InstallManifest(
|
||||||
|
Version: "0.2.0",
|
||||||
|
InstallDir: _tempDir,
|
||||||
|
WorkerDir: Path.Combine(_tempDir, "worker"),
|
||||||
|
InstalledAt: new DateTimeOffset(2026, 4, 15, 12, 34, 56, TimeSpan.Zero));
|
||||||
|
|
||||||
|
InstallManifestStore.Write(_tempDir, manifest);
|
||||||
|
|
||||||
|
var round = InstallManifestStore.TryRead(_tempDir);
|
||||||
|
Assert.NotNull(round);
|
||||||
|
Assert.Equal("0.2.0", round!.Version);
|
||||||
|
Assert.Equal(manifest.InstallDir, round.InstallDir);
|
||||||
|
Assert.Equal(manifest.WorkerDir, round.WorkerDir);
|
||||||
|
Assert.Equal(manifest.InstalledAt, round.InstalledAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Write_CreatesInstallDir_IfMissing()
|
||||||
|
{
|
||||||
|
var nested = Path.Combine(_tempDir, "nested");
|
||||||
|
Assert.False(Directory.Exists(nested));
|
||||||
|
|
||||||
|
InstallManifestStore.Write(nested, new InstallManifest(
|
||||||
|
"0.0.1", nested, Path.Combine(nested, "worker"), DateTimeOffset.UtcNow));
|
||||||
|
|
||||||
|
Assert.True(File.Exists(Path.Combine(nested, "install.json")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryRead_ReturnsNull_WhenJsonMalformed()
|
||||||
|
{
|
||||||
|
File.WriteAllText(Path.Combine(_tempDir, "install.json"), "{ not json");
|
||||||
|
var result = InstallManifestStore.TryRead(_tempDir);
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user