From 548251841f6f21b356229577cecb0e15e44b2b9f Mon Sep 17 00:00:00 2001 From: Mika Kuns Date: Wed, 15 Apr 2026 09:48:29 +0200 Subject: [PATCH] feat(installer): add WriteInstallManifestStep Co-Authored-By: Claude Sonnet 4.6 --- .../Steps/WriteInstallManifestStep.cs | 25 +++++++++ .../WriteInstallManifestStepTests.cs | 51 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/ClaudeDo.Installer/Steps/WriteInstallManifestStep.cs create mode 100644 tests/ClaudeDo.Installer.Tests/WriteInstallManifestStepTests.cs diff --git a/src/ClaudeDo.Installer/Steps/WriteInstallManifestStep.cs b/src/ClaudeDo.Installer/Steps/WriteInstallManifestStep.cs new file mode 100644 index 0000000..32ae104 --- /dev/null +++ b/src/ClaudeDo.Installer/Steps/WriteInstallManifestStep.cs @@ -0,0 +1,25 @@ +using System.IO; +using ClaudeDo.Installer.Core; + +namespace ClaudeDo.Installer.Steps; + +public sealed class WriteInstallManifestStep : IInstallStep +{ + public string Name => "Write Install Manifest"; + + public Task ExecuteAsync(InstallContext ctx, IProgress progress, CancellationToken ct) + { + if (string.IsNullOrWhiteSpace(ctx.InstalledVersion)) + return Task.FromResult(StepResult.Fail("Installed version is not set — DownloadAndExtractStep must run first.")); + + var manifest = new InstallManifest( + Version: ctx.InstalledVersion!, + InstallDir: ctx.InstallDirectory, + WorkerDir: Path.Combine(ctx.InstallDirectory, "worker"), + InstalledAt: DateTimeOffset.UtcNow); + + InstallManifestStore.Write(ctx.InstallDirectory, manifest); + progress.Report($"Wrote {InstallManifestStore.ManifestPath(ctx.InstallDirectory)}"); + return Task.FromResult(StepResult.Ok()); + } +} diff --git a/tests/ClaudeDo.Installer.Tests/WriteInstallManifestStepTests.cs b/tests/ClaudeDo.Installer.Tests/WriteInstallManifestStepTests.cs new file mode 100644 index 0000000..3dc4044 --- /dev/null +++ b/tests/ClaudeDo.Installer.Tests/WriteInstallManifestStepTests.cs @@ -0,0 +1,51 @@ +using ClaudeDo.Installer.Core; +using ClaudeDo.Installer.Steps; + +namespace ClaudeDo.Installer.Tests; + +public sealed class WriteInstallManifestStepTests : IDisposable +{ + private readonly string _installDir; + + public WriteInstallManifestStepTests() + { + _installDir = Path.Combine(Path.GetTempPath(), "ClaudeDoWriteManifest-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_installDir); + } + + public void Dispose() + { + try { Directory.Delete(_installDir, recursive: true); } catch { } + } + + [Fact] + public async Task Writes_Manifest_WithAllFields() + { + var ctx = new InstallContext + { + InstallDirectory = _installDir, + InstalledVersion = "0.2.0", + }; + + var step = new WriteInstallManifestStep(); + var result = await step.ExecuteAsync(ctx, new Progress(_ => { }), CancellationToken.None); + + Assert.True(result.Success); + var manifest = InstallManifestStore.TryRead(_installDir); + Assert.NotNull(manifest); + Assert.Equal("0.2.0", manifest!.Version); + Assert.Equal(_installDir, manifest.InstallDir); + Assert.Equal(Path.Combine(_installDir, "worker"), manifest.WorkerDir); + } + + [Fact] + public async Task Fails_When_InstalledVersion_Missing() + { + var ctx = new InstallContext { InstallDirectory = _installDir }; // no version set + + var step = new WriteInstallManifestStep(); + var result = await step.ExecuteAsync(ctx, new Progress(_ => { }), CancellationToken.None); + + Assert.False(result.Success); + } +}