refactor(installer): drop self-update, publish stable-named ClaudeDo.Installer.exe
Release workflow now names the installer asset ClaudeDo.Installer.exe (no version) for a permanent download URL; it is still uploaded and checksummed on every release. App + worker keep the git tag version. Removes the self-update preflight from App.OnStartup and deletes the now-dead SelfUpdater / SelfUpdatePromptWindow / SelfUpdateResult plus their tests. App-update detection is unaffected: the manifest records the release tag via DownloadAndExtractStep. Updates the installer CLAUDE.md.
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
namespace ClaudeDo.Releases;
|
||||
|
||||
public sealed record InstallerAssetMatch(ReleaseAsset Asset, string Version);
|
||||
|
||||
public enum SelfUpdateDecisionKind
|
||||
{
|
||||
NoUpdate,
|
||||
UpdateAvailable,
|
||||
}
|
||||
|
||||
public sealed record SelfUpdateDecision(
|
||||
SelfUpdateDecisionKind Kind,
|
||||
string? LatestVersion = null,
|
||||
ReleaseAsset? InstallerAsset = null,
|
||||
ReleaseAsset? ChecksumsAsset = null);
|
||||
@@ -1,126 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ClaudeDo.Releases;
|
||||
|
||||
public static partial class SelfUpdater
|
||||
{
|
||||
[GeneratedRegex(@"^ClaudeDo\.Installer-(?<version>[\d\.]+)\.exe$", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex InstallerAssetRegex();
|
||||
|
||||
public static InstallerAssetMatch? FindInstallerAsset(IEnumerable<ReleaseAsset> assets)
|
||||
{
|
||||
foreach (var asset in assets)
|
||||
{
|
||||
var m = InstallerAssetRegex().Match(asset.Name);
|
||||
if (m.Success)
|
||||
{
|
||||
return new InstallerAssetMatch(asset, m.Groups["version"].Value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task<SelfUpdateDecision> DecideUpdateAsync(
|
||||
IReleaseClient releases,
|
||||
string currentVersion,
|
||||
CancellationToken ct)
|
||||
{
|
||||
GiteaRelease? release;
|
||||
try
|
||||
{
|
||||
release = await releases.GetLatestReleaseAsync(ct);
|
||||
}
|
||||
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException)
|
||||
{
|
||||
return new SelfUpdateDecision(SelfUpdateDecisionKind.NoUpdate);
|
||||
}
|
||||
|
||||
if (release is null)
|
||||
return new SelfUpdateDecision(SelfUpdateDecisionKind.NoUpdate);
|
||||
|
||||
var match = FindInstallerAsset(release.Assets);
|
||||
if (match is null)
|
||||
return new SelfUpdateDecision(SelfUpdateDecisionKind.NoUpdate);
|
||||
|
||||
var cmp = VersionComparer.Compare(match.Version, currentVersion);
|
||||
if (!cmp.IsNewer)
|
||||
return new SelfUpdateDecision(SelfUpdateDecisionKind.NoUpdate);
|
||||
|
||||
var checksums = release.Assets.FirstOrDefault(
|
||||
a => string.Equals(a.Name, "checksums.txt", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return new SelfUpdateDecision(
|
||||
SelfUpdateDecisionKind.UpdateAvailable,
|
||||
LatestVersion: match.Version,
|
||||
InstallerAsset: match.Asset,
|
||||
ChecksumsAsset: checksums);
|
||||
}
|
||||
|
||||
public static async Task<bool> HandleReplaceSelfAsync(
|
||||
string oldPath,
|
||||
string currentExePath,
|
||||
Func<string, bool> launchProcess,
|
||||
int maxWaitMs = 5000)
|
||||
{
|
||||
var deadline = DateTime.UtcNow.AddMilliseconds(maxWaitMs);
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(oldPath))
|
||||
{
|
||||
File.Delete(oldPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(oldPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
File.Copy(currentExePath, oldPath, overwrite: false);
|
||||
return launchProcess(oldPath);
|
||||
}
|
||||
|
||||
public static async Task<string?> DownloadAndVerifyAsync(
|
||||
IReleaseClient releases,
|
||||
ReleaseAsset installerAsset,
|
||||
ReleaseAsset checksumsAsset,
|
||||
string tempDir,
|
||||
IProgress<long> progress,
|
||||
CancellationToken ct)
|
||||
{
|
||||
Directory.CreateDirectory(tempDir);
|
||||
var installerPath = Path.Combine(tempDir, installerAsset.Name);
|
||||
var checksumsPath = Path.Combine(tempDir, "checksums.txt");
|
||||
|
||||
try
|
||||
{
|
||||
await releases.DownloadAsync(installerAsset.BrowserDownloadUrl, installerPath, progress, ct);
|
||||
await releases.DownloadAsync(checksumsAsset.BrowserDownloadUrl, checksumsPath, new Progress<long>(_ => { }), ct);
|
||||
}
|
||||
catch (Exception ex) when (ex is HttpRequestException or IOException or TaskCanceledException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var checksumsText = await File.ReadAllTextAsync(checksumsPath, ct);
|
||||
var map = ChecksumVerifier.ParseChecksumsFile(checksumsText);
|
||||
if (!map.TryGetValue(installerAsset.Name, out var expected))
|
||||
return null;
|
||||
|
||||
return ChecksumVerifier.Verify(installerPath, expected) ? installerPath : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user