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

KSP-CKAN / CKAN / 15833572481

23 Jun 2025 07:42PM UTC coverage: 42.239% (+0.1%) from 42.099%
15833572481

push

github

HebaruSan
Merge #4398 Exception handling revamp, parallel multi-host inflation

3882 of 9479 branches covered (40.95%)

Branch coverage included in aggregate %.

48 of 137 new or added lines in 30 files covered. (35.04%)

12 existing lines in 6 files now uncovered.

8334 of 19442 relevant lines covered (42.87%)

0.88 hits per line

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

0.0
/GUI/Main/MainRepo.cs
1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel;
4
using System.Timers;
5
using System.Threading;
6
using System.Linq;
7
using System.Windows.Forms;
8
using System.Transactions;
9
using Timer = System.Timers.Timer;
10
#if NET5_0_OR_GREATER
11
using System.Runtime.Versioning;
12
#endif
13

14
using Autofac;
15

16
using CKAN.Configuration;
17
using CKAN.Extensions;
18
using CKAN.GUI.Attributes;
19

20
// Don't warn if we use our own obsolete properties
21
#pragma warning disable 0618
22

23
namespace CKAN.GUI
24
{
25
    using RepoArgument = Tuple<bool, bool>;
26
    using RepoResult   = Tuple<RepositoryDataManager.UpdateResult, Dictionary<string, bool>, bool>;
27

28
    #if NET5_0_OR_GREATER
29
    [SupportedOSPlatform("windows")]
30
    #endif
31
    public partial class Main
32
    {
33
        public Timer? refreshTimer;
34

35
        public void UpdateRepo(bool forceFullRefresh = false, bool refreshWithoutChanges = false)
36
        {
×
37
            tabController.RenameTab(WaitTabPage.Name, Properties.Resources.MainRepoWaitTitle);
×
38

39
            try
40
            {
×
41
                Wait.StartWaiting(UpdateRepo, PostUpdateRepo, true,
×
42
                                  new RepoArgument(forceFullRefresh, refreshWithoutChanges));
43
            }
×
44
            catch (Exception exc)
×
45
            {
×
46
                log.Error("Failed to start repo update!", exc);
×
47
            }
×
48

49
            DisableMainWindow();
×
50

51
            Wait.SetDescription(Properties.Resources.MainRepoContacting);
×
52
            ShowWaitDialog();
×
53
        }
×
54

55
        [ForbidGUICalls]
56
        private void UpdateRepo(object? sender, DoWorkEventArgs? e)
57
        {
×
58
            if (e?.Argument is (bool forceFullRefresh, bool refreshWithoutChanges)
×
59
                && CurrentInstance != null)
60
            {
×
61
                // Don't repeat this stuff if downloads fail
62
                currentUser.RaiseMessage(Properties.Resources.MainRepoScanning);
×
63
                log.Debug("Scanning before repo update");
×
64
                var regMgr = RegistryManager.Instance(CurrentInstance, repoData);
×
65
                bool scanChanged = regMgr.ScanUnmanagedFiles();
×
66

67
                // Note the current mods' compatibility for the NewlyCompatible filter
68
                var registry = regMgr.registry;
×
69
                var stabilityTolerance = CurrentInstance.StabilityToleranceConfig;
×
70

71
                var cancelTokenSrc = new CancellationTokenSource();
×
72
                Wait.OnCancel += cancelTokenSrc.Cancel;
×
73

74
                // Load cached data with progress bars instead of without if not already loaded
75
                // (which happens if auto-update is enabled, otherwise this is a no-op).
76
                // We need the old data to alert the user of newly compatible modules after update.
77
                repoData.Prepopulate(
×
78
                    registry.Repositories.Values.ToList(),
79
                    new ProgressImmediate<int>(p => currentUser.RaiseProgress(Properties.Resources.LoadingCachedRepoData, p)));
×
80

81
                var versionCriteria = CurrentInstance.VersionCriteria();
×
82
                var oldModules = registry.CompatibleModules(stabilityTolerance, versionCriteria)
×
83
                                         .ToDictionary(m => m.identifier, m => false);
×
84
                registry.IncompatibleModules(stabilityTolerance, versionCriteria)
×
85
                        .Where(m => !oldModules.ContainsKey(m.identifier))
×
86
                        .ToList()
87
                        .ForEach(m => oldModules.Add(m.identifier, true));
×
88

89
                using (var transaction = CkanTransaction.CreateTransactionScope())
×
90
                {
×
91
                    // Only way out is to return or throw
92
                    while (true)
×
93
                    {
×
94
                        var repos = registry.Repositories.Values.ToArray();
×
95
                        try
96
                        {
×
97
                            var downloader = new NetAsyncDownloader(currentUser, () => null, userAgent);
×
98
                            downloader.TargetProgress += (target, remaining, total) =>
×
99
                            {
×
100
                                var repo = repos.FirstOrDefault(r => target.urls.Contains(r.uri));
×
101
                                if (repo != null && total > 0)
×
102
                                {
×
103
                                    Wait.SetProgress(repo.name, remaining, total);
×
104
                                }
×
105
                            };
×
106

107
                            currentUser.RaiseMessage(Properties.Resources.MainRepoUpdating);
×
108

109
                            var updateResult = repoData.Update(repos, CurrentInstance.game,
×
110
                                                               forceFullRefresh, downloader, currentUser, userAgent);
111

112
                            if (cancelTokenSrc.Token.IsCancellationRequested)
×
113
                            {
×
114
                                throw new CancelledActionKraken();
×
115
                            }
116

117
                            if (updateResult == RepositoryDataManager.UpdateResult.NoChanges && scanChanged)
×
118
                            {
×
119
                                updateResult = RepositoryDataManager.UpdateResult.Updated;
×
120
                            }
×
121
                            e.Result = new RepoResult(updateResult, oldModules, refreshWithoutChanges);
×
122

123
                            // If we make it to the end, we are done
124
                            transaction.Complete();
×
125
                            return;
×
126
                        }
127
                        catch (DownloadErrorsKraken k)
×
128
                        {
×
129
                            log.Debug("Caught download errors kraken");
×
130
                            DownloadsFailedDialog? dfd = null;
×
131
                            Util.Invoke(this, () =>
×
132
                            {
×
133
                                dfd = new DownloadsFailedDialog(
×
134
                                    Properties.Resources.RepoDownloadsFailedMessage,
135
                                    Properties.Resources.RepoDownloadsFailedColHdr,
136
                                    Properties.Resources.RepoDownloadsFailedAbortBtn,
137
                                    k.Exceptions.Select(kvp => new KeyValuePair<object[], Exception>(
×
138
                                        repos.Where(r => kvp.Key.urls.Contains(r.uri))
×
139
                                             .ToArray(),
140
                                        kvp.Value)),
141
                                    // Rows are only linked to themselves
142
                                    (r1, r2) => r1 == r2);
×
143
                                dfd.ShowDialog(this);
×
144
                            });
×
145
                            var skip  = dfd?.Wait()?.Select(r => r as Repository)
×
146
                                                    .OfType<Repository>()
147
                                                    .ToArray();
148
                            var abort = dfd?.Abort;
×
149
                            dfd?.Dispose();
×
150
                            if (abort ?? false)
×
151
                            {
×
152
                                e.Result = new RepoResult(RepositoryDataManager.UpdateResult.Failed,
×
153
                                                          oldModules, refreshWithoutChanges);
154
                                throw new CancelledActionKraken();
×
155
                            }
156
                            if (skip != null && skip.Length > 0)
×
157
                            {
×
158
                                foreach (var r in skip)
×
159
                                {
×
160
                                    registry.RepositoriesRemove(r.name);
×
161
                                    needRegistrySave = true;
×
162
                                }
×
163
                            }
×
164

165
                            // Loop back around to retry
166
                        }
×
167
                    }
×
168
                }
169
            }
170
        }
×
171

172
        private void PostUpdateRepo(object? sender, RunWorkerCompletedEventArgs? e)
173
        {
×
174
            if (e?.Error != null)
×
175
            {
×
176
                switch (e.Error)
×
177
                {
178
                    case CancelledActionKraken k:
179
                        EnableMainWindow();
×
180
                        HideWaitDialog();
×
181
                        break;
×
182

183
                    case AggregateException exc:
UNCOV
184
                        EnableMainWindow();
×
185
                        foreach (var inner in exc.InnerExceptions
×
186
                                                 .SelectMany(inner =>
187
                                                     inner.TraverseNodes(ex => ex.InnerException)
×
188
                                                          .Reverse()))
189
                        {
×
190
                            log.Error(inner.Message, inner);
×
191
                            currentUser.RaiseMessage("{0}", inner.Message);
×
192
                        }
×
193
                        currentUser.RaiseMessage(Properties.Resources.MainRepoFailed);
×
194
                        Wait.Finish();
×
195
                        break;
×
196

197
                    case Kraken kraken:
198
                        // Show nice message for known problems
NEW
199
                        log.Error(kraken.Message, kraken);
×
NEW
200
                        EnableMainWindow();
×
NEW
201
                        currentUser.RaiseMessage("{0}", kraken.Message);
×
NEW
202
                        currentUser.RaiseMessage(Properties.Resources.MainRepoFailed);
×
NEW
203
                        Wait.Finish();
×
NEW
204
                        break;
×
205

206
                    case Exception exc:
207
                        // Show stack trace for code problems
208
                        log.Error(exc.Message, exc);
×
209
                        EnableMainWindow();
×
NEW
210
                        currentUser.RaiseMessage("{0}", exc.ToString());
×
211
                        currentUser.RaiseMessage(Properties.Resources.MainRepoFailed);
×
212
                        Wait.Finish();
×
213
                        break;
×
214
                }
215
            }
×
216
            else if (e?.Result is (RepositoryDataManager.UpdateResult updateResult,
×
217
                                   Dictionary<string, bool>           oldModules,
218
                                   bool                               refreshWithoutChanges))
219
            {
×
220
                switch (updateResult)
×
221
                {
222
                    case RepositoryDataManager.UpdateResult.NoChanges:
223
                        currentUser.RaiseMessage(Properties.Resources.MainRepoUpToDate);
×
224
                        // Reload rows if user added a cached repo repo
225
                        if (refreshWithoutChanges)
×
226
                        {
×
227
                            RefreshModList(false, oldModules);
×
228
                        }
×
229
                        else
230
                        {
×
231
                            // Nothing changed, just go back
232
                            EnableMainWindow();
×
233
                            HideWaitDialog();
×
234
                            Util.Invoke(this, ManageMods.ModGrid.Select);
×
235
                        }
×
236
                        break;
×
237

238

239
                    case RepositoryDataManager.UpdateResult.OutdatedClient:
240
                        currentUser.RaiseMessage(Properties.Resources.MainRepoOutdatedClient);
×
241
                        if (CheckForCKANUpdate())
×
242
                        {
×
243
                            UpdateCKAN();
×
244
                        }
×
245
                        else
246
                        {
×
247
                            // No update available or user said no. Proceed as normal.
248
                            ShowRefreshQuestion();
×
249
                            UpgradeNotification();
×
250
                            RefreshModList(false, oldModules);
×
251
                        }
×
252
                        break;
×
253

254
                    case RepositoryDataManager.UpdateResult.Updated:
255
                    default:
256
                        currentUser.RaiseMessage(Properties.Resources.MainRepoSuccess);
×
257
                        ShowRefreshQuestion();
×
258
                        UpgradeNotification();
×
259
                        RefreshModList(false, oldModules);
×
260
                        break;
×
261
                }
262
            }
×
263
        }
×
264

265
        private void ShowRefreshQuestion()
266
        {
×
267
            if (configuration != null && !configuration.RefreshOnStartupNoNag)
×
268
            {
×
269
                configuration.RefreshOnStartupNoNag = true;
×
270
                if (!currentUser.RaiseYesNoDialog(Properties.Resources.MainRepoAutoRefreshPrompt))
×
271
                {
×
272
                    configuration.RefreshOnStartup = false;
×
273
                }
×
274
            }
×
275
        }
×
276

277
        public void InitRefreshTimer()
278
        {
×
279
            if (refreshTimer == null)
×
280
            {
×
281
                refreshTimer = new Timer
×
282
                {
283
                    AutoReset = true,
284
                    Enabled = true
285
                };
286
                refreshTimer.Elapsed += OnRefreshTimer;
×
287
            }
×
288
            UpdateRefreshTimer();
×
289
        }
×
290

291
        public void UpdateRefreshTimer()
292
        {
×
293
            refreshTimer?.Stop();
×
294
            IConfiguration cfg = ServiceLocator.Container.Resolve<IConfiguration>();
×
295

296
            // Interval is set to 1 minute * RefreshRate
297
            if (cfg.RefreshRate > 0 && refreshTimer != null)
×
298
            {
×
299
                refreshTimer.Interval = 1000 * 60 * cfg.RefreshRate;
×
300
                refreshTimer?.Start();
×
301
            }
×
302
        }
×
303

304
        private void OnRefreshTimer(object? sender, ElapsedEventArgs e)
305
        {
×
306
            if (MainMenu.Enabled && configuration != null && !configuration.RefreshPaused)
×
307
            {
×
308
                // Just a safety check
309
                UpdateRepo();
×
310
            }
×
311
        }
×
312

313
        private void UpgradeNotification()
314
        {
×
315
            int numUpgradeable = ManageMods.mainModList.Modules.Count(mod => mod.HasUpdate);
×
316
            if (numUpgradeable > 0)
×
317
            {
×
318
                Util.Invoke(this, () =>
×
319
                {
×
320
                    minimizeNotifyIcon.ShowBalloonTip(
×
321
                        10000,
322
                        string.Format(Properties.Resources.MainRepoBalloonTipDetails, numUpgradeable),
323
                        Properties.Resources.MainRepoBalloonTipTooltip,
324
                        ToolTipIcon.Info
325
                    );
326
                });
×
327
            }
×
328
        }
×
329
    }
330
}
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