From caf900b02dff3d441dc61f96467046add910f36f Mon Sep 17 00:00:00 2001 From: mika kuns Date: Thu, 23 Apr 2026 14:49:48 +0200 Subject: [PATCH] feat(installer): self-update pre-flight before wizard --- src/ClaudeDo.Installer/App.xaml.cs | 98 +++++++++++++++++++ .../Views/SelfUpdatePromptWindow.xaml | 25 +++++ .../Views/SelfUpdatePromptWindow.xaml.cs | 42 ++++++++ 3 files changed, 165 insertions(+) create mode 100644 src/ClaudeDo.Installer/Views/SelfUpdatePromptWindow.xaml create mode 100644 src/ClaudeDo.Installer/Views/SelfUpdatePromptWindow.xaml.cs diff --git a/src/ClaudeDo.Installer/App.xaml.cs b/src/ClaudeDo.Installer/App.xaml.cs index bb06f73..33e0645 100644 --- a/src/ClaudeDo.Installer/App.xaml.cs +++ b/src/ClaudeDo.Installer/App.xaml.cs @@ -22,6 +22,104 @@ public partial class App : Application { base.OnStartup(e); + // --- Self-update pre-flight --- + // Resolve current exe path. Assembly.Location may point to a .dll for apphost-based + // .NET apps; swap to the .exe companion when that happens. + var currentExePath = Assembly.GetEntryAssembly()!.Location; + if (currentExePath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + currentExePath = System.IO.Path.ChangeExtension(currentExePath, ".exe"); + } + + // Arg form: --replace-self "" + var replaceSelfIndex = Array.FindIndex(e.Args, a => a.Equals("--replace-self", StringComparison.OrdinalIgnoreCase)); + if (replaceSelfIndex >= 0 && replaceSelfIndex + 1 < e.Args.Length) + { + var oldPath = e.Args[replaceSelfIndex + 1]; + var relaunched = await SelfUpdater.HandleReplaceSelfAsync( + oldPath: oldPath, + currentExePath: currentExePath, + launchProcess: path => + { + try + { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(path) { UseShellExecute = true }); + return true; + } + catch { return false; } + }); + if (relaunched) + { + Shutdown(0); + return; + } + // Replacement failed — fall through to normal wizard from the temp location. + } + else + { + // Normal launch: check for a newer installer. + using var selfUpdateHttp = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; + var selfUpdateReleases = new ReleaseClient(selfUpdateHttp); + var currentVersion = GetInstallerVersion(); + + var decision = await SelfUpdater.DecideUpdateAsync(selfUpdateReleases, currentVersion, CancellationToken.None); + if (decision.Kind == SelfUpdateDecisionKind.UpdateAvailable) + { + var prompt = new SelfUpdatePromptWindow(currentVersion, decision.LatestVersion!); + DarkTitleBar.Apply(prompt); + var ok = prompt.ShowDialog() == true; + if (!ok) + { + Shutdown(0); + return; + } + if (prompt.Choice == SelfUpdateChoice.Update) + { + prompt.ShowProgress("Downloading..."); + var tempDir = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "ClaudeDo.Installer.Update"); + var verifiedPath = await SelfUpdater.DownloadAndVerifyAsync( + selfUpdateReleases, + decision.InstallerAsset!, + decision.ChecksumsAsset!, + tempDir, + new Progress(_ => { }), + CancellationToken.None); + + if (verifiedPath is null) + { + MessageBox.Show(prompt, + "Update download or verification failed. Continuing with current installer.", + "ClaudeDo Installer", MessageBoxButton.OK, MessageBoxImage.Warning); + } + else + { + try + { + var psi = new System.Diagnostics.ProcessStartInfo(verifiedPath) + { + UseShellExecute = true, + }; + psi.ArgumentList.Add("--replace-self"); + psi.ArgumentList.Add(currentExePath); + System.Diagnostics.Process.Start(psi); + Shutdown(0); + return; + } + catch (Exception ex) + { + MessageBox.Show(prompt, + "Failed to launch updated installer: " + ex.Message + "\nContinuing with current installer.", + "ClaudeDo Installer", MessageBoxButton.OK, MessageBoxImage.Warning); + } + } + } + // SelfUpdateChoice.Continue — fall through to normal wizard. + } + // No-update or check failed — fall through to normal wizard. + } + + // --- Existing wizard start-up unchanged below this line --- + _services = BuildServices(); var context = _services.GetRequiredService(); diff --git a/src/ClaudeDo.Installer/Views/SelfUpdatePromptWindow.xaml b/src/ClaudeDo.Installer/Views/SelfUpdatePromptWindow.xaml new file mode 100644 index 0000000..e9f9aa4 --- /dev/null +++ b/src/ClaudeDo.Installer/Views/SelfUpdatePromptWindow.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + +