Repository navigation
Expand file tree
/
Copy pathUpdateService.cs
More file actions
136 lines (114 loc) · 4.88 KB
/
Copy pathUpdateService.cs
File metadata and controls
136 lines (114 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
namespace NoteUI;
public static class UpdateService
{
public const string CurrentVersion = "0.5.0";
private const string GitHubOwner = "devohmycode";
private const string GitHubRepo = "NoteUI";
private static readonly HttpClient Http = CreateHttpClient();
public record UpdateInfo(string Version, string DownloadUrl, string ReleaseUrl, string Body);
/// <summary>Detects whether this installation includes CUDA libraries.</summary>
public static bool IsCudaBuild()
{
var cudaDir = Path.Combine(AppContext.BaseDirectory, "runtimes", "win-x64", "native", "cuda12");
return Directory.Exists(cudaDir);
}
private static HttpClient CreateHttpClient()
{
var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("NoteUI", CurrentVersion));
client.Timeout = TimeSpan.FromSeconds(15);
return client;
}
/// <summary>Checks GitHub for a newer release. Returns null if up to date or on error.</summary>
public static async Task<UpdateInfo?> CheckForUpdateAsync()
{
try
{
var url = $"https://api.github.com/repos/{GitHubOwner}/{GitHubRepo}/releases/latest";
var response = await Http.GetAsync(url);
if (!response.IsSuccessStatusCode)
return null;
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
var tagName = root.GetProperty("tag_name").GetString() ?? "";
var remoteVersion = tagName.TrimStart('v', 'V');
if (!IsNewer(remoteVersion, CurrentVersion))
return null;
// Pick the right installer asset based on current build variant
var expectedAsset = IsCudaBuild() ? "NoteUI-Setup-CUDA.exe" : "NoteUI-Setup-CPU.exe";
string? downloadUrl = null;
if (root.TryGetProperty("assets", out var assets))
{
foreach (var asset in assets.EnumerateArray())
{
var name = asset.GetProperty("name").GetString() ?? "";
if (name.Equals(expectedAsset, StringComparison.OrdinalIgnoreCase))
{
downloadUrl = asset.GetProperty("browser_download_url").GetString();
break;
}
}
}
var releaseUrl = root.GetProperty("html_url").GetString() ?? "";
var body = root.TryGetProperty("body", out var bodyProp) ? bodyProp.GetString() ?? "" : "";
return new UpdateInfo(remoteVersion, downloadUrl ?? "", releaseUrl, body);
}
catch
{
return null;
}
}
/// <summary>Downloads the installer to a temp file and returns the path.</summary>
public static async Task<string?> DownloadInstallerAsync(string downloadUrl, IProgress<double>? progress = null)
{
try
{
using var response = await Http.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
if (!response.IsSuccessStatusCode)
return null;
var totalBytes = response.Content.Headers.ContentLength ?? -1;
var tempPath = Path.Combine(Path.GetTempPath(), $"NoteUI-Setup-{Guid.NewGuid():N}.exe");
await using var contentStream = await response.Content.ReadAsStreamAsync();
await using var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
var buffer = new byte[81920];
long totalRead = 0;
int bytesRead;
while ((bytesRead = await contentStream.ReadAsync(buffer)) > 0)
{
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead));
totalRead += bytesRead;
if (totalBytes > 0)
progress?.Report((double)totalRead / totalBytes);
}
progress?.Report(1.0);
return tempPath;
}
catch
{
return null;
}
}
/// <summary>Launches the downloaded installer with /SILENT and exits the app.</summary>
public static void LaunchInstallerAndExit(string installerPath)
{
Process.Start(new ProcessStartInfo
{
FileName = installerPath,
Arguments = "/SILENT",
UseShellExecute = true
});
Environment.Exit(0);
}
/// <summary>Returns true if remote version is strictly newer than local.</summary>
private static bool IsNewer(string remote, string local)
{
if (Version.TryParse(remote, out var r) && Version.TryParse(local, out var l))
return r > l;
return false;
}
}