2 Commits

Author SHA1 Message Date
mika kuns
4148dcdb18 fix(installer): stop the running app before updating, not just the worker
All checks were successful
Release / release (push) Successful in 34s
A running ClaudeDo.App.exe locks the install\app directory, so the extract
step's Directory.Move failed with "Access to the path '...\app' is denied"
during an update. StopWorkerStep now also terminates app processes scoped to
the install dir (benefits uninstall too).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 13:26:47 +02:00
mika kuns
5783790733 fix(installer): keep step badges green and reset state on re-run
Step status and output lines arrive on two separate Progress<T> channels, so a
trailing "Running" line-message could be delivered after a step's terminal
Done/Failed and downgrade the badge back to orange. Guard against that
downgrade. Also reset each step's messages/status/expansion at the start of a
run so re-running no longer appends to the previous run's output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 13:22:36 +02:00
2 changed files with 31 additions and 11 deletions

View File

@@ -84,6 +84,15 @@ public partial class InstallPageViewModel : ObservableObject, IInstallerPage
{
if (IsInstalling) return;
// Reset per-step state so a re-run starts clean instead of appending
// output to the previous run's messages.
foreach (var s in Steps)
{
s.Messages.Clear();
s.Status = StepStatus.Pending;
s.IsExpanded = false;
}
IsInstalling = true;
IsComplete = false;
HasErrors = false;
@@ -96,6 +105,10 @@ public partial class InstallPageViewModel : ObservableObject, IInstallerPage
var step = Steps.FirstOrDefault(s => s.Name == p.StepName);
if (step is null) return;
// Status and output lines arrive on two separate Progress<T> channels, so a
// trailing "Running" line-message can be delivered after the step's terminal
// Done/Failed. Never let that downgrade a completed step back to Running.
if (!(step.Status is StepStatus.Done or StepStatus.Failed && p.Status is StepStatus.Running))
step.Status = p.Status;
if (p.Message is not null)
{

View File

@@ -7,15 +7,21 @@ namespace ClaudeDo.Installer.Steps;
public sealed class StopWorkerStep : IInstallStep
{
public const string LegacyTaskName = "ClaudeDoWorker";
public const string ProcessName = "ClaudeDo.Worker";
// Both must be stopped before the install dir is touched: a running app/worker
// exe locks its directory, so Directory.Move during extraction would otherwise
// fail with "Access to the path '...\app' is denied".
private static readonly string[] ProcessNames = { "ClaudeDo.Worker", "ClaudeDo.App" };
public string Name => "Stop Worker";
public async Task<StepResult> ExecuteAsync(InstallContext ctx, IProgress<string> progress, CancellationToken ct)
{
progress.Report("Stopping worker process (if running)...");
progress.Report("Stopping ClaudeDo processes (if running)...");
var installDir = ctx.InstallDirectory;
foreach (var p in Process.GetProcessesByName(ProcessName))
foreach (var name in ProcessNames)
{
foreach (var p in Process.GetProcessesByName(name))
{
try
{
@@ -27,6 +33,7 @@ public sealed class StopWorkerStep : IInstallStep
catch { /* process may have exited or be inaccessible */ }
finally { p.Dispose(); }
}
}
await Task.CompletedTask;
return StepResult.Ok();
}