• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ParadoxGameConverters / Fronter.NET / 15191184392

22 May 2025 03:52PM UTC coverage: 18.155% (-0.08%) from 18.23%
15191184392

Pull #881

github

web-flow
Merge f5b958086 into 8ffda492f
Pull Request #881: Show a notification when dowloading an update installer

73 of 654 branches covered (11.16%)

Branch coverage included in aggregate %.

0 of 19 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

551 of 2783 relevant lines covered (19.8%)

9.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.21
/Fronter.NET/Services/UpdateChecker.cs
1
using Avalonia;
2
using Avalonia.Controls;
3
using Avalonia.Layout;
4
using Avalonia.Media;
5
using Avalonia.Notification;
6
using commonItems;
7
using Fronter.Extensions;
8
using Fronter.Models;
9
using Fronter.Models.Configuration;
10
using Fronter.Views;
11
using log4net;
12
using System;
13
using System.Diagnostics;
14
using System.IO;
15
using System.Net.Http;
16
using System.Text;
17
using System.Text.Json;
18
using System.Threading.Tasks;
19

20
namespace Fronter.Services;
21

22
internal static class UpdateChecker {
23
        private static readonly ILog Logger = LogManager.GetLogger("Update checker");
1✔
24
        private static readonly HttpClient HttpClient = new() {Timeout = TimeSpan.FromMinutes(5)};
1✔
25
        public static async Task<bool> IsUpdateAvailable(string commitIdFilePath, string commitIdUrl) {
3✔
26
                if (!File.Exists(commitIdFilePath)) {
4✔
27
                        Logger.Debug($"File \"{commitIdFilePath}\" does not exist!");
1✔
28
                        return false;
1✔
29
                }
30

31
                try {
2✔
32
                        var response = await HttpClient.GetAsync(commitIdUrl);
2✔
33
                        if (!response.IsSuccessStatusCode) {
3✔
34
                                Logger.Warn($"Failed to get commit id from \"{commitIdUrl}\"; status code: {response.StatusCode}!");
1✔
35
                                return false;
1✔
36
                        }
37

38
                        var latestReleaseCommitId = await response.Content.ReadAsStringAsync();
1✔
39
                        latestReleaseCommitId = latestReleaseCommitId.Trim();
1✔
40

41
                        using var commitIdFileReader = new StreamReader(commitIdFilePath);
1✔
42
                        var localCommitId = (await commitIdFileReader.ReadLineAsync())?.Trim();
1!
43

44
                        return localCommitId is not null && !localCommitId.Equals(latestReleaseCommitId);
1!
45
                } catch (Exception e) {
×
46
                        Logger.Warn($"Failed to get commit id from \"{commitIdUrl}\"; {e}!");
×
47
                        return false;
×
48
                }
49
        }
3✔
50

51
        private static (string, string)? GetOSNameAndArch() {
1✔
52
                if (OperatingSystem.IsWindows()) {
1!
53
                        return ("win", "x64");
×
54
                }
55
                if (OperatingSystem.IsLinux()) {
2!
56
                        return ("linux", "x64");
1✔
57
                }
58
                if (OperatingSystem.IsMacOS()) {
×
59
                        return ("osx", "arm64");
×
60
                }
61
                return null;
×
62
        }
1✔
63

64
        public static async Task<UpdateInfoModel> GetLatestReleaseInfo(string converterName) {
1✔
65
                var osNameAndArch = GetOSNameAndArch();
1✔
66
                if (osNameAndArch is null) {
1!
67
                        return new UpdateInfoModel();
×
68
                }
69

70
                var osName = osNameAndArch.Value.Item1;
1✔
71
                var architecture = osNameAndArch.Value.Item2;
1✔
72

73
                var info = new UpdateInfoModel();
1✔
74
                var apiUrl = $"https://api.github.com/repos/ParadoxGameConverters/{converterName}/releases/latest";
1✔
75
                var requestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl);
1✔
76
                requestMessage.Headers.Add("User-Agent", "ParadoxGameConverters");
1✔
77

78
                HttpResponseMessage responseMessage;
79
                try {
1✔
80
                        responseMessage = await HttpClient.SendAsync(requestMessage);
1✔
81
                } catch (Exception e) {
1✔
82
                        Logger.Warn($"Failed to get release info from \"{apiUrl}\": {e}!");
×
83
                        return info;
×
84
                }
85
                await using var responseStream = await responseMessage.Content.ReadAsStreamAsync();
1✔
86

87
                var releaseInfo = await JsonSerializer.DeserializeAsync<ConverterReleaseInfo>(responseStream);
1✔
88
                if (releaseInfo is null) {
1!
89
                        return info;
×
90
                }
91

92
                info.Description = releaseInfo.Body;
1✔
93
                info.Version = releaseInfo.Name;
1✔
94

95
                DetermineReleaseBuildUrl(releaseInfo, info, osName, architecture);
1✔
96

97
                if (info.AssetUrl is null) {
1!
98
                        Logger.Debug($"Release {info.Version} doesn't have a release build for this platform.");
×
99
                }
×
100

101
                return info;
1✔
102
        }
1✔
103

104
        private static void DetermineReleaseBuildUrl(ConverterReleaseInfo releaseInfo, UpdateInfoModel info, string osName, string architecture) {
1✔
105
                var assets = releaseInfo.Assets;
1✔
106
                foreach (var asset in assets) {
5✔
107
                        string? assetName = asset.Name;
1✔
108

109
                        if (assetName is null) {
1!
110
                                continue;
×
111
                        }
112

113
                        assetName = assetName.ToLower();
1✔
114
                        var extension = CommonFunctions.GetExtension(assetName);
1✔
115
                        if (extension is not "zip" and not "tgz" and not "exe") {
1!
116
                                continue;
×
117
                        }
118

119
                        // For Windows, prefer an installer over an archive.
120
                        if (extension.Equals("exe") && osName.Equals("win")) {
1!
121
                                info.AssetUrl = asset.BrowserDownloadUrl;
×
122
                                break;
×
123
                        }
124

125
                        var assetNameWithoutExtension = CommonFunctions.TrimExtension(assetName);
1✔
126
                        if (!assetNameWithoutExtension.EndsWith($"-{osName}-{architecture}", StringComparison.OrdinalIgnoreCase)) {
1!
127
                                continue;
×
128
                        }
129

130
                        info.AssetUrl = asset.BrowserDownloadUrl;
1✔
131
                        break;
1✔
132
                }
133
        }
1✔
134

135
        public static string GetUpdateMessageBody(string baseBody, UpdateInfoModel updateInfo) {
×
136
                var stringBuilder = new StringBuilder(baseBody);
×
137
                stringBuilder.AppendLine();
×
138

139
                var version = updateInfo.Version;
×
140
                if (version is not null) {
×
141
                        stringBuilder.AppendLine();
×
142
                        stringBuilder.Append("Version: ");
×
143
                        stringBuilder.AppendLine(version);
×
144
                }
×
145

146
                var description = updateInfo.Description;
×
147
                if (description is not null) {
×
148
                        stringBuilder.AppendLine();
×
149
                        stringBuilder.AppendLine(description);
×
150
                }
×
151

152
                return stringBuilder.ToString();
×
153
        }
×
154

155
        private static async Task DownloadFileAsync(string installerUrl, string fileName) {
×
156
                var responseBytes = await HttpClient.GetByteArrayAsync(installerUrl);
×
157
                await File.WriteAllBytesAsync(fileName, responseBytes);
×
158
        }
×
159

160
        public static async Task RunInstallerAndDie(string installerUrl, Config config, INotificationMessageManager notificationManager) {
×
161
                Logger.Debug("Downloading installer...");
×
NEW
162
                var downloadingMessage = notificationManager.CreateMessage()
×
NEW
163
                        .Accent(Brushes.Gray)
×
NEW
164
                        .Background(Brushes.Gray)
×
NEW
165
                        .HasMessage("Downloading installer...")
×
NEW
166
                        .WithOverlay(new ProgressBar {
×
NEW
167
                                VerticalAlignment = VerticalAlignment.Bottom,
×
NEW
168
                                HorizontalAlignment = HorizontalAlignment.Stretch,
×
NEW
169
                                Height = 3,
×
NEW
170
                                BorderThickness = new Thickness(0),
×
NEW
171
                                Foreground = Brushes.Green,
×
NEW
172
                                Background = Brushes.Gray,
×
NEW
173
                                IsIndeterminate = true,
×
NEW
174
                                IsHitTestVisible = false
×
NEW
175
                        })
×
NEW
176
                        .Queue();
×
177

178
                var fileName = Path.GetTempFileName();
×
179
                try {
×
180
                        await DownloadFileAsync(installerUrl, fileName);
×
181
                } catch (Exception ex) {
×
182
                        Logger.Debug($"Failed to download installer: {ex.Message}");
×
183
                        notificationManager
×
184
                                .CreateError()
×
185
                                .HasMessage("Failed to download installer, probably because of network issues. \n" +
×
186
                                            "Try updating the converter manually.")
×
187
                                .SuggestManualUpdate(config)
×
188
                                .Queue();
×
189
                        return;
×
190
                }
191

NEW
192
                notificationManager.Dismiss(downloadingMessage);
×
193

194
                Logger.Debug("Running installer...");
×
195
                var proc = new Process();
×
196
                proc.StartInfo.FileName = fileName;
×
197
                try {
×
198
                        proc.Start();
×
199
                } catch (Exception ex) {
×
200
                        Logger.Debug($"Installer process failed to start: {ex.Message}");
×
201
                        notificationManager
×
202
                                .CreateError()
×
203
                                .HasMessage("Failed to start installer, probably because of an antivirus. \n" +
×
204
                                            "Try updating the converter manually.")
×
205
                                .SuggestManualUpdate(config)
×
206
                                .Queue();
×
207
                        return;
×
208
                }
209

210
                // Die. The installer will handle the rest.
211
                MainWindow.Instance.Close();
×
212
        }
×
213

214
        public static void StartUpdaterAndDie(string archiveUrl, string converterBackendDirName) {
×
215
                var updaterDirPath = Path.Combine(".", "Updater");
×
216
                var updaterRunningDirPath = Path.Combine(".", "Updater-running");
×
217

218
                const string manualUpdateHint = "Try updating the converter manually.";
219
                if (Directory.Exists(updaterRunningDirPath) && !SystemUtils.TryDeleteFolder(updaterRunningDirPath)) {
×
220
                        Logger.Warn($"Failed to delete Updater-running folder! {manualUpdateHint}");
×
221
                        return;
×
222
                }
223

224
                if (!SystemUtils.TryCopyFolder(updaterDirPath, updaterRunningDirPath)) {
×
225
                        Logger.Warn($"Failed to create Updater-running folder! {manualUpdateHint}");
×
226
                        return;
×
227
                }
228

229
                string updaterRunningPath = Path.Combine(updaterRunningDirPath, "updater");
×
230
                if (OperatingSystem.IsWindows()) {
×
231
                        updaterRunningPath += ".exe";
×
232
                }
×
233

234
                var proc = new Process();
×
235
                proc.StartInfo.FileName = updaterRunningPath;
×
236
                proc.StartInfo.Arguments = $"{archiveUrl} {converterBackendDirName}";
×
237
                try {
×
238
                        proc.Start();
×
239
                } catch (Exception ex) {
×
240
                        Logger.Debug($"Updater process failed to start: {ex.Message}");
×
241
                        Logger.Error($"Failed to start updater, probably because of an antivirus. {manualUpdateHint}");
×
242
                        return;
×
243
                }
244

245
                // Die. The updater will start Fronter after a successful update.
246
                MainWindow.Instance.Close();
×
247
        }
×
248
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc