feat(installer): self-update pre-flight before wizard

This commit is contained in:
mika kuns
2026-04-23 14:49:48 +02:00
parent 98c188a5da
commit caf900b02d
3 changed files with 165 additions and 0 deletions

View File

@@ -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 "<old-path>"
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<long>(_ => { }),
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<InstallContext>();