Skip to content

Commit 98df906

Browse files
authored
refactor: remove dead code and fix bugs in GeneralUpdate.Core (#405)
* Remove AOT conditional exclusions for SignalR and delete deprecated methods - Remove #define AOT constant and SignalR Compile Remove entries from csproj - Remove Condition on SignalR.Client PackageReference for AOT builds - Remove #if !AOT blocks in GeneralUpdateBootstrap.cs (HubDownloadSource + LaunchSilentAsync) - Delete deprecated abstract/override methods (ExecuteStrategy, ExecuteStrategyAsync, StrategyFactory) Closes #402 * Fix HubDownloadSource AOT support and Hub start silent failure - Add PacketDTO to HttpParameterJsonContext for source-generated AOT deserialization - Replace reflection-based Deserialize<PacketDTO> with source-gen version in HubDownloadSource - Remove try/catch in UpgradeHubService.StartAsync so hub connection failures propagate * refactor: remove dead code and fix bugs in GeneralUpdate.Core - Fix WindowsStrategy/MacStrategy Execute() missing or broken overrides - Fix DownloadProgressReporter all-completed event firing prematurely - Fix StorageManager GetAllfiles null return causing NRE - Fix FileNode GetHashCode violating Equals contract - Fix ObjectTranslator unsafe cast, IsComplated typo - Delete unused FileTreeCore duplicate types, IEventManager interface - Remove dead fields: _diffMode, _script, _compressionStrategy, _hooks/_reporter - Remove empty InfixOrder methods, duplicate DriverDirectory, dead ctor - Extract shared StreamDownloadAsync to eliminate download loop duplication - Wire _customSkipOption into bootstrap execution flow - Clean stale XML doc params Closes #404 * fix: restore and wire hooks/reporter into SilentPollOrchestrator Previously hooks and reporter were injected into SilentPollOrchestrator but never actually used. This commit completes the integration: - Hooks.OnBeforeUpdateAsync() called before download (allows cancellation) - Hooks.OnDownloadCompletedAsync() called after successful download - Hooks.OnAfterUpdateAsync() called after pipeline completes - Hooks.OnUpdateErrorAsync() called on download or pipeline failures - Reporter reports: UpdateStarted, DownloadCompleted, UpdateApplied, UpdateFailed - Hooks & reporter calls are wrapped in try/catch so they never break the flow Closes #404 * fix: address code review feedback - Fix MutiDownloadCompletedEventArgs.IsCompleted PascalCase (was isCompleted) - Fix EventListenerTests to use IsCompleted instead of IsComplated - Fix DispatchAllCompleted to accept sender param (was passing null) - Fix DefaultDownloadOrchestrator to filter only failed results for FailedVersions - Fix FileNode.GetHashCode to use OrdinalIgnoreCase (match Equals contract)
1 parent 06e8f47 commit 98df906

23 files changed

Lines changed: 255 additions & 290 deletions

src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.IO;
@@ -126,6 +126,14 @@ private async Task<GeneralUpdateBootstrap> LaunchWithStrategy(IStrategy roleStra
126126
}
127127

128128
roleStrategy.Create(_configInfo);
129+
130+
// Check custom skip condition before executing update
131+
if (_customSkipOption?.Invoke() == true)
132+
{
133+
GeneralTracer.Info("GeneralUpdateBootstrap: update skipped by custom skip option.");
134+
return this;
135+
}
136+
129137
await roleStrategy.ExecuteAsync();
130138
}
131139
catch (Exception ex)

src/c#/GeneralUpdate.Core/Compress/CompressProvider.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,18 @@ namespace GeneralUpdate.Core.Compress;
66

77
public class CompressProvider
88
{
9-
private static ICompressionStrategy _compressionStrategy;
10-
119
private CompressProvider() { }
1210

13-
public static void Compress(string compressType,string sourcePath, string destinationPath, bool includeRootDirectory, Encoding encoding)
11+
public static void Compress(string compressType, string sourcePath, string destinationPath, bool includeRootDirectory, Encoding encoding)
1412
{
15-
_compressionStrategy = GetCompressionStrategy(compressType);
16-
_compressionStrategy.Compress(sourcePath, destinationPath, includeRootDirectory, encoding);
13+
var strategy = GetCompressionStrategy(compressType);
14+
strategy.Compress(sourcePath, destinationPath, includeRootDirectory, encoding);
1715
}
1816

1917
public static void Decompress(string compressType, string archivePath, string destinationPath, Encoding encoding)
2018
{
21-
_compressionStrategy = GetCompressionStrategy(compressType);
22-
_compressionStrategy.Decompress(archivePath, destinationPath, encoding);
19+
var strategy = GetCompressionStrategy(compressType);
20+
strategy.Decompress(archivePath, destinationPath, encoding);
2321
}
2422

2523
private static ICompressionStrategy GetCompressionStrategy(string compressType) => compressType switch

src/c#/GeneralUpdate.Core/Configuration/ConfiginfoBuilder.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public class ConfiginfoBuilder
2929
private string _updateLogUrl;
3030
private string _reportUrl;
3131
private string _bowl;
32-
private string _script;
3332
private string _driverDirectory;
3433
private List<string> _blackFiles;
3534
private List<string> _blackFormats;
@@ -313,14 +312,6 @@ public ConfiginfoBuilder SetBowl(string bowl)
313312
/// <summary>
314313
/// Sets the shell script content.
315314
/// </summary>
316-
/// <param name="script">Shell script content used to grant file permissions on Linux/Unix systems.</param>
317-
/// <returns>The current ConfiginfoBuilder instance for method chaining.</returns>
318-
public ConfiginfoBuilder SetScript(string script)
319-
{
320-
_script = script;
321-
return this;
322-
}
323-
324315
/// <summary>
325316
/// Sets the driver directory.
326317
/// </summary>

src/c#/GeneralUpdate.Core/Configuration/GlobalConfigInfo.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ public class GlobalConfigInfo : BaseConfigInfo
9999
/// Directory path containing driver files for update.
100100
/// Used when DriveEnabled is true to locate driver files for installation.
101101
/// </summary>
102-
public string DriverDirectory { get; set; }
103-
104102
/// <summary>
105103
/// Indicates whether differential patch update is enabled.
106104
/// Computed from UpdateOption.Patch or defaults to true.

src/c#/GeneralUpdate.Core/Configuration/ObjectTranslator.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ namespace GeneralUpdate.Core.Configuration;
22

33
public sealed class ObjectTranslator
44
{
5-
public static string GetPacketHash(object version) =>
6-
!GeneralTracer.IsTracingEnabled() ? string.Empty : $"[PacketHash]:{(version as VersionInfo).Hash} ";
5+
public static string GetPacketHash(object version)
6+
{
7+
if (!GeneralTracer.IsTracingEnabled()) return string.Empty;
8+
if (version is VersionInfo vi) return $"[PacketHash]:{vi.Hash} ";
9+
return string.Empty;
10+
}
711
}

src/c#/GeneralUpdate.Core/Configuration/ProcessInfo.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ public ProcessInfo() { }
4242
/// <param name="bowl">The process name to terminate before updating</param>
4343
/// <param name="scheme">The URL scheme for update requests</param>
4444
/// <param name="token">The authentication token</param>
45-
/// <param name="script">The Linux permission script</param>
4645
/// <param name="driverDirectory">The directory path containing driver files</param>
4746
/// <param name="blackFileFormats">List of file format extensions to skip</param>
4847
/// <param name="blackFiles">List of specific files to skip</param>

src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ namespace GeneralUpdate.Core.Download.Executors;
1111

1212
/// <summary>
1313
/// HTTP-based download executor with optional Range/resume support.
14-
/// Uses the shared HttpClient from VersionService for consistent SSL/auth handling.
1514
/// </summary>
1615
public class HttpDownloadExecutor : IDownloadExecutor
1716
{
@@ -33,7 +32,6 @@ public async Task<DownloadResult> ExecuteAsync(
3332
{
3433
var sw = Stopwatch.StartNew();
3534
int retries = 0;
36-
long totalBytes = -1;
3735
long existingBytes = 0;
3836

3937
// Check for existing partial file (resume support; skip when disabled)
@@ -45,8 +43,6 @@ public async Task<DownloadResult> ExecuteAsync(
4543
try
4644
{
4745
using var request = new HttpRequestMessage(HttpMethod.Get, url);
48-
49-
// Request resume from existing position (skip when resume is disabled)
5046
if (_enableResume && existingBytes > 0)
5147
request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(existingBytes, null);
5248

@@ -57,56 +53,67 @@ public async Task<DownloadResult> ExecuteAsync(
5753
request, HttpCompletionOption.ResponseHeadersRead, cts.Token)
5854
.ConfigureAwait(false);
5955

60-
// If server doesn't support Range, discard partial file
6156
if (_enableResume && existingBytes > 0 && response.StatusCode != System.Net.HttpStatusCode.PartialContent)
6257
{
6358
existingBytes = 0;
6459
File.Delete(destPath);
6560
}
6661

6762
response.EnsureSuccessStatusCode();
68-
totalBytes = response.Content.Headers.ContentLength ?? -1;
63+
var totalBytes = response.Content.Headers.ContentLength ?? -1;
6964

70-
// Append or create file
7165
var mode = existingBytes > 0 ? FileMode.Append : FileMode.Create;
7266
using var fs = new FileStream(destPath, mode, FileAccess.Write, FileShare.None);
7367
using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
7468

75-
var buffer = new byte[8192];
76-
long downloaded = existingBytes;
77-
int read;
78-
long lastReport = 0;
79-
80-
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) > 0)
81-
{
82-
await fs.WriteAsync(buffer, 0, read, token).ConfigureAwait(false);
83-
downloaded += read;
84-
85-
// Report progress every ~250ms
86-
var now = sw.ElapsedMilliseconds;
87-
if (now - lastReport >= 250 || downloaded == totalBytes + existingBytes)
88-
{
89-
lastReport = now;
90-
var pct = totalBytes > 0 ? (double)downloaded / (totalBytes + existingBytes) * 100 : -1;
91-
progress?.Report(new DownloadProgress(
92-
Path.GetFileName(destPath), downloaded,
93-
totalBytes > 0 ? totalBytes + existingBytes : null,
94-
pct, DownloadStatus.Downloading));
95-
}
96-
}
69+
var (downloaded, elapsed) = await StreamDownloadAsync(stream, fs, totalBytes, existingBytes,
70+
destPath, progress, sw, token).ConfigureAwait(false);
9771

98-
sw.Stop();
9972
progress?.Report(new DownloadProgress(
10073
Path.GetFileName(destPath), downloaded,
10174
totalBytes > 0 ? totalBytes + existingBytes : null,
10275
100, DownloadStatus.Completed));
10376

104-
return new DownloadResult(url, destPath, downloaded, sw.Elapsed, retries, true, null);
77+
return new DownloadResult(url, destPath, downloaded, elapsed, retries, true, null);
10578
}
10679
catch (Exception ex) when (ex is not OperationCanceledException)
10780
{
10881
sw.Stop();
10982
return new DownloadResult(url, null, existingBytes, sw.Elapsed, retries, false, ex.Message);
11083
}
11184
}
85+
86+
/// <summary>
87+
/// Shared download loop: reads from source stream, writes to dest, reports progress.
88+
/// Used by both HTTP and OSS executors to avoid duplicated buffer/read/write/progress logic.
89+
/// </summary>
90+
internal static async Task<(long Downloaded, TimeSpan Elapsed)> StreamDownloadAsync(
91+
Stream source, Stream dest, long totalBytes, long existingBytes,
92+
string destPath, IProgress<DownloadProgress>? progress, Stopwatch sw, CancellationToken token)
93+
{
94+
var buffer = new byte[8192];
95+
long downloaded = existingBytes;
96+
long lastReport = 0;
97+
int read;
98+
99+
while ((read = await source.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) > 0)
100+
{
101+
await dest.WriteAsync(buffer, 0, read, token).ConfigureAwait(false);
102+
downloaded += read;
103+
104+
var now = sw.ElapsedMilliseconds;
105+
if (now - lastReport >= 250 || downloaded == totalBytes + existingBytes)
106+
{
107+
lastReport = now;
108+
var pct = totalBytes > 0 ? (double)downloaded / (totalBytes + existingBytes) * 100 : -1;
109+
progress?.Report(new DownloadProgress(
110+
Path.GetFileName(destPath), downloaded,
111+
totalBytes > 0 ? totalBytes + existingBytes : null,
112+
pct, DownloadStatus.Downloading));
113+
}
114+
}
115+
116+
sw.Stop();
117+
return (downloaded, sw.Elapsed);
118+
}
112119
}

src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public async Task<DownloadResult> ExecuteAsync(
2222
CancellationToken token = default)
2323
{
2424
var sw = System.Diagnostics.Stopwatch.StartNew();
25-
long lastReport = 0;
2625
try
2726
{
2827
using var response = await _client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token)
@@ -32,25 +31,13 @@ public async Task<DownloadResult> ExecuteAsync(
3231
Directory.CreateDirectory(Path.GetDirectoryName(destPath)!);
3332
using var fs = new FileStream(destPath, FileMode.Create);
3433
using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
35-
var buffer = new byte[8192];
36-
long downloaded = 0;
37-
int read;
38-
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) > 0)
39-
{
40-
await fs.WriteAsync(buffer, 0, read, token).ConfigureAwait(false);
41-
downloaded += read;
42-
var now = sw.ElapsedMilliseconds;
43-
if (now - lastReport >= 250 || downloaded == total)
44-
{
45-
lastReport = now;
46-
var pct = total > 0 ? (double)downloaded / total * 100 : -1;
47-
progress?.Report(new DownloadProgress(
48-
Path.GetFileName(destPath), downloaded, total > 0 ? total : null, pct, DownloadStatus.Downloading));
49-
}
50-
}
51-
sw.Stop();
52-
progress?.Report(new DownloadProgress(Path.GetFileName(destPath), downloaded, total > 0 ? total : null, 100, DownloadStatus.Completed));
53-
return new DownloadResult(url, destPath, downloaded, sw.Elapsed, 0, true, null);
34+
35+
var (downloaded, elapsed) = await HttpDownloadExecutor.StreamDownloadAsync(
36+
stream, fs, total, 0, destPath, progress, sw, token).ConfigureAwait(false);
37+
38+
progress?.Report(new DownloadProgress(
39+
Path.GetFileName(destPath), downloaded, total > 0 ? total : null, 100, DownloadStatus.Completed));
40+
return new DownloadResult(url, destPath, downloaded, elapsed, 0, true, null);
5441
}
5542
catch (Exception ex) when (ex is not OperationCanceledException)
5643
{

src/c#/GeneralUpdate.Core/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
namespace GeneralUpdate.Core.Download
44
{
5-
public class MultiDownloadCompletedEventArgs(object version, bool isComplated) : EventArgs
5+
public class MultiDownloadCompletedEventArgs(object version, bool isCompleted) : EventArgs
66
{
77
public object Version { get; private set; } = version;
88

9-
public bool IsComplated { get; private set; } = isComplated;
9+
public bool IsCompleted { get; private set; } = isCompleted;
1010
}
1111
}

src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using GeneralUpdate.Core.Download.Policy;
1313
using GeneralUpdate.Core.Download.Models;
1414
using GeneralUpdate.Core.Download.Pipeline;
15+
using GeneralUpdate.Core.Download.Progress;
1516

1617
namespace GeneralUpdate.Core.Download.Orchestrators;
1718

@@ -119,6 +120,14 @@ public async Task<DownloadReport> ExecuteAsync(
119120
await Task.WhenAll(tasks).ConfigureAwait(false);
120121
sw.Stop();
121122

123+
// Dispatch all-completed event ONCE after all assets finish (only failed results)
124+
var failedDetails = results.Where(r => !r.Success)
125+
.Select(r => ((object)r.Url, r.ErrorMessage ?? "failed")).ToList();
126+
DownloadProgressReporter.DispatchAllCompleted(
127+
this,
128+
results.All(r => r.Success),
129+
failedDetails);
130+
122131
return new DownloadReport(
123132
results,
124133
totalBytes,

0 commit comments

Comments
 (0)