feat(installer): harden database init and service setup steps

- InitDatabaseStep: create DbPath parent directory so custom paths work
- RegisterServiceStep: pass obj= argument so ServiceAccount is honoured
- StartServiceStep: poll for RUNNING state so downstream steps don't race
This commit is contained in:
mika kuns
2026-04-23 13:07:16 +02:00
parent cc01871407
commit 31218fc205
3 changed files with 30 additions and 6 deletions

View File

@@ -13,15 +13,26 @@ public sealed class StartServiceStep : IInstallStep
progress.Report($"Starting {ServiceName}...");
var (exit, _) = await ProcessRunner.RunAsync("sc.exe", $"start {ServiceName}", null, progress, ct);
if (exit == 0) return StepResult.Ok();
// 1056 = ERROR_SERVICE_ALREADY_RUNNING — fine, fall through to the readiness poll.
if (exit != 0 && exit != 1056)
return StepResult.Fail($"sc.exe start failed with exit code {exit}");
// Exit 1056 = ERROR_SERVICE_ALREADY_RUNNING — that's fine too.
if (exit == 1056)
{
progress.Report("Service was already running.");
return StepResult.Ok();
// sc.exe start returns as soon as SCM accepts the command. Poll until the
// service actually reports RUNNING so downstream steps and SignalR clients
// don't race the worker's startup.
progress.Report("Waiting for service to reach RUNNING state...");
for (var i = 0; i < 30; i++)
{
ct.ThrowIfCancellationRequested();
var (q, output) = await ProcessRunner.RunAsync("sc.exe", $"query {ServiceName}", null, progress, ct);
if (q == 0 && output.Contains("RUNNING", StringComparison.OrdinalIgnoreCase))
return StepResult.Ok();
await Task.Delay(1000, ct);
}
return StepResult.Fail($"sc.exe start failed with exit code {exit}");
return StepResult.Fail("Service did not reach RUNNING state within 30 seconds.");
}
}