diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs index 172ca081..b21336e1 100644 --- a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs @@ -87,6 +87,13 @@ public GeneralUpdateBootstrap() } /// Cancel the current update operation. + /// + /// Signals the internal to request cancellation + /// of the ongoing update workflow. After calling this method, the running strategy + /// (e.g., ) will observe the cancellation + /// token and terminate the current operation at the next safe checkpoint. + /// A cancellation message is also written to the trace log. + /// public void Cancel() { _cts?.Cancel(); @@ -245,6 +252,15 @@ public GeneralUpdateBootstrap SetConfig(Configinfo configInfo) /// If just a filename (no directory separator), resolves relative to the current directory. /// Relative or absolute paths are used as-is. /// + /// This bootstrap instance for chaining. + /// Thrown when is null or whitespace. + /// Thrown when the specified file does not exist. + /// Thrown when the JSON file cannot be deserialised into a valid . + /// + /// Reads the file content as UTF-8 JSON, deserialises it into a + /// using the source-generated JSON serialisation context (), + /// then delegates to for validation and mapping. + /// public GeneralUpdateBootstrap SetConfig(string filePath) { if (string.IsNullOrWhiteSpace(filePath)) @@ -282,6 +298,21 @@ public GeneralUpdateBootstrap UseDiffPipeline(Action? confi return this; } + /// + /// Registers a pre-check callback that is invoked after update information is retrieved + /// but before downloads begin. The callback can inspect the metadata and decide whether + /// to proceed with or abort the update. + /// + /// A predicate that receives + /// and returns true to continue or false to abort the update. + /// This bootstrap instance for chaining. + /// Thrown when is null. + /// + /// The pre-check function is called during the client update strategy's standard workflow, + /// immediately after version comparison and before any packages are downloaded. + /// This allows conditional update logic, such as skipping updates under certain + /// network conditions or user preferences. + /// public GeneralUpdateBootstrap AddListenerUpdatePrecheck(Func func) { _updatePrecheck = func ?? throw new ArgumentNullException(nameof(func)); @@ -456,24 +487,64 @@ private GeneralUpdateBootstrap AddListener(Action action) return this; } + /// + /// Registers a callback for the multi-all-download-completed event, raised when all + /// update files across all versions have been downloaded. + /// + /// The callback to invoke. + /// This bootstrap instance for chaining. public GeneralUpdateBootstrap AddListenerMultiAllDownloadCompleted( Action cb) => AddListener(cb); + /// + /// Registers a callback for the multi-download-completed event, raised when a single + /// version's set of files has finished downloading. + /// + /// The callback to invoke. + /// This bootstrap instance for chaining. public GeneralUpdateBootstrap AddListenerMultiDownloadCompleted( Action cb) => AddListener(cb); + /// + /// Registers a callback for the multi-download-error event, raised when an error + /// occurs during batch download. + /// + /// The callback to invoke. + /// This bootstrap instance for chaining. public GeneralUpdateBootstrap AddListenerMultiDownloadError( Action cb) => AddListener(cb); + /// + /// Registers a callback for download statistics events, providing real-time throughput + /// and progress information during batch downloads. + /// + /// The callback to invoke. + /// This bootstrap instance for chaining. public GeneralUpdateBootstrap AddListenerMultiDownloadStatistics( Action cb) => AddListener(cb); + /// + /// Registers a callback for exception events raised during the update workflow. + /// + /// The callback to invoke. + /// This bootstrap instance for chaining. public GeneralUpdateBootstrap AddListenerException( Action cb) => AddListener(cb); + /// + /// Registers a callback for update-information events, providing version metadata + /// retrieved from the server. + /// + /// The callback to invoke. + /// This bootstrap instance for chaining. public GeneralUpdateBootstrap AddListenerUpdateInfo( Action cb) => AddListener(cb); + /// + /// Registers a callback for general progress events during the update process. + /// + /// The callback to invoke. + /// This bootstrap instance for chaining. public GeneralUpdateBootstrap AddListenerProgress( Action cb) => AddListener(cb); diff --git a/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs index 575d0ac7..e9b4a8c7 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs @@ -11,25 +11,28 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 提供引导程序基类,支持泛型自引用(CRTP)模式,用于配置和启动更新流程。 + /// Provides the bootstrap base class supporting a generic self-referencing (CRTP) pattern + /// for configuring and launching the update workflow. /// - /// 派生引导程序类型,必须继承自 - /// 更新策略类型,必须实现 + /// The derived bootstrap type; must inherit from . + /// The update strategy type; must implement . /// - /// 本类采用扩展点注册/解析模式来管理更新流程中的可替换组件。核心机制如下: + /// This class uses an extension-point registration/resolution pattern to manage + /// replaceable components in the update workflow. The core mechanisms are: /// - /// - _extensions 字典存储 Type→Type 的映射(接口类型→实现类型),通过 .Strategy<T>()、 - /// .DownloadSource<T>() 等方法注册,在需要时通过 延迟实例化。 + /// - The _extensions dictionary stores Type→Type mappings (interface type → implementation type). + /// Extensions are registered via fluent methods such as .Strategy<T>() or + /// .DownloadSource<T>() and are lazily instantiated on demand through + /// .
+ /// - The _instances dictionary stores already-instantiated singleton objects + /// (e.g., BlackListConfig). These take precedence over lazy registrations + /// in _extensions.
+ /// - The method provides fluent configuration + /// options, read via , with a default-value + /// fallback mechanism. ///
- /// - /// - _instances 字典存储已实例化的单例对象(如 BlackListConfig), - /// 优先于 _extensions 中的延迟注册。 - /// - /// - /// - 方法提供流式配置选项,通过 读取, - /// 支持默认值回退机制。 - /// - /// 典型用法:链式调用注册各种扩展组件,最后调用 启动更新流程。 + /// Typical usage: chain extension registrations together, then call + /// to start the update workflow. ///
public abstract class AbstractBootstrap where TBootstrap : AbstractBootstrap @@ -37,10 +40,10 @@ public abstract class AbstractBootstrap { private readonly ConcurrentDictionary _options; - /// 用户注册的扩展类型映射(接口类型→实现类型),用于延迟实例化。 + /// User-registered extension type mappings (interface type → implementation type), used for lazy instantiation. private readonly Dictionary _extensions = new(); - /// 已注册的单例实例(如 BlackListConfig)。 + /// Registered singleton instances (e.g., BlackListConfig). private readonly Dictionary _instances = new(); protected internal AbstractBootstrap() @@ -51,15 +54,18 @@ protected internal AbstractBootstrap() public abstract Task LaunchAsync(); /// - /// 设置更新选项值,支持流式(fluent)调用。 + /// Sets an update option value, supporting fluent chaining. /// - /// 选项值的类型。 - /// 要设置的选项键。 - /// 要设置的选项值。如果为 null,则从字典中移除该选项,后续读取将回退到默认值。 - /// 返回当前 实例,支持链式调用。 + /// The type of the option value. + /// The option key to set. + /// The value to set. If null, the option entry is removed + /// from the dictionary so that subsequent reads fall back to the default value. + /// The current instance for chaining. /// - /// 使用 ConcurrentDictionary 存储选项,保证线程安全。 - /// 如果 null,则删除该选项条目,使得 返回 。 + /// Options are stored in a ConcurrentDictionary to guarantee thread safety. + /// When is null, the entry is removed, causing + /// to return + /// . /// public TBootstrap Option(UpdateOption option, T value) { @@ -71,16 +77,18 @@ public TBootstrap Option(UpdateOption option, T value) } /// - /// 获取指定选项的值。如果选项为 null 或未注册,则返回默认值。 + /// Gets the value of the specified option. Returns the default value when the option + /// is null or has not been registered. /// - /// 选项值的类型。 - /// 要获取的选项键,可为 null。 + /// The type of the option value. + /// The option key to retrieve; can be null. /// - /// 如果找到注册的值则返回该值;否则返回 。 + /// The registered value if found; otherwise, . /// /// - /// 先尝试从 _options 字典中查找,如果未找到则回退到 。 - /// 这是 的配套读取方法。 + /// First attempts to look up the option in the _options dictionary. + /// If not found, falls back to . + /// This is the companion read method for . /// protected T GetOption(UpdateOption? option) { @@ -93,13 +101,17 @@ protected T GetOption(UpdateOption? option) // ═══════════ Extension point registration ═══════════ /// - /// 注册自定义的 OS 级别策略实现(如 )。 + /// Registers a custom OS-level strategy implementation (e.g., + /// , , + /// ). /// - /// 策略实现类型,必须实现 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The strategy implementation type; must implement + /// and have a parameterless constructor. + /// The current instance for chaining. /// - /// 注册后,当 ClientUpdateStrategy 调用 ResolveOsStrategy() 时会优先使用此处注册的类型, - /// 而非自动探测当前操作系统。此扩展点在 执行时生效。 + /// Once registered, when ClientUpdateStrategy.ResolveOsStrategy() is called, the + /// type registered here is used instead of auto-detecting the current operating system. + /// This extension point takes effect when executes. /// public TBootstrap Strategy() where T : IStrategy, new() { @@ -108,18 +120,21 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册更新生命周期钩子实现,用于在更新流程的关键节点注入自定义逻辑。 + /// Registers update lifecycle hook implementations for injecting custom logic at key + /// points in the update workflow. /// - /// 钩子实现类型,必须实现 IUpdateHooks 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The hook implementation type; must implement IUpdateHooks + /// and have a parameterless constructor. + /// The current instance for chaining. /// - /// 钩子回调在以下节点被调用: - /// - OnBeforeUpdateAsync:下载开始前,可取消更新; - /// - OnDownloadCompletedAsync:所有文件下载完成后; - /// - OnAfterUpdateAsync:升级包应用完成后; - /// - OnBeforeStartAppAsync:启动升级进程前; - /// - OnUpdateErrorAsync:更新过程中出现异常时。 - /// 默认使用 NoOpUpdateHooks(无操作实现)。所有钩子调用都包裹在 try-catch 中,单个钩子失败不会阻断流程。 + /// Hook callbacks are invoked at the following points in the workflow: + /// - OnBeforeUpdateAsync: Called before the download starts; can cancel the update. + /// - OnDownloadCompletedAsync: Called after all files have been downloaded. + /// - OnAfterUpdateAsync: Called after the upgrade packages have been applied. + /// - OnBeforeStartAppAsync: Called before launching the upgrade process. + /// - OnUpdateErrorAsync: Called when an exception occurs during the update. + /// The default implementation is NoOpUpdateHooks (no-op). All hook invocations + /// are wrapped in try-catch; a single hook failure does not block the workflow. /// public TBootstrap Hooks() where T : Hooks.IUpdateHooks, new() { @@ -128,29 +143,37 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册 SSL 验证策略实现,用于自定义 HTTPS 证书验证行为。 + /// Registers an SSL validation policy implementation for customising HTTPS certificate + /// validation behaviour. /// - /// SSL 策略实现类型,必须实现 ISslValidationPolicy 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The SSL policy implementation type; must implement + /// ISslValidationPolicy and have a parameterless constructor. + /// The current instance for chaining. /// - /// 可用于跳过自签名证书验证、使用自定义根证书颁发机构或实施证书锁定等场景。 - /// 此扩展点在发起 HTTP 下载请求时由 HttpClientProvider 读取并应用于 HttpClientHandler。 + /// Can be used to bypass self-signed certificate validation, use custom root certificate + /// authorities, or implement certificate pinning. This extension point is read by + /// HttpClientProvider and applied to the HttpClientHandler when initiating + /// HTTP download requests. /// public TBootstrap SslPolicy() where T : Security.ISslValidationPolicy, new() { _extensions[typeof(Security.ISslValidationPolicy)] = typeof(T); return (TBootstrap)this; } - + /// - /// 注册自定义下载重试/超时策略,用于控制下载失败时的重试行为。 + /// Registers a custom download retry/timeout policy for controlling how the system + /// behaves when downloads fail. /// - /// 下载策略实现类型,必须实现 IDownloadPolicy 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The download policy implementation type; must implement + /// IDownloadPolicy and have a parameterless constructor. + /// The current instance for chaining. /// - /// 仅在使用默认下载编排器(DefaultDownloadOrchestrator)时生效。 - /// 如果同时通过 注册了自定义编排器,则此设置被忽略。 - /// 下载策略决定每次失败后的等待时间和最大重试次数。 + /// Only takes effect when using the default download orchestrator + /// (DefaultDownloadOrchestrator). If a custom orchestrator is also registered + /// via , this setting is ignored. + /// The download policy determines the wait interval after each failure and the + /// maximum retry count. /// public TBootstrap DownloadPolicy() where T : Download.Abstractions.IDownloadPolicy, new() { @@ -159,14 +182,17 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册自定义单文件下载执行器,用于支持非 HTTP/HTTPS 下载协议。 + /// Registers a custom single-file download executor for supporting protocols other + /// than HTTP/HTTPS. /// - /// 下载执行器实现类型,必须实现 IDownloadExecutor 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The download executor implementation type; must implement + /// IDownloadExecutor and have a parameterless constructor. + /// The current instance for chaining. /// - /// 仅在使用默认下载编排器(DefaultDownloadOrchestrator)时生效。 - /// 如果同时通过 注册了自定义编排器,则此设置被忽略。 - /// 可用于实现 FTP、SFTP 或私有协议的文件下载。 + /// Only takes effect when using the default download orchestrator + /// (DefaultDownloadOrchestrator). If a custom orchestrator is also registered + /// via , this setting is ignored. + /// Can be used to implement FTP, SFTP, or private-protocol file downloads. /// public TBootstrap DownloadExecutor() where T : Download.Abstractions.IDownloadExecutor, new() { @@ -175,14 +201,20 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册下载数据源实现,用于从服务器获取版本信息和更新文件清单。 + /// Registers a download data source implementation for retrieving version information + /// and update file manifests from the server. /// - /// 下载源实现类型,必须实现 IDownloadSource 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The download source implementation type; must implement + /// IDownloadSource and have a parameterless constructor. + /// The current instance for chaining. /// - /// 内置实现包括 HttpDownloadSource(基于 HTTP/HTTPS)和 SignalR Hub 下载源。 - /// 通过此方法可注册自定义数据源,如从本地文件系统、FTP 服务器或私有云存储获取更新清单。 - /// 此扩展点在 ClientUpdateStrategy.ExecuteStandardWorkflowAsync() 中调用 downloadSource.ListAsync() 时生效。 + /// Built-in implementations include HttpDownloadSource (HTTP/HTTPS) and + /// SignalR Hub download sources. + /// Use this method to register custom data sources, such as a local file system, + /// FTP server, or private cloud storage for fetching update manifests. + /// This extension point takes effect when + /// ClientUpdateStrategy.ExecuteStandardWorkflowAsync() calls + /// downloadSource.ListAsync(). /// public TBootstrap DownloadSource() where T : Download.Abstractions.IDownloadSource, new() { @@ -191,14 +223,18 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册自定义下载后处理管道,用于对已下载的文件进行哈希校验、解密、病毒扫描等后处理。 + /// Registers a custom post-download processing pipeline for performing hash verification, + /// decryption, virus scanning, or other post-processing on downloaded files. /// - /// 下载管道实现类型,必须实现 IDownloadPipeline 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The download pipeline implementation type; must implement + /// IDownloadPipeline and have a parameterless constructor. + /// The current instance for chaining. /// - /// 仅在使用默认下载编排器(DefaultDownloadOrchestrator)时生效。 - /// 如果同时通过 注册了自定义编排器,则此设置被忽略。 - /// 下载管道接收下载完成的文件路径,可执行完整性校验、解密加密文件或扫描恶意软件等操作。 + /// Only takes effect when using the default download orchestrator + /// (DefaultDownloadOrchestrator). If a custom orchestrator is also registered + /// via , this setting is ignored. + /// The download pipeline receives the path of each completed download and can perform + /// integrity checks, decrypt encrypted files, or scan for malware. /// public TBootstrap DownloadPipeline() where T : Download.Abstractions.IDownloadPipeline, new() { @@ -207,17 +243,20 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册更新状态报告器,用于向服务器(如 GeneralSpacestation)上报更新进度和结果。 + /// Registers an update status reporter for reporting update progress and results + /// to a server (e.g., GeneralSpacestation). /// - /// 报告器实现类型,必须实现 IUpdateReporter 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The reporter implementation type; must implement + /// IUpdateReporter and have a parameterless constructor. + /// The current instance for chaining. /// - /// 报告器在更新流程的关键节点被调用: - /// - 更新开始:ReportAsync(Updating) - /// - 下载完成:ReportAsync(Updating) - /// - 更新应用成功:ReportAsync(Success) - /// - 更新失败:ReportAsync(Failure) - /// 默认使用 NoOpUpdateReporter(无操作实现)。所有上报调用都包裹在 try-catch 中,单个上报失败不会阻断流程。 + /// The reporter is invoked at key points in the update workflow: + /// - Update starts: ReportAsync(Updating). + /// - Download completes: ReportAsync(Updating). + /// - Update applied successfully: ReportAsync(Success). + /// - Update failed: ReportAsync(Failure). + /// The default implementation is NoOpUpdateReporter (no-op). All reporter + /// invocations are wrapped in try-catch; a single failure does not block the workflow. /// public TBootstrap UpdateReporter() where T : Download.Reporting.IUpdateReporter, new() { @@ -226,13 +265,17 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册 HTTP 身份验证提供程序,用于在下载请求中添加认证信息。 + /// Registers an HTTP authentication provider for attaching authentication credentials + /// to download requests. /// - /// 认证提供程序实现类型,必须实现 IHttpAuthProvider 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The authentication provider implementation type; must implement + /// IHttpAuthProvider and have a parameterless constructor. + /// The current instance for chaining. /// - /// 可用于支持需要令牌认证(Bearer Token)、基本认证(Basic Auth)或自定义认证头的下载服务器。 - /// 此扩展点在创建 HTTP 下载请求时由 HttpClientProvider 读取并注入请求头。 + /// Can be used to support servers that require token authentication (Bearer Token), + /// basic authentication (Basic Auth), or custom authentication headers. This extension + /// point is read by HttpClientProvider and injected into request headers when + /// creating HTTP download requests. /// public TBootstrap UpdateAuth() where T : Security.IHttpAuthProvider, new() { @@ -241,15 +284,21 @@ protected T GetOption(UpdateOption? option) } /// - /// 注册自定义下载编排器,端到端处理批量下载任务。 + /// Registers a custom download orchestrator for end-to-end handling of batch + /// download tasks. /// - /// 编排器实现类型,必须实现 IDownloadOrchestrator 并具有无参构造函数。 - /// 返回当前 实例,支持链式调用。 + /// The orchestrator implementation type; must implement + /// IDownloadOrchestrator and have a parameterless constructor. + /// The current instance for chaining. /// - /// 这是最高级别的下载抽象,拥有完整的下载管道的所有权。 - /// 当设置自定义编排器后, 的注册将被忽略, - /// 因为自定义编排器完全接管了下载流程的控制权。 - /// 适用于需要完全自定义下载行为(如使用第三方下载库、分块下载、多线程下载等)的高级场景。 + /// This is the highest-level download abstraction, owning full control of the + /// download pipeline. + /// When a custom orchestrator is set, registrations via + /// , , and + /// are ignored, because the custom orchestrator + /// assumes full control of the download workflow. + /// Suitable for advanced scenarios requiring fully customised download behaviour + /// (e.g., third-party download libraries, chunked download, multi-threaded download). /// public TBootstrap DownloadOrchestrator() where T : Download.Abstractions.IDownloadOrchestrator, new() { @@ -258,16 +307,23 @@ protected T GetOption(UpdateOption? option) } /// - /// 解析并实例化指定接口类型的扩展。采用两阶段查找策略。 + /// Resolves and instantiates an extension of the specified interface type using a + /// two-phase lookup strategy. /// - /// 要解析的扩展接口类型。 - /// 扩展的实例;如果未注册对应的实现类型,则返回 null + /// The extension interface type to resolve. + /// The extension instance; null if no corresponding implementation + /// type has been registered. /// - /// 两阶段查找: - /// 第一阶段:在 _extensions 字典中按接口类型查找已注册的实现类型 Type, - /// 找到后通过 Activator.CreateInstance 创建新实例。 - /// 第二阶段:如果在 _extensions 中未找到,则在 _instances 字典中查找已存在的单例实例。 - /// _instances 中的单例实例优先于 _extensions 中的延迟注册。 + /// Two-phase lookup: + /// + /// Phase 1 — Looks up the registered implementation Type in the + /// _extensions dictionary by interface type. If found, a new instance is + /// created via Activator.CreateInstance.
+ /// Phase 2 — If not found in _extensions, looks up an existing + /// singleton instance in the _instances dictionary. + ///
+ /// Singleton instances in _instances take precedence over lazy + /// registrations in _extensions. ///
protected TExtension? ResolveExtension() where TExtension : class { @@ -279,18 +335,20 @@ protected T GetOption(UpdateOption? option) } /// - /// 解析指定扩展接口的注册实现类型,但不实例化该类型。 + /// Resolves the registered implementation type for the specified extension interface + /// without instantiating it. /// - /// 要解析的扩展接口类型。 - /// 注册的实现类型;如果未注册则返回 null + /// The extension interface type to resolve. + /// The registered implementation type; null if not registered. /// - /// 不同于 ,此方法仅返回类型信息, - /// 适用于需要反射获取类型元数据但暂时不需要创建实例的场景, - /// 例如需要提前检查某扩展是否已注册以决定流程分支。 + /// Unlike , this method only returns + /// type information. It is suitable for scenarios where reflection-based type + /// metadata is needed without creating an instance — for example, checking + /// whether an extension is registered in order to decide a workflow branch. /// protected Type? ResolveExtensionType() where TExtension : class { return _extensions.TryGetValue(typeof(TExtension), out var t) ? t : null; } } -} \ No newline at end of file +} diff --git a/src/c#/GeneralUpdate.Core/Configuration/BaseConfigInfo.cs b/src/c#/GeneralUpdate.Core/Configuration/BaseConfigInfo.cs index 4ca75743..33739d27 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/BaseConfigInfo.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/BaseConfigInfo.cs @@ -4,22 +4,25 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 基础配置抽象类,包含所有配置对象共有的公共字段。 - /// 作为面向用户的配置 ()、内部运行时状态 () - /// 以及进程间通信参数 () 的基类,统一管理公共属性以减少重复代码。 + /// Abstract base configuration class containing common fields shared by all configuration objects. + /// Serves as the base class for the user-facing configuration (), the internal + /// runtime state (), and the inter-process communication parameters + /// (), managing common properties in a single place to reduce code duplication. /// /// /// - /// 该类定义了更新流程中通用的配置项,包括应用名称、安装路径、版本号、认证信息、 - /// 排除列表(黑名单文件、格式、目录)以及网络通信相关参数(URL Scheme、Token 等)。 + /// This class defines the configuration items common to the entire update workflow, including application + /// names, installation path, version numbers, authentication information, exclusion lists (blacklisted + /// files, formats, and directories), and network communication parameters (URL scheme, token, etc.). /// /// - /// 其中 UpdateAppNameInstallPath 提供了合理的默认值, - /// 分别为 "Update.exe" 和当前程序运行目录,在未显式配置时可正常工作。 + /// UpdateAppName and InstallPath provide sensible defaults ("Update.exe" and the + /// current application directory, respectively), so the system can function even when these values are + /// not explicitly configured. /// /// - /// 此类为抽象类,不能直接实例化,必须通过派生类(、 - /// )使用。 + /// This class is abstract and cannot be instantiated directly. It must be used through derived classes + /// (, ). /// /// /// @@ -28,161 +31,161 @@ namespace GeneralUpdate.Core.Configuration public abstract class BaseConfigInfo { /// - /// 升级应用程序的可执行文件名称(例如 "Update.exe")。 - /// 当客户端需要启动升级进程时,使用此名称定位并启动升级程序。 + /// The executable file name of the updater application (e.g., "Update.exe"). + /// When the client needs to launch the upgrade process, this name is used to locate and start the updater. /// /// - /// 默认值为 "Update.exe"。如果升级程序使用了不同的文件名, - /// 需要通过 进行配置。 + /// Defaults to "Update.exe". If the updater uses a different filename, it must be configured via + /// . /// public string UpdateAppName { get; set; } = "Update.exe"; /// - /// 主应用程序的可执行文件名称。 - /// 用于标识将要被更新的主应用程序进程。 + /// The executable file name of the main application. + /// Used to identify the main application process that will be updated. /// /// - /// 该属性在 中会被校验不能为空。 - /// 在 中, - /// 此值会被映射到 属性。 + /// This property is validated to be non-empty in . + /// In , this value is mapped to the + /// property. /// public string MainAppName { get; set; } /// - /// 应用程序文件的安装路径。 - /// 这是所有更新文件操作所依据的根目录。 + /// The installation path of the application files. + /// This is the root directory for all update file operations. /// /// - /// 默认值为 AppDomain.CurrentDomain.BaseDirectory, - /// 即当前程序运行所在的基目录。通常情况下无需手动设置, - /// 但在需要将更新文件安装到非默认路径时必须显式配置。 + /// Defaults to AppDomain.CurrentDomain.BaseDirectory, the base directory of the currently running + /// application. Typically, manual configuration is not required, but it must be explicitly set when + /// update files need to be installed to a non-default path. /// public string InstallPath { get; set; } = AppDomain.CurrentDomain.BaseDirectory; /// - /// 升级可执行文件所在的目录路径(可选)。 - /// 可以是绝对路径,也可以是相对于 的相对路径。 + /// The directory path where the updater executable is located (optional). + /// Can be an absolute path or a relative path relative to . /// /// /// - /// 当设置了此属性时,升级进程将从 UpdatePath 目录启动, - /// 而非 目录。 + /// When this property is set, the upgrade process starts from the UpdatePath directory + /// instead of the directory. /// /// - /// 如果此属性为 null 或空字符串,则回退到 , - /// 保持向后兼容性。 + /// If this property is null or empty, it falls back to for backward compatibility. /// /// - /// 示例:设置为 "Upgrade" 时,升级程序将位于 - /// InstallPath/Upgrade/UpdateAppName。 + /// Example: When set to "Upgrade", the updater will be located at + /// InstallPath/Upgrade/UpdateAppName. /// /// public string UpdatePath { get; set; } /// - /// 更新日志网页的 URL 地址。 - /// 用户可通过此地址查看详细的版本变更记录。 + /// The URL of the update log web page. + /// Users can view detailed version change records at this address. /// /// - /// 在 中,如果此属性已设置, - /// 则会校验其是否为有效的绝对 URI 格式。 + /// In , if this property is set, it is validated to be a valid absolute URI. /// public string UpdateLogUrl { get; set; } /// - /// 用于身份验证的应用程序密钥。 - /// 在向更新服务器请求更新信息时,需要此密钥进行身份认证。 + /// The application secret key used for authentication. + /// This key is required when requesting update information from the update server. /// /// - /// 该属性为必填项,在 中会校验不能为空。 + /// This property is required and is validated to be non-empty in . /// public string AppSecretKey { get; set; } /// - /// 客户端应用程序的当前版本号。 - /// 格式应遵循语义化版本规范(例如 "1.0.0")。 + /// The current version number of the client application. + /// The format should follow semantic versioning conventions (e.g., "1.0.0"). /// /// - /// 该属性为必填项,在 中会校验不能为空。 - /// 通过比较 ClientVersion 与服务端返回的最新版本号, - /// 可确定主应用是否需要更新()。 + /// This property is required and is validated to be non-empty in . + /// Comparing ClientVersion against the latest version from the server determines whether the + /// main application needs updating (). /// public string ClientVersion { get; set; } /// - /// 应从更新过程中排除的特定文件列表。 - /// 黑名单中的文件将在更新操作期间被跳过,不会被覆盖或删除。 + /// A list of specific files to exclude from the update process. + /// Files in the blacklist will be skipped during update operations and will not be overwritten or deleted. /// /// - /// 与 共同构成了 - /// 更新排除策略,用于保护不应被更新操作影响的关键文件。 + /// Together with and , this forms the update + /// exclusion strategy that protects critical files from being affected by the update. /// public List BlackFiles { get; set; } /// - /// 应从更新过程中排除的文件格式扩展名列表。 - /// 例如:[".log", ".tmp", ".cache"] 将跳过所有具有这些扩展名的文件。 + /// A list of file format extensions to exclude from the update process. + /// For example: [".log", ".tmp", ".cache"] will skip all files with these extensions. /// /// - /// 此为批量排除机制,适用于需要跳过某一类文件的场景, - /// 与 针对单个文件的排除方式互补。 + /// This is a bulk exclusion mechanism, useful for skipping entire categories of files. + /// It complements , which handles individual file exclusions. /// public List BlackFormats { get; set; } /// - /// 应在更新过程中跳过的目录路径列表。 - /// 列表中的整个目录树都将被忽略,不参与任何更新操作。 + /// A list of directory paths to skip during the update process. + /// Entire directory trees in this list will be ignored and excluded from all update operations. /// public List SkipDirectorys { get; set; } /// - /// 用于报告更新状态和结果的 API 端点 URL。 - /// 更新的进度和完成状态将发送到此 URL。 + /// The API endpoint URL for reporting update status and results. + /// Update progress and completion status are sent to this URL. /// /// - /// 此 URL 在 中会映射到 - /// ,由升级进程在更新完成后进行回调。 + /// This URL is mapped to in + /// and is called back by the upgrade process + /// after the update completes. /// public string ReportUrl { get; set; } /// - /// 在开始更新前应被终止的进程名称。 - /// 通常用于关闭可能冲突的后台进程(例如 "Bowl" 进程)。 + /// The name of the process to terminate before starting the update. + /// Typically used to shut down conflicting background processes (e.g., the "Bowl" process). /// /// - /// 在更新流程中,启动升级进程之前会尝试终止此名称对应的进程, - /// 以避免文件占用导致更新失败。 + /// In the update workflow, the system attempts to terminate the process matching this name before + /// launching the upgrade process, to avoid file-locking issues that could cause the update to fail. /// public string Bowl { get; set; } /// - /// 用于更新请求的 URL 方案(例如 "http" 或 "https")。 - /// 此方案决定了与更新服务器通信时使用的协议。 + /// The URL scheme used for update requests (e.g., "http" or "https"). + /// This scheme determines the protocol used when communicating with the update server. /// public string Scheme { get; set; } /// - /// 用于 API 请求的身份验证令牌。 - /// 在与更新服务器通信时,此令牌会包含在 HTTP 请求头中。 + /// The authentication token used for API requests. + /// This token is included in HTTP request headers when communicating with the update server. /// public string Token { get; set; } /// - /// 包含驱动程序文件的目录路径,用于驱动更新功能。 - /// 当启用驱动更新时,系统会从此目录定位并安装驱动程序文件。 + /// The directory path containing driver files, used for driver-based update functionality. + /// When driver updates are enabled, the system locates and installs driver files from this directory. /// public string DriverDirectory { get; set; } /// - /// 当前更新角色 — 决定 - /// 启动哪个应用程序。 + /// The current update role — determines which application is launched by + /// . /// /// /// - /// 当 时,启动 UpdateAppName(升级进程)。 + /// When set to , launches UpdateAppName (the upgrade process). /// /// - /// 当 时,启动 MainAppName 主应用和 Bowl 进程。 + /// When set to , launches the MainAppName main application + /// and the Bowl process. /// /// public AppType? AppType { get; set; } diff --git a/src/c#/GeneralUpdate.Core/Configuration/Configinfo.cs b/src/c#/GeneralUpdate.Core/Configuration/Configinfo.cs index c845f7ff..baa76c59 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/Configinfo.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/Configinfo.cs @@ -4,19 +4,21 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 面向外部 API 调用者的更新参数配置类。 - /// 该类专为外部使用者设计,用于配置更新行为所需的核心参数。 - /// 继承自 ,复用公共字段以减少重复并提高可维护性。 + /// Update parameter configuration class for external API callers. + /// Designed specifically for external consumers to configure the core parameters required for update behavior. + /// Inherits from to reuse common fields, reducing duplication and improving maintainability. /// /// /// - /// Configinfo 是更新流程的入口配置对象,由 通过建造者模式构建。 - /// 构建完成后,会通过 映射为内部运行时配置 - /// ,供更新工作流使用。 + /// Configinfo is the entry-point configuration object for the update workflow, constructed by + /// using the builder pattern. Once built, it is mapped to the internal + /// runtime configuration via + /// for use by the update pipeline. /// /// - /// 调用 方法可对所有必填字段进行完整性校验, - /// 确保 UpdateUrlMainAppNameClientVersion 等关键参数不为空或格式正确。 + /// Calling the method performs completeness validation on all required fields, + /// ensuring that key parameters such as UpdateUrl, MainAppName, and ClientVersion + /// are not empty or incorrectly formatted. /// /// /// @@ -26,60 +28,60 @@ namespace GeneralUpdate.Core.Configuration public class Configinfo : BaseConfigInfo { /// - /// 用于检查可用更新的 API 端点 URL。 - /// 客户端通过查询此 URL 来确定是否存在新版本可供更新。 + /// The API endpoint URL used to check for available updates. + /// The client queries this URL to determine whether a new version is available. /// /// - /// 该属性为必填项,在 方法中会校验其是否为有效的绝对 URI 格式。 - /// 如果未配置或格式无效,将抛出 。 + /// This property is required. The method checks that it is a valid absolute URI. + /// If it is not configured or is malformed, an is thrown. /// public string UpdateUrl { get; set; } /// - /// 升级程序(更新器自身)的当前版本号。 - /// 该版本号用于实现更新器自身的独立升级,与主应用的版本管理解耦。 + /// The current version number of the updater (the update client itself). + /// This version number enables independent upgrades of the updater itself, decoupled from the main application's version management. /// /// - /// 通过比较 UpgradeClientVersion 与服务端返回的最新版本号, - /// 可确定是否需要先对更新器本身执行升级操作。 + /// By comparing UpgradeClientVersion with the latest version returned by the server, + /// the system determines whether the updater itself needs to be upgraded first. /// public string UpgradeClientVersion { get; set; } /// - /// 用于跟踪和更新管理的唯一产品标识符。 - /// 多个产品可以共享同一套更新基础设施,通过不同的产品 ID 进行区分。 + /// The unique product identifier used for tracking and update management. + /// Multiple products can share the same update infrastructure and are distinguished by different product IDs. /// public string ProductId { get; set; } /// - /// 校验当前配置对象的必填字段是否完整且格式正确。 + /// Validates that all required fields of the configuration object are present and correctly formatted. /// /// - /// 该方法会对以下字段执行校验逻辑: + /// The method performs validation on the following fields: /// /// - /// UpdateUrl:不能为空,且必须是有效的绝对 URI。 + /// UpdateUrl: Must not be empty and must be a valid absolute URI. /// - /// UpdateLogUrl:如果已设置,则必须是有效的绝对 URI。 + /// UpdateLogUrl: If set, must be a valid absolute URI. /// - /// UpdateAppName:不能为空。 + /// UpdateAppName: Must not be empty. /// - /// MainAppName:不能为空。 + /// MainAppName: Must not be empty. /// - /// AppSecretKey:不能为空。 + /// AppSecretKey: Must not be empty. /// - /// ClientVersion:不能为空。 + /// ClientVersion: Must not be empty. /// - /// InstallPath:不能为空。 + /// InstallPath: Must not be empty. /// /// - /// 该方法通常在 方法的末尾被调用, - /// 以确保构建出的配置对象是完整且合法的。 + /// This method is typically called at the end of the method + /// to ensure the constructed configuration object is complete and valid. /// /// /// - /// 当任一必填字段为空、仅含空白字符或格式无效时抛出, - /// 异常消息会指明具体是哪个字段校验失败。 + /// Thrown when any required field is null, empty, consists only of whitespace, or is malformed. + /// The exception message indicates which specific field failed validation. /// public void Validate() { diff --git a/src/c#/GeneralUpdate.Core/Configuration/ConfiginfoBuilder.cs b/src/c#/GeneralUpdate.Core/Configuration/ConfiginfoBuilder.cs index bb573b83..dab1e383 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/ConfiginfoBuilder.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/ConfiginfoBuilder.cs @@ -6,14 +6,14 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 通用的 建造者类,用于简化更新配置的创建过程。 - /// 只需三个核心参数(UpdateUrlTokenScheme), - /// 即可自动生成平台适用的默认值,其他配置项均可选填。 + /// A generic builder class that simplifies the creation of update configuration. + /// With just three core parameters (UpdateUrl, Token, Scheme), it automatically + /// generates platform-appropriate defaults. All other configuration items are optional. /// /// /// - /// 该建造者采用流式(Fluent)接口设计,所有 Set 方法均返回当前实例, - /// 支持链式调用。例如: + /// This builder uses a fluent interface design — all Set methods return the current instance, + /// enabling method chaining. For example: /// /// var config = new ConfiginfoBuilder() /// .SetUpdateUrl("https://update.example.com") @@ -23,13 +23,15 @@ namespace GeneralUpdate.Core.Configuration /// /// /// - /// 设计灵感来源于 Velopack 等项目的零配置设计模式。 - /// 通过 Create() 静态工厂方法可从 update_config.json 配置文件加载设置, - /// 该方式优先级最高,配置文件中指定的所有值将覆盖代码中的设置。 + /// The design is inspired by the zero-configuration pattern used in projects like Velopack. + /// The Create() static factory method loads settings from an update_config.json file, + /// which has the highest priority — all values specified in the configuration file override + /// programmatic settings. /// /// - /// 构建完成后调用 方法会触发生成的 对象的 - /// 校验,确保生成的配置合法有效。 + /// After building, calling the method triggers validation on the generated + /// object via , ensuring the resulting + /// configuration is valid. /// /// /// @@ -60,16 +62,17 @@ public class ConfiginfoBuilder private List _skipDirectorys; /// - /// 通过从 update_config.json 配置文件中加载设置来创建 - /// 实例。 + /// Creates a instance by loading settings from the + /// update_config.json configuration file. /// /// /// - /// 配置文件必须存在于应用程序的运行目录中,并包含所有必需的设置。 - /// 配置文件具有最高优先级——所有设置都必须在 JSON 文件中指定。 + /// The configuration file must exist in the application's running directory and contain all + /// required settings. This approach has the highest priority — all settings must be specified + /// in the JSON file. /// /// - /// JSON 配置文件示例: + /// Example JSON configuration file: /// /// { /// "UpdateUrl": "https://update.example.com", @@ -81,18 +84,19 @@ public class ConfiginfoBuilder /// /// /// - /// 如果配置文件不存在或格式无效,将抛出 。 - /// 该方法不支持回退到手动设置——如果需要手动设置,请直接使用构造函数并通过 Set 方法链式构建。 + /// If the configuration file does not exist or has an invalid format, a + /// is thrown. This method does not fall back to manual settings. For manual configuration, use the + /// constructor and chain Set methods directly. /// /// /// - /// 一个从配置文件加载了设置的新 实例。 + /// A new instance with settings loaded from the configuration file. /// /// - /// 当运行目录中找不到 update_config.json 文件时抛出。 + /// Thrown when the update_config.json file cannot be found in the running directory. /// /// - /// 当配置文件格式无效或无法加载时抛出。 + /// Thrown when the configuration file format is invalid or cannot be loaded. /// public static ConfiginfoBuilder Create() { @@ -109,16 +113,16 @@ public static ConfiginfoBuilder Create() } /// - /// 从运行目录中的 update_config.json 文件加载配置。 + /// Loads configuration from the update_config.json file in the running directory. /// /// - /// 此方法会尝试读取并解析 JSON 文件。如果文件不存在、JSON 格式无效、 - /// 文件读取权限不足或发生其他意外错误,均会返回 null 而非抛出异常, - /// 以便调用方决定回退策略。 + /// This method attempts to read and parse the JSON file. If the file does not exist, the JSON format + /// is invalid, file read permissions are insufficient, or any other unexpected error occurs, it returns + /// null instead of throwing an exception, allowing the caller to decide on a fallback strategy. /// /// - /// 成功时返回包含文件设置的 实例; - /// 如果文件不存在、格式无效或读取失败,则返回 null。 + /// A instance populated with the file settings on success; + /// null if the file does not exist, the format is invalid, or reading fails. /// private static ConfiginfoBuilder LoadFromConfigFile() { @@ -185,32 +189,32 @@ private static ConfiginfoBuilder LoadFromConfigFile() } catch (System.Text.Json.JsonException) { - // JSON 格式无效,回退到参数方式 + // JSON format is invalid, fall back to parameter-based approach return null; } catch (IOException) { - // 文件读取错误,回退到参数方式 + // File read error, fall back to parameter-based approach return null; } catch (UnauthorizedAccessException) { - // 权限不足,回退到参数方式 + // Insufficient permissions, fall back to parameter-based approach return null; } catch { - // 任何其他意外错误,回退到参数方式 + // Any other unexpected error, fall back to parameter-based approach return null; } } /// - /// 设置更新检查的 API 端点 URL。 + /// Sets the API endpoint URL for update checking. /// - /// 用于检查可用更新的 API 端点 URL。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The API endpoint URL used to check for available updates. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetUpdateUrl(string updateUrl) { if (string.IsNullOrWhiteSpace(updateUrl)) @@ -221,11 +225,11 @@ public ConfiginfoBuilder SetUpdateUrl(string updateUrl) } /// - /// 设置 API 请求的身份验证令牌。 + /// Sets the authentication token for API requests. /// - /// 用于 HTTP 请求头的身份验证令牌。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The authentication token for HTTP request headers. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetToken(string token) { if (string.IsNullOrWhiteSpace(token)) @@ -236,11 +240,11 @@ public ConfiginfoBuilder SetToken(string token) } /// - /// 设置 URL 方案(例如 "http" 或 "https")。 + /// Sets the URL scheme (e.g., "http" or "https"). /// - /// 用于服务器通信的 URL 方案。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The URL scheme used for server communication. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetScheme(string scheme) { if (string.IsNullOrWhiteSpace(scheme)) @@ -251,11 +255,11 @@ public ConfiginfoBuilder SetScheme(string scheme) } /// - /// 设置升级应用程序的名称(更新完成后启动的可执行文件)。 + /// Sets the name of the upgrade application (the executable launched after the update completes). /// - /// 升级应用程序的可执行文件名称。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The executable file name of the upgrade application. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetUpgradeAppName(string appName) { if (string.IsNullOrWhiteSpace(appName)) @@ -266,11 +270,11 @@ public ConfiginfoBuilder SetUpgradeAppName(string appName) } /// - /// 设置主应用程序的名称。 + /// Sets the name of the main application. /// - /// 主应用程序的名称(不含文件扩展名)。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The name of the main application (without file extension). + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetMainAppName(string mainAppName) { if (string.IsNullOrWhiteSpace(mainAppName)) @@ -281,11 +285,11 @@ public ConfiginfoBuilder SetMainAppName(string mainAppName) } /// - /// 设置客户端应用程序的当前版本号。 + /// Sets the current version number of the client application. /// - /// 客户端应用程序的当前版本号(应为语义化版本格式)。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The current version number of the client application (should follow semantic versioning format). + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetClientVersion(string clientVersion) { if (string.IsNullOrWhiteSpace(clientVersion)) @@ -296,12 +300,12 @@ public ConfiginfoBuilder SetClientVersion(string clientVersion) } /// - /// 设置升级客户端程序的当前版本号。 - /// 用于实现更新器自身的独立版本管理。 + /// Sets the current version number of the upgrade client program. + /// Used for independent version management of the updater itself. /// - /// 升级应用程序的当前版本号。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The current version number of the upgrade application. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetUpgradeClientVersion(string upgradeClientVersion) { if (string.IsNullOrWhiteSpace(upgradeClientVersion)) @@ -312,11 +316,11 @@ public ConfiginfoBuilder SetUpgradeClientVersion(string upgradeClientVersion) } /// - /// 设置用于身份验证的应用程序密钥。 + /// Sets the application secret key used for authentication. /// - /// 用于更新请求身份验证的应用程序密钥。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The application secret key used for authenticating update requests. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetAppSecretKey(string appSecretKey) { if (string.IsNullOrWhiteSpace(appSecretKey)) @@ -327,12 +331,12 @@ public ConfiginfoBuilder SetAppSecretKey(string appSecretKey) } /// - /// 设置唯一产品标识符。 - /// 用于在共享同一更新服务器的多个产品之间进行区分。 + /// Sets the unique product identifier. + /// Used to distinguish between multiple products sharing the same update server. /// - /// 唯一的应用程序产品标识符。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The unique application product identifier. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetProductId(string productId) { if (string.IsNullOrWhiteSpace(productId)) @@ -343,11 +347,11 @@ public ConfiginfoBuilder SetProductId(string productId) } /// - /// 设置应用程序文件的安装路径。 + /// Sets the installation path for the application files. /// - /// 应用程序文件所在的安装目录路径。 - /// 当前 实例,支持链式调用。 - /// 为 null、空字符串或仅含空白字符时抛出。 + /// The installation directory path for the application files. + /// The current instance for chaining. + /// Thrown when is null, empty, or consists only of whitespace. public ConfiginfoBuilder SetInstallPath(string installPath) { if (string.IsNullOrWhiteSpace(installPath)) @@ -358,11 +362,11 @@ public ConfiginfoBuilder SetInstallPath(string installPath) } /// - /// 设置更新日志网页的 URL 地址。 + /// Sets the URL of the update log web page. /// - /// 用于查看更新日志的网页 URL。 - /// 当前 实例,支持链式调用。 - /// 不为空但格式不是有效的绝对 URI 时抛出。 + /// The web page URL for viewing the update log. + /// The current instance for chaining. + /// Thrown when is not empty but is not a valid absolute URI. public ConfiginfoBuilder SetUpdateLogUrl(string updateLogUrl) { if (!string.IsNullOrWhiteSpace(updateLogUrl) && !Uri.IsWellFormedUriString(updateLogUrl, UriKind.Absolute)) @@ -373,11 +377,11 @@ public ConfiginfoBuilder SetUpdateLogUrl(string updateLogUrl) } /// - /// 设置用于报告更新状态和结果的 API 端点 URL。 + /// Sets the API endpoint URL for reporting update status and results. /// - /// 用于报告更新状态和结果的 API 端点 URL。 - /// 当前 实例,支持链式调用。 - /// 不为空但格式不是有效的绝对 URI 时抛出。 + /// The API endpoint URL for reporting update status and results. + /// The current instance for chaining. + /// Thrown when is not empty but is not a valid absolute URI. public ConfiginfoBuilder SetReportUrl(string reportUrl) { if (!string.IsNullOrWhiteSpace(reportUrl) && !Uri.IsWellFormedUriString(reportUrl, UriKind.Absolute)) @@ -388,10 +392,10 @@ public ConfiginfoBuilder SetReportUrl(string reportUrl) } /// - /// 设置需要在更新前终止的冲突进程名称。 + /// Sets the name of the conflicting process to terminate before the update. /// - /// 应在开始更新前终止的进程名称。 - /// 当前 实例,支持链式调用。 + /// The process name to terminate before starting the update. + /// The current instance for chaining. public ConfiginfoBuilder SetBowl(string bowl) { _bowl = bowl; @@ -399,11 +403,11 @@ public ConfiginfoBuilder SetBowl(string bowl) } /// - /// 设置驱动程序文件所在的目录路径。 - /// 用于驱动更新功能(Drive 模式)。 + /// Sets the directory path containing driver files. + /// Used for driver-based update functionality (Drive mode). /// - /// 包含驱动程序文件的目录路径。 - /// 当前 实例,支持链式调用。 + /// The directory path containing the driver files. + /// The current instance for chaining. public ConfiginfoBuilder SetDriverDirectory(string driverDirectory) { _driverDirectory = driverDirectory; @@ -411,10 +415,10 @@ public ConfiginfoBuilder SetDriverDirectory(string driverDirectory) } /// - /// 设置应从更新过程中排除的黑名单文件列表。 + /// Sets the list of blacklisted files to exclude from the update process. /// - /// 应被排除的特定文件列表。 - /// 当前 实例,支持链式调用。 + /// The list of specific files to be excluded. + /// The current instance for chaining. public ConfiginfoBuilder SetBlackFiles(List blackFiles) { _blackFiles = blackFiles ?? new List(); @@ -422,10 +426,10 @@ public ConfiginfoBuilder SetBlackFiles(List blackFiles) } /// - /// 设置应从更新过程中排除的黑名单文件格式扩展名列表。 + /// Sets the list of blacklisted file format extensions to exclude from the update process. /// - /// 应被排除的文件扩展名列表(例如 ".log"、".tmp")。 - /// 当前 实例,支持链式调用。 + /// The list of file extensions to be excluded (e.g., ".log", ".tmp"). + /// The current instance for chaining. public ConfiginfoBuilder SetBlackFormats(List blackFormats) { _blackFormats = blackFormats ?? new List(); @@ -433,10 +437,10 @@ public ConfiginfoBuilder SetBlackFormats(List blackFormats) } /// - /// 设置应在更新过程中跳过的目录路径列表。 + /// Sets the list of directory paths to skip during the update process. /// - /// 应在更新期间跳过的目录路径列表。 - /// 当前 实例,支持链式调用。 + /// The list of directory paths to skip during the update. + /// The current instance for chaining. public ConfiginfoBuilder SetSkipDirectorys(List skipDirectorys) { _skipDirectorys = skipDirectorys ?? new List(); @@ -444,22 +448,23 @@ public ConfiginfoBuilder SetSkipDirectorys(List skipDirectorys) } /// - /// 构建并返回一个完整的 对象,包含所有已配置和默认的值。 + /// Builds and returns a complete object containing all configured and default values. /// /// /// - /// 此方法是建造者模式的最终步骤。它会将所有配置的值(包括显式设置的和使用默认值的) - /// 组装到新的 实例中。 + /// This method is the final step of the builder pattern. It assembles all configured values + /// (both explicitly set and defaulted) into a new instance. /// /// - /// 构建完成后会自动调用 方法进行完整性校验。 - /// 如果校验失败,校验异常将被包装为 重新抛出。 + /// After building, it automatically calls to validate the + /// configuration. If validation fails, the validation exception is wrapped in an + /// and rethrown. /// /// - /// 一个完全配置的 实例。 + /// A fully configured instance. /// - /// 当生成的配置对象未通过 校验时抛出, - /// 内部异常包含具体的校验失败原因。 + /// Thrown when the generated configuration object fails validation. + /// The inner exception contains the specific validation failure details. /// public Configinfo Build() { diff --git a/src/c#/GeneralUpdate.Core/Configuration/ConfigurationMapper.cs b/src/c#/GeneralUpdate.Core/Configuration/ConfigurationMapper.cs index 4e8859fa..10d523ec 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/ConfigurationMapper.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/ConfigurationMapper.cs @@ -5,35 +5,38 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 提供配置对象之间的集中映射工具方法。 - /// 确保 - /// 之间的字段映射保持一致,降低在维护过程中遗漏或错误映射字段的风险。 + /// Provides centralized mapping utility methods between configuration objects. + /// Ensures consistent field mapping across , , + /// and , reducing the risk of missed or incorrectly mapped fields during maintenance. /// /// /// - /// ConfigurationMapper 是更新流程中配置转换的核心枢纽,负责以下两个方向的映射: + /// ConfigurationMapper is the central hub for configuration transformation in the update workflow, + /// responsible for mapping in two directions: /// /// /// /// /// - /// :将用户提供的 - /// 映射为内部运行时配置 。此映射在更新流程初始化阶段执行, - /// 将外部 API 的配置参数传递到内部工作流。 + /// : Maps the user-provided + /// to the internal runtime configuration . This mapping is + /// performed during the update workflow initialization phase, passing external API configuration + /// parameters into the internal workflow. /// /// /// /// - /// :将内部运行时配置 - /// 映射为进程间通信参数 。此映射在客户端准备启动升级进程时执行, - /// 将计算后的所有运行时状态序列化后传递给升级进程。 + /// : Maps the internal runtime configuration + /// to the inter-process communication parameters + /// . This mapping is performed when the client is about to launch + /// the upgrade process, serializing all computed runtime state for the upgrade process. /// /// /// /// /// - /// 通过将所有映射逻辑集中在此类中,避免了在引导代码(Bootstrap)各处分散的字段赋值逻辑, - /// 从而简化了维护工作并降低了引入缺陷的可能性。 + /// By centralizing all mapping logic in this class, scattered field assignment logic throughout the + /// bootstrap code is avoided, simplifying maintenance and reducing the likelihood of introducing defects. /// /// /// @@ -43,29 +46,29 @@ namespace GeneralUpdate.Core.Configuration public static class ConfigurationMapper { /// - /// 将用户提供的配置 () 映射到内部运行时配置 - /// ()。 - /// 对所有共享的配置属性执行一对一的字段映射。 + /// Maps the user-provided configuration () to the internal runtime configuration + /// (). + /// Performs one-to-one field mapping for all shared configuration properties. /// /// /// - /// 此方法执行浅拷贝映射,将 中的所有公共属性和基类属性 - /// 逐一赋值到 实例中。 + /// This method performs a shallow copy mapping, assigning all public and base class properties from + /// to a instance one by one. /// /// - /// 如果 null,将自动创建一个新的 - /// 实例。如果 null, - /// 则直接返回空的(或新创建的)目标实例,不会抛出异常。 + /// If is null, a new instance is + /// automatically created. If is null, the method returns the empty + /// (or newly created) target instance without throwing an exception. /// /// /// - /// 包含初始设置的用户提供配置对象。可以为 null。 + /// The user-provided configuration object containing initial settings. Can be null. /// /// - /// 待填充的内部配置对象。如果为 null,将自动创建新实例。 + /// The internal configuration object to populate. If null, a new instance is automatically created. /// /// - /// 一个填充了来自 的配置值的 实例。 + /// A instance populated with configuration values from . /// public static GlobalConfigInfo MapToGlobalConfigInfo(Configinfo source, GlobalConfigInfo target = null) { @@ -104,61 +107,63 @@ public static GlobalConfigInfo MapToGlobalConfigInfo(Configinfo source, GlobalCo } /// - /// 将内部运行时配置 () 映射到进程间通信参数 - /// ()。 + /// Maps the internal runtime configuration () to inter-process communication + /// parameters (). /// /// /// - /// 此方法将之前分散在引导代码各处的复杂参数传递逻辑集中到一处管理。 - /// 生成的 对象将被序列化为 JSON 字符串, - /// 通过命令行参数或标准输入传递给升级进程。 + /// This method centralizes the complex parameter passing logic that was previously scattered across + /// the bootstrap code. The resulting object is serialized to a JSON string + /// and passed to the upgrade process via command-line arguments or standard input. /// /// - /// 映射过程中需要注意以下几点: + /// The following points should be noted during mapping: /// /// /// - /// MainAppName 映射到 ProcessInfo.AppName(字段名不同以保持向后兼容)。 + /// MainAppName maps to ProcessInfo.AppName (different field name for backward compatibility). /// /// /// /// - /// ClientVersion 映射到 ProcessInfo.CurrentVersion。 + /// ClientVersion maps to ProcessInfo.CurrentVersion. /// /// /// /// - /// 压缩编码 () 和压缩格式 () 是管道阶段计算后的值。 + /// The compression encoding () and compression format () + /// are values computed during the pipeline stage. /// /// /// /// - /// 黑名单参数来自 BlackListManager,在映射过程中作为独立参数传入。 + /// Blacklist parameters come from BlackListManager and are passed as separate parameters + /// during mapping. /// /// /// /// /// /// - /// 包含所有运行时状态的内部配置对象。不能为 null。 + /// The internal configuration object containing all runtime state. Must not be null. /// /// - /// 来自更新服务器响应的版本信息对象列表。 + /// The list of version information objects from the update server response. /// /// - /// 来自 BlackListManager 的黑名单文件格式列表。 + /// The list of blacklisted file formats from BlackListManager. /// /// - /// 来自 BlackListManager 的黑名单文件列表。 + /// The list of blacklisted files from BlackListManager. /// /// - /// 来自 BlackListManager 的需跳过目录列表。 + /// The list of directories to skip from BlackListManager. /// /// - /// 一个准备序列化用于进程间通信的 对象。 + /// A object ready for serialization and inter-process communication. /// /// - /// 当 null 时抛出。 + /// Thrown when is null. /// public static ProcessInfo MapToProcessInfo( GlobalConfigInfo source, diff --git a/src/c#/GeneralUpdate.Core/Configuration/GlobalConfigInfo.cs b/src/c#/GeneralUpdate.Core/Configuration/GlobalConfigInfo.cs index 9609536a..c56507e3 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/GlobalConfigInfo.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/GlobalConfigInfo.cs @@ -5,34 +5,35 @@ namespace GeneralUpdate.Core.Configuration; /// -/// 内部运行时配置类,将用户提供的配置与计算后的运行时状态相结合。 -/// 作为更新工作流执行过程中的核心配置持有者,同时继承 -/// 的公共字段并添加运行时特有的属性。 +/// Internal runtime configuration class that combines user-provided configuration with computed runtime state. +/// Serves as the central configuration holder during the update workflow, inheriting common fields from +/// and adding runtime-specific properties. /// /// /// -/// GlobalConfigInfo 是更新流程的内部枢纽配置对象。它通过 -/// -/// 映射而来,并在更新管道的各个阶段(下载、解压、差异更新等)被逐步填充运行时计算值。 +/// GlobalConfigInfo is the internal hub configuration object for the update workflow. It is mapped +/// from via and is +/// progressively populated with runtime-computed values during the various stages of the update pipeline +/// (download, extraction, differential update, etc.). /// /// -/// 主要职责包括: +/// Its primary responsibilities include: /// /// -/// 存储用户提供的配置(继承自 +/// Storing user-provided configuration (inherited from ) /// /// -/// 维护计算后的运行时值(编码、格式、路径、版本号等) +/// Maintaining computed runtime values (encoding, format, paths, version numbers, etc.) /// /// -/// 跟踪更新工作流状态( +/// Tracking the update workflow state (, ) /// /// /// /// -/// 在更新管道执行完毕后,GlobalConfigInfo 会通过 -/// 映射为 , -/// 序列化为 JSON 后通过 IPC 传递给升级进程。 +/// After the update pipeline completes, GlobalConfigInfo is mapped to via +/// , serialized to JSON, and passed to the upgrade process +/// through IPC. /// /// /// @@ -46,156 +47,154 @@ public class GlobalConfigInfo : BaseConfigInfo // ────────────────────────────── /// - /// 用于检查可用更新的 API 端点 URL。 - /// 继承自用户配置,用于向服务器发起版本校验请求。 + /// The API endpoint URL used to check for available updates. + /// Inherited from user configuration, used to send version check requests to the server. /// /// - /// 此值由 从 - /// 映射而来。 + /// This value is mapped from by + /// . /// public string UpdateUrl { get; set; } /// - /// 升级程序(更新器自身)的当前版本号。 - /// 与 分开管理,用于实现更新器的独立升级。 + /// The current version number of the updater (the update client itself). + /// Managed separately from to enable independent updater upgrades. /// /// - /// 通过比较此版本号与服务端响应,可以确定 的值。 + /// Comparing this version number against the server response determines the value of . /// public string UpgradeClientVersion { get; set; } /// - /// 当前应用程序的唯一产品标识符。 - /// 用于在共享同一更新服务器的多个产品之间进行区分。 + /// The unique product identifier for the current application. + /// Used to distinguish between multiple products sharing the same update server. /// public string ProductId { get; set; } // ────────────────────────────── - // 运行时计算字段(在管道执行过程中计算) + // Runtime-computed fields (calculated during pipeline execution) // ────────────────────────────── /// - /// 更新包使用的压缩格式。 - /// 从 计算得出,默认为 ZIP。 + /// The compression format used for update packages. + /// Computed from , defaults to ZIP. /// public Format Format { get; set; } /// - /// 指示升级程序(更新器自身)是否需要更新。 - /// 通过比较 与服务端返回的最新版本号计算得出。 + /// Indicates whether the updater itself needs to be updated. + /// Computed by comparing against the latest version from the server. /// public bool IsUpgradeUpdate { get; set; } /// - /// 指示主应用程序是否需要更新。 - /// 通过比较 与服务端返回的最新版本号计算得出。 + /// Indicates whether the main application needs to be updated. + /// Computed by comparing against the latest version from the server. /// public bool IsMainUpdate { get; set; } /// - /// 升级过程完成后是否启动客户端应用程序。 - /// 默认为 true。在静默模式下可设置为 false, - /// 由调用方自行控制重启时机。 + /// Whether to launch the client application after the update completes. + /// Defaults to true. Set to false in silent mode to let the caller control the restart timing. /// public bool LaunchClientAfterUpdate { get; set; } = true; /// - /// 需要更新的版本信息对象列表。 - /// 根据 的取值, - /// 从更新服务器响应中填充。 + /// The list of version information objects to be updated. + /// Populated from the update server response based on and . /// public List UpdateVersions { get; set; } /// - /// 文件操作所使用的编码格式(例如 UTF-8、ASCII)。 - /// 从 计算得出,默认为系统默认编码。 + /// The text encoding used for file operations (e.g., UTF-8, ASCII). + /// Computed from , defaults to the system default encoding. /// public Encoding Encoding { get; set; } /// - /// 下载操作的超时时间(秒)。 - /// 从 计算得出,默认为 60 秒。 + /// The download operation timeout in seconds. + /// Computed from , defaults to 60 seconds. /// public int DownloadTimeOut { get; set; } /// - /// 更新服务器上的最新可用版本号。 - /// 在版本校验完成后从服务器响应体中提取。 + /// The latest available version number on the update server. + /// Extracted from the server response body after the version check completes. /// public string LastVersion { get; set; } /// - /// 用于下载和提取更新文件的临时目录路径。 - /// 通过 StorageManager.GetTempDirectory("main_temp") 计算得出。 + /// The temporary directory path used for downloading and extracting update files. + /// Computed via StorageManager.GetTempDirectory("main_temp"). /// public string TempPath { get; set; } /// - /// 序列化为 JSON 字符串的 对象。 - /// 包含升级进程所需的所有参数,用于进程间通信(IPC)。 + /// The object serialized to a JSON string. + /// Contains all parameters required by the upgrade process, used for inter-process communication (IPC). /// public string ProcessInfo { get; set; } /// - /// 是否启用差异补丁更新。 - /// 从 计算得出,默认为 true。 + /// Whether differential patch updates are enabled. + /// Computed from , defaults to true. /// public bool? PatchEnabled { get; set; } /// - /// 在应用更新前是否备份当前版本。 - /// 从 计算得出,默认为 true。 + /// Whether to back up the current version before applying an update. + /// Computed from , defaults to true. /// public bool? BackupEnabled { get; set; } /// - /// 当前版本文件在更新前备份到的目录路径。 - /// 通过组合 和带版本号的目录名计算得出。 + /// The directory path where the current version files are backed up before the update. + /// Computed by combining with a version-specific directory name. /// public string BackupDirectory { get; set; } // ────────────────────────────── - // 下载/更新行为选项(从 UpdateOptions 注入) + // Download/Update behavior options (injected from UpdateOptions) // ────────────────────────────── /// - /// 最大并发下载操作数。 - /// 从 计算得出,默认为 2。 + /// The maximum number of concurrent download operations. + /// Computed from , defaults to 2. /// /// - /// 有效范围为 1 到 * 2。 - /// 超出此范围的取值会被自动限制到边界值。 + /// The valid range is 1 to * 2. + /// Values outside this range are automatically clamped to the boundary. /// public int MaxConcurrency { get; set; } = 2; /// - /// 是否通过 HTTP Range 请求支持断点续传。 - /// 从 计算得出,默认为 true。 + /// Whether HTTP Range requests are supported for resumable downloads. + /// Computed from , defaults to true. /// public bool EnableResume { get; set; } = true; /// - /// 下载失败时的最大重试次数。 - /// 从 计算得出,默认为 3。 + /// The maximum number of retry attempts on download failure. + /// Computed from , defaults to 3. /// public int RetryCount { get; set; } = 3; /// - /// 指数退避策略的初始重试间隔。 - /// 从 计算得出,默认为 1 秒。 + /// The initial retry interval for the exponential backoff strategy. + /// Computed from , defaults to 1 second. /// public TimeSpan RetryInterval { get; set; } = TimeSpan.FromSeconds(1); /// - /// 下载完成后是否执行 SHA256 校验和验证。 - /// 从 计算得出,默认为 true。 + /// Whether to perform SHA256 checksum verification after download. + /// Computed from , defaults to true. /// public bool VerifyChecksum { get; set; } = true; /// - /// 差异/补丁生成模式 — 串行 () 或并行 - /// ()。 - /// 从 计算得出,默认为 。 + /// The diff/patch generation mode — serial () or parallel + /// (). + /// Computed from , defaults to . /// public DiffMode DiffMode { get; set; } = DiffMode.Serial; } diff --git a/src/c#/GeneralUpdate.Core/Configuration/ProcessInfo.cs b/src/c#/GeneralUpdate.Core/Configuration/ProcessInfo.cs index 3e42e321..53138a44 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/ProcessInfo.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/ProcessInfo.cs @@ -7,56 +7,60 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 进程间通信(IPC)参数对象。 - /// 此对象被序列化为 JSON 字符串,通过进程参数传递给升级进程, - /// 使独立的升级应用程序能够使用正确的配置执行更新操作。 + /// Inter-process communication (IPC) parameter object. + /// This object is serialized to a JSON string and passed to the upgrade process via process arguments, + /// enabling the standalone upgrade application to execute the update operation with the correct configuration. /// /// /// - /// ProcessInfo 是更新流程中客户端进程与升级进程之间的数据传输契约。 - /// 其生命周期如下: + /// ProcessInfo is the data transfer contract between the client process and the upgrade process + /// in the update workflow. Its lifecycle is as follows: /// /// /// - /// 在客户端更新管道的最后阶段,由 - /// 从 - /// 映射创建。 + /// Created at the end of the client update pipeline by + /// from + /// . /// /// /// /// - /// 序列化为 JSON 字符串,存储在 属性中。 + /// Serialized to a JSON string and stored in the property. /// /// /// /// - /// 客户端启动升级进程时,将 JSON 字符串通过命令行参数传递。 + /// When the client launches the upgrade process, the JSON string is passed via command-line arguments. /// /// /// /// - /// 升级进程反序列化 JSON 字符串,恢复 ProcessInfo 对象,并使用其中的配置执行更新。 + /// The upgrade process deserializes the JSON string, reconstructs the ProcessInfo object, + /// and uses its configuration to perform the update. /// /// /// /// /// - /// 设计说明: + /// Design notes: /// /// /// - /// 所有字段均使用 注解以确保序列化名称一致。 + /// All fields are annotated with to ensure consistent + /// serialization names. /// /// /// /// - /// 构造函数会执行参数校验,确保升级进程收到的配置是合法有效的。 + /// The constructor performs parameter validation to ensure the upgrade process receives a valid + /// configuration. /// /// /// /// - /// 某些字段的名称与其他配置类略有不同,这是为了保持与早期版本的 JSON 序列化向后兼容。 - /// 例如:AppName 对应 MainAppNameCurrentVersion 对应 ClientVersion。 + /// Some field names differ slightly from other configuration classes to maintain JSON serialization + /// backward compatibility with earlier versions. For example: AppName corresponds to + /// MainAppName, CurrentVersion corresponds to ClientVersion. /// /// /// @@ -68,86 +72,86 @@ namespace GeneralUpdate.Core.Configuration public class ProcessInfo { /// - /// 默认无参构造函数,用于 JSON 反序列化。 + /// Default parameterless constructor for JSON deserialization. /// /// - /// 在使用 - /// 反序列化 ProcessInfo 的 JSON 字符串时需要此构造函数。 + /// Required when deserializing a ProcessInfo JSON string using + /// . /// public ProcessInfo() { } /// - /// 带参数校验的构造函数,用于创建 ProcessInfo 实例。 - /// 所有参数均经过校验,确保升级进程接收到有效的配置。 + /// Parameterized constructor with validation for creating a ProcessInfo instance. + /// All parameters are validated to ensure the upgrade process receives a valid configuration. /// /// /// - /// 构造函数会对以下参数进行非空或有效性校验: + /// The constructor validates the following parameters for null or validity: /// /// - /// :不能为 null + /// : Must not be null /// /// - /// :不能为 null,且路径必须存在 + /// : Must not be null, and the directory must exist /// /// - /// :不能为 null + /// : Must not be null /// /// - /// :不能为 null + /// : Must not be null /// /// - /// :必须大于等于 0 + /// : Must be greater than or equal to 0 /// /// - /// :不能为 null + /// : Must not be null /// /// - /// :不能为 null 或空集合 + /// : Must not be null or an empty collection /// /// - /// :不能为 null + /// : Must not be null /// /// - /// :不能为 null + /// : Must not be null /// /// /// /// /// - /// 更新完成后要启动的应用程序名称(从 映射而来)。 + /// The application name to launch after the update completes (mapped from ). /// - /// 安装目录路径(必须存在)。 - /// 更新前的当前版本号。 - /// 更新后的目标版本号。 - /// 查看更新日志的 URL。 - /// 压缩文件所使用的编码。 - /// 压缩格式(ZIP、7Z 等)的扩展名字符串。 - /// 下载超时时间(秒),必须大于 0。 - /// 用于身份验证的应用程序密钥。 - /// 要更新的版本信息列表,不能为空。 - /// 用于报告更新状态的 URL。 - /// 备份文件的目录路径。 - /// 更新前需要终止的进程名称。 - /// 更新请求的 URL 方案。 - /// 身份验证令牌。 - /// 驱动程序文件所在的目录路径。 - /// 客户端下载更新包所在的临时目录路径。 - /// 需要跳过的文件格式扩展名列表。 - /// 需要跳过的特定文件列表。 - /// 需要跳过的目录列表。 - /// 升级可执行文件所在的目录(可选,默认使用 )。 - /// 升级完成后是否启动客户端应用程序(可选,默认为 true)。 + /// The installation directory path (must exist). + /// The current version number before the update. + /// The target version number after the update. + /// The URL for viewing the update log. + /// The text encoding used for compression. + /// The compression format extension string (e.g., ZIP, 7Z). + /// The download timeout in seconds, must be greater than 0. + /// The application secret key used for authentication. + /// The list of version information to update; must not be empty. + /// The URL for reporting update status. + /// The directory path for backup files. + /// The process name to terminate before the update. + /// The URL scheme for update requests. + /// The authentication token. + /// The directory path containing driver files. + /// The temporary directory path where the client downloaded the update package. + /// The list of file format extensions to skip. + /// The list of specific files to skip. + /// The list of directories to skip. + /// The directory where the upgrade executable resides (optional; defaults to ). + /// Whether to launch the client application after the update completes (optional; defaults to true). /// - /// 当 、 - /// 、 - /// 或 - /// null 时抛出。 + /// Thrown when , , + /// , , + /// , , or + /// is null. /// /// - /// 当 指向的目录不存在、 - /// 小于 0 或 - /// 为 null 或空集合时抛出。 + /// Thrown when points to a non-existent directory, + /// is less than 0, or + /// is null or an empty collection. /// public ProcessInfo(string appName , string installPath @@ -172,7 +176,7 @@ public ProcessInfo(string appName , string upgradePath = null , bool launchClient = true) { - // 校验必填字符串参数 + // Validate required string parameters AppName = appName ?? throw new ArgumentNullException(nameof(appName)); if (!Directory.Exists(installPath)) throw new ArgumentException($"{nameof(installPath)} path does not exist ! {installPath}."); InstallPath = installPath ?? throw new ArgumentNullException(nameof(installPath)); @@ -180,194 +184,192 @@ public ProcessInfo(string appName LastVersion = lastVersion ?? throw new ArgumentNullException(nameof(lastVersion)); UpdateLogUrl = updateLogUrl; - // 校验并设置压缩参数 + // Validate and set compression parameters CompressEncoding = compressEncoding.WebName; CompressFormat = compressFormat; if (downloadTimeOut < 0) throw new ArgumentException("Timeout must be greater than 0 !"); DownloadTimeOut = downloadTimeOut; - // 校验认证参数 + // Validate authentication parameters AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); - // 校验更新版本集合 + // Validate update version collection if (updateVersions == null || updateVersions.Count == 0) throw new ArgumentException("Collection cannot be null or has 0 elements !"); UpdateVersions = updateVersions; - // 设置报告和备份参数 + // Set report and backup parameters ReportUrl = reportUrl ?? throw new ArgumentNullException(nameof(reportUrl)); BackupDirectory = backupDirectory ?? throw new ArgumentNullException(nameof(backupDirectory)); - // 设置可选参数 + // Set optional parameters Bowl = bowl; Scheme = scheme; Token = token; DriverDirectory = driverDirectory; TempPath = tempPath; - // 设置黑名单参数 + // Set blacklist parameters BlackFileFormats = blackFileFormats; BlackFiles = blackFiles; SkipDirectorys = skipDirectories; - // 设置升级路径(可选 — 未设置时默认使用 InstallPath) + // Set upgrade path (optional — defaults to InstallPath when not set) UpdatePath = upgradePath; - // 设置启动标志(默认 true — 向后兼容) + // Set launch flag (defaults to true for backward compatibility) LaunchClientAfterUpdate = launchClient; } /// - /// 更新完成后要启动的应用程序名称。 - /// 注意:在 ProcessInfo 中,此字段存储的是其他配置类中的 - /// MainAppName 值。 + /// The name of the application to launch after the update completes. + /// Note: In ProcessInfo, this field stores the MainAppName value from other configuration classes. /// /// - /// JSON 序列化名称为 "UpdateAppName",这是为了保持与早期版本 - /// 的序列化格式向后兼容。 + /// The JSON serialization name is "UpdateAppName" to maintain backward compatibility with + /// the serialization format of earlier versions. /// [JsonPropertyName("UpdateAppName")] public string AppName { get; set; } /// - /// 文件将被更新到的安装目录。 - /// 所有更新操作均相对于此路径执行。 + /// The installation directory where files will be updated. + /// All update operations are performed relative to this path. /// /// - /// 构造函数会校验此路径必须存在,否则抛出 。 + /// The constructor validates that this directory exists; otherwise, it throws . /// [JsonPropertyName("InstallPath")] public string InstallPath { get; set; } /// - /// 更新前应用程序的当前版本号。 - /// 注意:此值映射自 。 + /// The current version number of the application before the update. + /// Note: This value is mapped from . /// [JsonPropertyName("CurrentVersion")] public string CurrentVersion { get; set; } /// - /// 更新完成后的目标版本号。 - /// 即更新服务器上可用的最新版本号。 + /// The target version number after the update completes. + /// This is the latest available version on the update server. /// [JsonPropertyName("LastVersion")] public string LastVersion { get; set; } /// - /// 用户可查看详细更新日志和变更记录的 URL。 + /// The URL where users can view detailed update logs and change records. /// [JsonPropertyName("UpdateLogUrl")] public string UpdateLogUrl { get; set; } /// - /// 用于压缩/解压缩更新包的文本编码。 - /// 以 字符串形式存储(例如 "utf-8"、"ascii")。 + /// The text encoding used for compressing/decompressing update packages. + /// Stored as an string (e.g., "utf-8", "ascii"). /// [JsonPropertyName("CompressEncoding")] public string CompressEncoding { get; set; } /// - /// 更新包的压缩格式(例如 "ZIP"、"7Z")。 + /// The compression format of the update package (e.g., "ZIP", "7Z"). /// [JsonPropertyName("CompressFormat")] public string CompressFormat { get; set; } /// - /// 下载更新包的超时时间(秒)。 - /// 如果下载操作超过此时间将被视为失败。 + /// The timeout for downloading the update package, in seconds. + /// If the download operation exceeds this time, it is considered failed. /// [JsonPropertyName("DownloadTimeOut")] public int DownloadTimeOut { get; set; } /// - /// 用于更新请求身份验证的应用程序密钥。 + /// The application secret key used for authenticating update requests. /// [JsonPropertyName("AppSecretKey")] public string AppSecretKey { get; set; } /// - /// 描述所有待更新版本的版本信息对象列表。 - /// 对于增量更新可包含多个版本条目。 + /// The list of version information objects describing all pending updates. + /// May contain multiple version entries for incremental updates. /// [JsonPropertyName("UpdateVersions")] public List UpdateVersions { get; set; } /// - /// 用于报告更新进度和完成状态的 API 端点 URL。 + /// The API endpoint URL for reporting update progress and completion status. /// [JsonPropertyName("ReportUrl")] public string ReportUrl { get; set; } /// - /// 更新前当前版本文件备份到的目录路径。 - /// 在更新失败时用于回滚操作。 + /// The directory path where current version files are backed up before the update. + /// Used for rollback in case of update failure. /// [JsonPropertyName("BackupDirectory")] public string BackupDirectory { get; set; } /// - /// 在开始更新前应被终止的进程名称。 - /// 通常用于关闭可能产生文件锁定的冲突后台进程。 + /// The name of the process to terminate before starting the update. + /// Typically used to shut down conflicting background processes that may hold file locks. /// [JsonPropertyName("Bowl")] public string Bowl { get; set; } /// - /// 与更新服务器通信的 URL 方案(例如 "http"、"https")。 + /// The URL scheme used for communicating with the update server (e.g., "http", "https"). /// [JsonPropertyName("Scheme")] public string Scheme { get; set; } /// - /// 包含在 HTTP 请求头中的身份验证令牌,用于 API 请求。 + /// The authentication token included in HTTP request headers for API requests. /// [JsonPropertyName("Token")] public string Token { get; set; } /// - /// 应从更新中排除的文件格式扩展名列表。 - /// 注意:此处的属性名称(BlackFileFormats)与其他配置类 - /// (BlackFormats)不同,以保持 JSON 序列化的向后兼容性。 + /// The list of file format extensions to exclude from the update. + /// Note: The property name here (BlackFileFormats) differs from other configuration classes + /// (BlackFormats) to maintain JSON serialization backward compatibility. /// [JsonPropertyName("BlackFileFormats")] public List BlackFileFormats { get; set; } /// - /// 应从更新中排除的特定文件名称列表。 + /// The list of specific file names to exclude from the update. /// [JsonPropertyName("BlackFiles")] public List BlackFiles { get; set; } /// - /// 更新操作期间应跳过的目录路径列表。 + /// The list of directory paths to skip during update operations. /// [JsonPropertyName("SkipDirectorys")] public List SkipDirectorys { get; set; } /// - /// 包含驱动程序文件的目录路径,用于驱动更新功能。 - /// 当启用驱动更新时,系统会从此目录定位并安装驱动程序文件。 + /// The directory path containing driver files, used for driver-based update functionality. + /// When driver updates are enabled, the system locates and installs driver files from this directory. /// [JsonPropertyName("DriverDirectory")] public string DriverDirectory { get; set; } /// - /// 客户端下载更新包时使用的临时目录路径。 - /// 升级进程通过更新管道从此路径读取更新包文件。 + /// The temporary directory path used by the client to download update packages. + /// The upgrade process reads update package files from this path via the update pipeline. /// [JsonPropertyName("TempPath")] public string TempPath { get; set; } /// - /// 升级可执行文件所在的目录路径(可选)。 - /// 当设置此值时,升级进程将从 UpdatePath 启动, - /// 而非 。 + /// The directory path where the upgrade executable resides (optional). + /// When set, the upgrade process starts from UpdatePath instead of . /// [JsonPropertyName("UpdatePath")] public string UpdatePath { get; set; } /// - /// 升级过程完成后是否启动客户端应用程序。 - /// 默认为 true。设置为 false 可在更新后保持应用程序停止状态。 + /// Whether to launch the client application after the update completes. + /// Defaults to true. Set to false to keep the application stopped after the update. /// [JsonPropertyName("LaunchClientAfterUpdate")] public bool LaunchClientAfterUpdate { get; set; } = true; diff --git a/src/c#/GeneralUpdate.Core/Configuration/UpdateOption.cs b/src/c#/GeneralUpdate.Core/Configuration/UpdateOption.cs index cfd94e81..ad2573f9 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/UpdateOption.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/UpdateOption.cs @@ -4,22 +4,23 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 强类型更新选项键的基类。 - /// 使用基于 的简单注册表实现, - /// 替代了早期版本的 Netty ConstantPool 模式。 + /// Base class for strongly-typed update option keys. + /// Uses a simple registry pattern backed by , + /// replacing the Netty ConstantPool pattern used in earlier versions. /// /// /// - /// UpdateOption 实现了选项名称的全局注册表,确保每个名称对应唯一的选项实例。 - /// 通过 静态方法获取或创建选项实例。 + /// UpdateOption implements a global registry for option names, ensuring each name maps to a + /// unique option instance. Instances are obtained or created via the + /// static method. /// /// - /// 该类使用双重检查锁定(通过 _lock 对象)来保证线程安全地创建和注册选项实例。 - /// 底层的 _registry 字典使用 - /// 以支持高并发读取。 + /// This class uses double-checked locking (via the _lock object) to ensure thread-safe creation + /// and registration of option instances. The underlying _registry dictionary uses + /// to support high-concurrency reads. /// /// - /// 使用示例: + /// Usage example: /// /// var option = UpdateOption.ValueOf<int>("MAXCONCURRENCY", 3); /// int defaultValue = option.DefaultValue; // 3 @@ -31,45 +32,46 @@ namespace GeneralUpdate.Core.Configuration public class UpdateOption { /// - /// 全局选项注册表,存储所有已创建的选项实例。 - /// 键为选项名称(字符串),值为 实例。 + /// The global option registry storing all created option instances. + /// Keys are option names (strings), values are instances. /// private static readonly ConcurrentDictionary _registry = new(); /// - /// 用于线程安全创建选项的同步锁对象。 + /// The synchronization lock object for thread-safe option creation. /// private static readonly object _lock = new(); /// - /// 选项的唯一名称标识。 - /// 在整个应用程序生命周期中保持唯一。 + /// The unique name identifier for this option. + /// Remains unique throughout the application lifetime. /// public string Name { get; } /// - /// 受保护的构造函数,防止直接实例化。 - /// 应使用 静态方法创建实例。 + /// Protected constructor to prevent direct instantiation. + /// Instances should be created via the static method. /// - /// 选项的唯一名称标识。 - /// null 时抛出。 + /// The unique name identifier for the option. + /// Thrown when is null. protected UpdateOption(string name) { Name = name ?? throw new ArgumentNullException(nameof(name)); } /// - /// 获取或创建指定名称的 实例。 - /// 如果指定名称的选项已存在,则返回现有实例;否则使用提供的默认值创建新实例。 - /// 后续使用相同名称的调用将始终返回同一个实例(单例模式)。 + /// Gets or creates an instance for the specified name. + /// If an option with the specified name already exists, the existing instance is returned; + /// otherwise, a new instance is created with the provided default value. + /// Subsequent calls with the same name will always return the same instance (singleton pattern). /// - /// 选项值的类型。 - /// 选项的唯一名称标识。 - /// 当选项未显式设置时使用的默认值。 + /// The type of the option value. + /// The unique name identifier for the option. + /// The default value used when the option is not explicitly set. /// - /// 与对应的 实例。 + /// The instance corresponding to . /// - /// null 时抛出。 + /// Thrown when is null. public static UpdateOption ValueOf(string name, T defaultValue = default!) { lock (_lock) @@ -84,43 +86,43 @@ public static UpdateOption ValueOf(string name, T defaultValue = default!) } /// - /// 返回选项的名称字符串。 + /// Returns the string representation of the option name. /// - /// 选项的唯一名称标识。 + /// The unique name identifier of the option. public override string ToString() => Name; /// - /// 基于选项名称的哈希码。 + /// Returns the hash code based on the option name. /// - /// 选项名称的哈希码。 + /// The hash code of the option name. public override int GetHashCode() => Name.GetHashCode(); /// - /// 基于选项名称判断两个选项是否相等。 + /// Determines whether two options are equal based on their names. /// - /// 要比较的对象。 + /// The object to compare. /// - /// 如果 且名称相同, - /// 则返回 true;否则返回 false。 + /// true if is an with the same name; + /// otherwise false. /// public override bool Equals(object? obj) => obj is UpdateOption other && Name == other.Name; } /// - /// 带默认值的强类型更新选项键。 - /// 通过 获取实例。 + /// A strongly-typed update option key with a default value. + /// Instances are obtained via . /// - /// 选项值的类型。 + /// The type of the option value. /// /// - /// UpdateOption{T} 是一个密封类,继承自 , - /// 在其基类基础上添加了类型安全的默认值存储。 + /// UpdateOption{T} is a sealed class that inherits from , + /// adding type-safe default value storage on top of the base class. /// /// - /// 此类与 配合使用—— - /// UpdateOption{T} 定义选项的"键"和默认值, - /// UpdateOptionValue{T} 则存储选项的运行时"值"。 + /// This class works together with — + /// UpdateOption{T} defines the option's "key" and default value, + /// while UpdateOptionValue{T} stores the option's runtime "value". /// /// /// @@ -129,15 +131,15 @@ public override bool Equals(object? obj) public sealed class UpdateOption : UpdateOption { /// - /// 当选项未显式设置时使用的默认值。 + /// The default value used when the option is not explicitly set. /// public T DefaultValue { get; } /// - /// 内部构造函数,通过 调用。 + /// Internal constructor called by . /// - /// 选项的唯一名称标识。 - /// 选项的默认值。 + /// The unique name identifier for the option. + /// The default value for the option. internal UpdateOption(string name, T defaultValue) : base(name) { DefaultValue = defaultValue; diff --git a/src/c#/GeneralUpdate.Core/Configuration/UpdateOptionValue.cs b/src/c#/GeneralUpdate.Core/Configuration/UpdateOptionValue.cs index 485d72f0..06d5149f 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/UpdateOptionValue.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/UpdateOptionValue.cs @@ -1,19 +1,21 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 封装具体选项值以便存储在选项字典中的抽象基类。 - /// 提供统一的类型擦除接口,允许不同类型的选项值以多态方式存储在同一个集合中。 + /// Abstract base class that encapsulates a specific option value for storage in an option dictionary. + /// Provides a unified, type-erased interface that allows option values of different types to be stored + /// polymorphically in the same collection. /// /// /// - /// UpdateOptionValue 的抽象基类, - /// 通过非泛型的 方法提供了 - /// 统一访问选项键和值的接口,而无需在调用方感知具体类型。 + /// UpdateOptionValue is the abstract base class for . + /// It provides a unified interface for accessing the option key and value through the non-generic + /// and methods, without requiring the caller to know + /// the concrete type. /// /// - /// 与 的关系:UpdateOption{T} 定义了选项的 - /// "键"(含名称和默认值),而 UpdateOptionValue{T} 存储了该选项的运行时"值"。 - /// 两者通过 属性关联。 + /// Relationship with : UpdateOption{T} defines the option's + /// "key" (with name and default value), while UpdateOptionValue{T} stores the option's + /// runtime "value". The two are linked via the property. /// /// /// @@ -21,32 +23,33 @@ namespace GeneralUpdate.Core.Configuration public abstract class UpdateOptionValue { /// - /// 此值所对应的选项键。 - /// 返回 实例,标识此值属于哪个选项。 + /// The option key that this value corresponds to. + /// Returns the instance identifying which option this value belongs to. /// public abstract UpdateOption Option { get; } /// - /// 以 类型返回存储的值。 - /// 调用方需要自行转换为正确的类型。 + /// Returns the stored value as an . + /// Callers must cast to the correct type themselves. /// - /// 存储的选项值。object 类型,可能需要拆箱或强制类型转换。 + /// The stored option value as an . May require unboxing or type casting. public abstract object GetValue(); } /// - /// 强类型的选项值包装器。 - /// 存储选项的运行时值,并与对应的 键关联。 + /// A strongly-typed option value wrapper. + /// Stores the runtime value of an option and associates it with the corresponding + /// key. /// - /// 选项值的具体类型。 + /// The concrete type of the option value. /// /// - /// UpdateOptionValue{T} 是对运行时选项值的强类型封装。 - /// 它持有一个对 的引用(通过 属性), - /// 以及该选项的当前值(通过 _value 字段)。 + /// UpdateOptionValue{T} is a strongly-typed wrapper around a runtime option value. + /// It holds a reference to the (via the property) + /// and the current value (via the _value field). /// /// - /// 使用示例: + /// Usage example: /// /// var option = UpdateOption.ValueOf<int>("MAXCONCURRENCY", 3); /// var value = new UpdateOptionValue<int>(option, 5); @@ -59,20 +62,20 @@ public abstract class UpdateOptionValue public sealed class UpdateOptionValue : UpdateOptionValue { /// - /// 此值所对应的选项键。 + /// The option key that this value corresponds to. /// public override UpdateOption Option { get; } /// - /// 内部存储的选项值。 + /// The internally stored option value. /// private readonly T _value; /// - /// 使用指定的选项键和值创建 实例。 + /// Creates an instance with the specified option key and value. /// - /// 与此值关联的 选项键。 - /// 要存储的选项值。 + /// The option key associated with this value. + /// The option value to store. public UpdateOptionValue(UpdateOption option, T value) { Option = option; @@ -80,17 +83,17 @@ public UpdateOptionValue(UpdateOption option, T value) } /// - /// 以 类型返回存储的值。 + /// Returns the stored value as an . /// - /// 存储的选项值,类型为 但以 形式返回。 + /// The stored option value of type returned as . public override object GetValue() => _value!; /// - /// 返回值的字符串表示形式。 + /// Returns the string representation of the value. /// /// - /// 如果值不为 null,则返回 _value.ToString(); - /// 否则返回 。 + /// _value.ToString() if the value is not null; + /// otherwise, . /// public override string ToString() => _value?.ToString() ?? string.Empty; } diff --git a/src/c#/GeneralUpdate.Core/Configuration/UpdateOptions.cs b/src/c#/GeneralUpdate.Core/Configuration/UpdateOptions.cs index e434b59a..4197e2e4 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/UpdateOptions.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/UpdateOptions.cs @@ -4,33 +4,35 @@ namespace GeneralUpdate.Core.Configuration { /// - /// 框架级别的更新选项常量定义。 - /// 每个选项均具有唯一的字符串名称和合理的默认值。 + /// Framework-level constant definitions for update options. + /// Each option has a unique string name and a sensible default value. /// /// /// - /// UpdateOptions 是一个静态类,集中定义了更新框架所有可配置选项的 - /// 实例。每个选项包含一个唯一的字符串名称和框架默认值。 + /// UpdateOptions is a static class that centrally defines all configurable options for the update + /// framework as instances. Each option contains a unique string name and + /// a framework-defined default value. /// /// - /// 与业务相关的配置(URL、密钥、应用名称等)不属于此类, - /// 应放置在 中。 - /// 此类仅负责行为层面的选项(如是否启用断点续传、重试次数、并发数等)。 + /// Business-related configuration (URLs, secrets, application names, etc.) does not belong in this class + /// and should be placed in or . This class is + /// concerned only with behavioral options (such as whether to enable resumable downloads, retry count, + /// concurrency level, etc.). /// /// - /// 选项值的存储和检索通过 实现, - /// 该机制基于 - /// 保证每个名称对应唯一的选项实例(单例模式)。 + /// Option values are stored and retrieved via , which + /// uses a to guarantee + /// that each name maps to a unique option instance (singleton pattern). /// /// - /// 各选项的默认值来源: + /// Default values for each option: /// - /// 并发下载数默认为 3( - /// 断点续传默认为启用( - /// 下载超时默认为 30 秒( - /// 差异补丁默认为启用( - /// 更新前备份默认为启用( - /// 静默更新默认为禁用( + /// Concurrent downloads default to 3 () + /// Resumable downloads default to enabled () + /// Download timeout defaults to 30 seconds () + /// Differential patching defaults to enabled () + /// Pre-update backup defaults to enabled () + /// Silent update defaults to disabled () /// /// /// @@ -44,108 +46,108 @@ public static class UpdateOptions // ════════════════════════════════════════ /// - /// 应用程序角色类型 — - /// 或 。 - /// 默认值为 。 + /// The application role type — , , + /// or . + /// Defaults to . /// public static UpdateOption AppType { get; } = UpdateOption.ValueOf("APPTYPE", Configuration.AppType.Client); // ════════════════════════════════════════ - // 差异模式 + // Diff Mode // ════════════════════════════════════════ /// - /// 差异/补丁生成模式 — (串行)或 - /// (并行)。 - /// 默认值为 。 + /// The diff/patch generation mode — (serial) or + /// (parallel). + /// Defaults to . /// public static UpdateOption DiffMode { get; } = UpdateOption.ValueOf("DIFFMODE", Configuration.DiffMode.Serial); // ════════════════════════════════════════ - // 向后兼容选项 + // Backward Compatibility Options // ════════════════════════════════════════ /// - /// 更新包使用的压缩编码。 - /// 默认值为 。 + /// The text encoding used for update package compression. + /// Defaults to . /// public static UpdateOption Encoding { get; } = UpdateOption.ValueOf("COMPRESSENCODING", System.Text.Encoding.UTF8); /// - /// 更新包使用的压缩格式。 - /// 默认值为 。 + /// The compression format used for update packages. + /// Defaults to . /// public static UpdateOption Format { get; } = UpdateOption.ValueOf("COMPRESSFORMAT", Configuration.Format.Zip); /// - /// 下载操作的超时时间(秒)。 - /// 默认值为 30 秒。 + /// The download operation timeout in seconds. + /// Defaults to 30 seconds. /// public static UpdateOption DownloadTimeout { get; } = UpdateOption.ValueOf("DOWNLOADTIMEOUT", 30); /// - /// 是否启用差异补丁更新。 - /// 默认值为 true。 + /// Whether differential patch updates are enabled. + /// Defaults to true. /// public static UpdateOption PatchEnabled { get; } = UpdateOption.ValueOf("PATCH", true); /// - /// 更新前是否启用备份。 - /// 默认值为 true。 + /// Whether pre-update backup is enabled. + /// Defaults to true. /// public static UpdateOption BackupEnabled { get; } = UpdateOption.ValueOf("BACKUP", true); /// - /// 是否启用静默后台更新模式。 - /// 默认值为 false。 + /// Whether silent background update mode is enabled. + /// Defaults to false. /// public static UpdateOption Silent { get; } = UpdateOption.ValueOf("ENABLESILENTUPDATE", false); // ════════════════════════════════════════ - // 静默模式选项 + // Silent Mode Options // ════════════════════════════════════════ /// - /// 静默更新模式下检查更新的轮询间隔(分钟)。 - /// 默认值为 60 分钟。 + /// The polling interval (in minutes) for checking updates in silent mode. + /// Defaults to 60 minutes. /// public static UpdateOption SilentPollIntervalMinutes { get; } = UpdateOption.ValueOf("SILENTPOLLINTERVALMINUTES", 60); // ════════════════════════════════════════ - // 并发与断点续传选项 + // Concurrency and Resume Options // ════════════════════════════════════════ /// - /// 最大并发下载操作数。 - /// 默认值为 3。 + /// The maximum number of concurrent download operations. + /// Defaults to 3. /// public static UpdateOption MaxConcurrency { get; } = UpdateOption.ValueOf("MAXCONCURRENCY", 3); /// - /// 是否启用 HTTP 断点续传(基于 Range 请求头)。 - /// 默认值为 true。 + /// Whether HTTP resumable downloads (based on Range headers) are enabled. + /// Defaults to true. /// public static UpdateOption EnableResume { get; } = UpdateOption.ValueOf("ENABLERESUME", true); // ════════════════════════════════════════ - // 容错与重试选项 + // Fault Tolerance and Retry Options // ════════════════════════════════════════ /// - /// 下载失败时的最大重试次数。 - /// 默认值为 3。 + /// The maximum number of retry attempts on download failure. + /// Defaults to 3. /// public static UpdateOption RetryCount { get; } = UpdateOption.ValueOf("RETRYCOUNT", 3); /// - /// 下载完成后是否执行 SHA256 校验和验证。 - /// 默认值为 true。 + /// Whether to perform SHA256 checksum verification after download. + /// Defaults to true. /// public static UpdateOption VerifyChecksum { get; } = UpdateOption.ValueOf("VERIFYCHECKSUM", true); /// - /// 指数退避策略的初始重试间隔。 - /// 默认值为 1 秒。 + /// The initial retry interval for the exponential backoff strategy. + /// Defaults to 1 second. /// public static UpdateOption RetryInterval { get; } = UpdateOption.ValueOf("RETRYINTERVAL", TimeSpan.FromSeconds(1)); } diff --git a/src/c#/GeneralUpdate.Core/Configuration/VersionInfo.cs b/src/c#/GeneralUpdate.Core/Configuration/VersionInfo.cs index 9d9d7558..23197b7f 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/VersionInfo.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/VersionInfo.cs @@ -4,45 +4,48 @@ namespace GeneralUpdate.Core.Configuration; /// -/// 表示更新服务器返回的版本信息对象。 -/// 对应服务端 JSON 响应的数据结构,包含版本标识、下载地址、更新日志、 -/// 升级模式等所有与单个版本相关的元数据。 +/// Represents a version information object returned by the update server. +/// Corresponds to the data structure of the server-side JSON response, containing all metadata related to +/// a single version, including version identifier, download URL, update log, and upgrade mode. /// /// /// -/// VersionInfo 是客户端与更新服务器之间的数据契约,从版本检查 API 的 -/// JSON 响应中反序列化而来。每个 VersionInfo 实例代表一个可用的更新版本。 +/// VersionInfo is the data contract between the client and the update server, deserialized from +/// the JSON response of the version check API. Each VersionInfo instance represents an available +/// update version. /// /// -/// 此对象在更新流程中的用途: +/// This object is used throughout the update workflow: /// /// /// -/// 版本检查阶段:从服务端获取版本列表,用于判断是否存在新版本。 +/// Version check phase: Retrieves the version list from the server to determine whether a +/// new version exists. /// /// /// /// -/// 更新下载阶段:通过 属性下载更新包。 +/// Update download phase: Downloads the update package via the property. /// /// /// /// -/// 差异更新阶段:利用 进行文件校验, -/// 用于确定差异补丁的作用范围。 +/// Differential update phase: Uses for file verification, and +/// and to determine the scope of the +/// differential patch. /// /// /// /// -/// IPC 传输阶段:作为 的列表元素 -/// 传递给升级进程。 +/// IPC transmission phase: Passed to the upgrade process as list elements in +/// . /// /// /// /// /// -/// 所有属性均使用 注解,映射服务端 JSON 响应的字段名。 -/// 属性大多为可空类型,以容错服务端可能缺失的字段。 +/// All properties are annotated with to map server-side JSON +/// response field names. Most properties are nullable to tolerate potentially missing fields from the server. /// /// /// @@ -50,127 +53,128 @@ namespace GeneralUpdate.Core.Configuration; public class VersionInfo { /// - /// 版本记录的唯一标识符(主键 ID)。 + /// The unique identifier (primary key ID) of the version record. /// [JsonPropertyName("recordId")] public int RecordId { get; set; } /// - /// 版本名称或标签(例如 "v1.0.1"、"Release Candidate 2")。 + /// The version name or label (e.g., "v1.0.1", "Release Candidate 2"). /// [JsonPropertyName("name")] public string? Name { get; set; } /// - /// 更新包文件的哈希值(通常为 SHA256),用于下载后的完整性校验。 + /// The hash value of the update package file (typically SHA256), used for integrity verification after download. /// [JsonPropertyName("hash")] public string? Hash { get; set; } /// - /// 版本的发布日期。 + /// The release date of the version. /// [JsonPropertyName("releaseDate")] public DateTime? ReleaseDate { get; set; } /// - /// 更新包的下载 URL 地址。 - /// 客户端通过此地址下载更新包文件。 + /// The download URL of the update package. + /// The client downloads the update package file from this address. /// [JsonPropertyName("url")] public string? Url { get; set; } /// - /// 此版本信息的版本号字符串(例如 "1.0.0.1")。 + /// The version number string of this version information (e.g., "1.0.0.1"). /// [JsonPropertyName("version")] public string? Version { get; set; } /// - /// 应用程序类型标识,用于区分主应用更新和升级器更新。 + /// The application type identifier, used to distinguish between main application updates and updater updates. /// /// - /// 取值为整数枚举:通常 0 表示主应用(Client),1 表示升级器(Upgrade)。 - /// 与 配合使用,决定此版本是用于 - /// 还是 - /// 。 + /// Takes an integer enum value: typically 0 for the main application (Client) and 1 for the updater (Upgrade). + /// Works with to determine whether this version is used for + /// or . /// [JsonPropertyName("appType")] public int? AppType { get; set; } /// - /// 目标平台标识,用于区分不同操作系统或架构的更新包。 + /// The target platform identifier, used to distinguish update packages for different operating systems or architectures. /// [JsonPropertyName("platform")] public int? Platform { get; set; } /// - /// 此版本关联的产品标识符,用于多产品环境下的版本筛选。 + /// The product identifier associated with this version, used for version filtering in multi-product environments. /// [JsonPropertyName("productId")] public string? ProductId { get; set; } /// - /// 是否强制更新。 - /// 如果为 true,客户端必须执行此更新才能继续使用。 + /// Whether the update is forced. + /// If true, the client must apply this update to continue using the application. /// [JsonPropertyName("isForcibly")] public bool? IsForcibly { get; set; } /// - /// 更新包的压缩格式名称(例如 "zip"、"7z"、"tar.gz")。 + /// The compression format name of the update package (e.g., "zip", "7z", "tar.gz"). /// [JsonPropertyName("format")] public string? Format { get; set; } /// - /// 更新包文件的大小(字节)。 + /// The size of the update package file in bytes. /// [JsonPropertyName("size")] public long? Size { get; set; } /// - /// 下载请求的 HTTP 身份验证方案(例如 "Bearer"、"Basic")。 + /// The HTTP authentication scheme for download requests (e.g., "Bearer", "Basic"). /// /// - /// 当更新包的下载需要额外的身份验证时使用,与服务端配置的鉴权方式对应。 + /// Used when additional authentication is required for downloading the update package, corresponding to the + /// authentication method configured on the server side. /// [JsonPropertyName("authScheme")] public string? AuthScheme { get; set; } /// - /// 下载请求的 HTTP 身份验证令牌。 + /// The HTTP authentication token for download requests. /// /// - /// 配合 使用,在下载更新包时附加到 HTTP 请求头中进行鉴权。 + /// Used in conjunction with , appended to HTTP request headers during update + /// package download for authentication. /// [JsonPropertyName("authToken")] public string? AuthToken { get; set; } /// - /// 此版本的更新日志或发行说明文本。 + /// The update log or release notes text for this version. /// [JsonPropertyName("updateLog")] public string? UpdateLog { get; set; } /// - /// 签名式下载 URL 的过期时间(UTC)。 - /// 过期后 URL 将失效,需要重新获取。 + /// The expiration time (UTC) of the signed download URL. + /// After expiration, the URL becomes invalid and must be re-acquired. /// [JsonPropertyName("urlExpireTimeUtc")] public DateTime? UrlExpireTimeUtc { get; set; } /// - /// 升级模式:1 = 版本链式升级(VersionChain),2 = 跨版本升级(CrossVersion)。 + /// The upgrade mode: 1 = VersionChain (sequential version upgrades), 2 = CrossVersion (cross-version upgrade). /// /// /// /// /// - /// 1(VersionChain):按顺序逐个版本升级,跳过中间版本。 + /// 1 (VersionChain): Upgrades through versions sequentially, without skipping intermediate versions. /// /// - /// 2(CrossVersion):直接从一个旧版本升级到任意新版本。 + /// 2 (CrossVersion): Directly upgrades from an old version to any newer version. /// /// /// @@ -179,37 +183,38 @@ public class VersionInfo public int? UpgradeMode { get; set; } /// - /// 是否为跨版本升级包。 - /// true 表示此包用于直接从一个旧版本升级到新版本,而非按顺序链式升级。 + /// Whether this is a cross-version upgrade package. + /// true indicates this package is used to upgrade directly from an old version to a new version, + /// rather than through sequential chain upgrades. /// [JsonPropertyName("isCrossVersion")] public bool? IsCrossVersion { get; set; } /// - /// 跨版本升级包的源版本号。 - /// 表示此差异包可以从哪个源版本应用。 + /// The source version number for cross-version upgrade packages. + /// Indicates which source version this differential patch can be applied to. /// /// - /// 仅当 true 时有效。 - /// 与 共同定义了差异补丁的版本作用范围。 + /// Only valid when is true. + /// Together with , defines the version scope of the differential patch. /// [JsonPropertyName("fromVersion")] public string? FromVersion { get; set; } /// - /// 跨版本升级包的目标版本号。 - /// 表示此差异包应用后将升级到的目标版本。 + /// The target version number for cross-version upgrade packages. + /// Indicates the target version that will be reached after applying this differential patch. /// /// - /// 仅当 true 时有效。 - /// 与 共同定义了差异补丁的版本作用范围。 + /// Only valid when is true. + /// Together with , defines the version scope of the differential patch. /// [JsonPropertyName("toVersion")] public string? ToVersion { get; set; } /// - /// 此版本包是否被冻结(归档,不用于活跃更新)。 - /// 冻结的版本包不会被用于更新检测和下载。 + /// Whether this version package is frozen (archived and not used for active updates). + /// Frozen version packages will not be used for update detection or download. /// [JsonPropertyName("isFreeze")] public bool? IsFreeze { get; set; } diff --git a/src/c#/GeneralUpdate.Core/Differential/DefaultCleanMatcher.cs b/src/c#/GeneralUpdate.Core/Differential/DefaultCleanMatcher.cs index e03b2571..e6895040 100644 --- a/src/c#/GeneralUpdate.Core/Differential/DefaultCleanMatcher.cs +++ b/src/c#/GeneralUpdate.Core/Differential/DefaultCleanMatcher.cs @@ -6,23 +6,28 @@ namespace GeneralUpdate.Core.Differential; /// -/// 的默认实现,保留 Clean 阶段(差异生成)的原始行为。 +/// Default implementation of that preserves the original behavior +/// of the Clean stage (differential generation). /// /// /// -/// DefaultCleanMatcher 实现了差异生成阶段的三种核心匹配操作: +/// DefaultCleanMatcher implements three core matching operations for the differential +/// generation stage: /// /// -/// :委托给 , -/// 对新旧两个目录进行完整的递归比较。 -/// :委托给 , -/// 以左侧为基准找出右侧不存在的文件(即待删除的文件)。 -/// :通过文件名称和相对路径的不区分大小写匹配, -/// 在新文件中查找对应的旧版本文件。匹配同时要求两个文件都存在在磁盘上才视为有效匹配。 +/// : Delegates to , +/// performing a full recursive comparison of two directories. +/// : Delegates to , +/// identifying files present in the source but absent in the target +/// (i.e., files to be deleted). +/// : Performs a case-insensitive match on file name and +/// relative path to find the corresponding old version of a new file. +/// Both files must exist on disk for the match to be considered valid. /// /// -/// 此实现的设计目标是保持与早期版本的向后兼容性。 -/// 如需自定义匹配逻辑(如基于文件哈希或元数据的匹配),可实现 接口。 +/// This implementation is designed to maintain backward compatibility with earlier versions. +/// To customize matching logic (e.g., hash-based or metadata-based matching), +/// implement the interface directly. /// /// public class DefaultCleanMatcher : ICleanMatcher diff --git a/src/c#/GeneralUpdate.Core/Differential/DefaultDirtyMatcher.cs b/src/c#/GeneralUpdate.Core/Differential/DefaultDirtyMatcher.cs index b171e230..fa9ed15b 100644 --- a/src/c#/GeneralUpdate.Core/Differential/DefaultDirtyMatcher.cs +++ b/src/c#/GeneralUpdate.Core/Differential/DefaultDirtyMatcher.cs @@ -5,23 +5,25 @@ namespace GeneralUpdate.Core.Differential; /// -/// 的默认实现:通过文件名的智能匹配规则, -/// 在补丁文件集合中查找与应用程序文件对应的差异补丁。 +/// Default implementation of : uses smart file name matching rules +/// to find the corresponding differential patch for each application file within the +/// patch file collection. /// /// /// -/// 匹配规则: +/// Matching rules: /// -/// 补丁文件的后缀名必须为 .patch(不区分大小写)。 -/// 补丁文件的基名(不含扩展名)需与应用程序文件的名称匹配。 -/// 支持嵌套 .patch 后缀的处理:如 example.dll.patch -/// 会去掉 .patch 后缀后得到 example.dll,再与应用程序文件名比较。 -/// 所有字符串比较不区分大小写。 +/// Patch files must have a .patch extension (case-insensitive). +/// The base name (without extension) of the patch file must match the application file name. +/// Supports nested .patch suffixes: e.g., example.dll.patch +/// strips .patch to yield example.dll, which is then compared to the +/// application file name. +/// All string comparisons are case-insensitive. /// /// /// -/// 此实现对应的是"脏"(Dirty)阶段,即补丁应用阶段,与 -/// 的差异生成阶段相对应。 +/// This implementation corresponds to the "Dirty" (patch application) stage, +/// which is the counterpart to the differential generation stage in . /// /// public class DefaultDirtyMatcher : IDirtyMatcher diff --git a/src/c#/GeneralUpdate.Core/Differential/ICleanMatcher.cs b/src/c#/GeneralUpdate.Core/Differential/ICleanMatcher.cs index a2175d92..7597f573 100644 --- a/src/c#/GeneralUpdate.Core/Differential/ICleanMatcher.cs +++ b/src/c#/GeneralUpdate.Core/Differential/ICleanMatcher.cs @@ -4,63 +4,79 @@ namespace GeneralUpdate.Core.Differential; /// -/// 定义差异生成(Clean)阶段的完整匹配策略接口。 -/// 实现者负责目录比较、识别已删除的文件以及将新文件匹配到对应的旧文件。 +/// Defines the complete matching strategy interface for the Clean (differential generation) stage. +/// Implementations are responsible for directory comparison, identifying deleted files, +/// and matching new files to their corresponding old files. /// /// /// -/// Clean 阶段(差异生成阶段)是差异更新的第一步,其核心目标是从新旧两个版本的目录中 -/// 生成差异补丁所需的所有信息。此接口定义了三个关键操作: +/// The Clean stage (differential generation stage) is the first step of differential updates. +/// Its core objective is to extract all information needed to generate differential patches +/// from two versions of a directory. This interface defines three key operations: /// /// -/// :对新旧目录进行递归比较,获取完整的差异信息。 -/// :识别在旧版本中存在但在新版本中被移除的文件。 -/// :将新版本中的文件与旧版本中对应的文件建立匹配关系, -/// 以便后续通过差异算法生成二进制补丁。 +/// : Recursively compares the old and new directories +/// to obtain complete differential information. +/// : Identifies files that existed in the old version +/// but were removed in the new version. +/// : Establishes a correspondence between files in the +/// new version and their counterparts in the old version, enabling subsequent binary +/// patch generation via a differential algorithm. /// /// -/// 默认实现请参考 。 -/// 如果需要自定义匹配逻辑(例如基于文件哈希值或自定义元数据的匹配),可以实现此接口。 +/// Refer to for the default implementation. +/// Implement this interface directly if you need custom matching logic +/// (e.g., hash-based or custom metadata-based matching). /// /// public interface ICleanMatcher { /// - /// 比较源目录和目标目录,返回发生变更的文件集合。 + /// Compares the source and target directories and returns the collection of files + /// that have changed. /// - /// 源(旧版本)目录路径。 - /// 目标(新版本)目录路径。 - /// 包含左右两侧节点列表和差异节点列表的 + /// The source (old version) directory path. + /// The target (new version) directory path. + /// A containing the left and right node lists + /// and the list of differing nodes. /// - /// 此方法是差异生成的入口点。实现应递归遍历两个目录, - /// 通过文件哈希值或元数据比较识别出所有新增、修改和删除的文件。 + /// This is the entry point for differential generation. Implementations should recursively + /// traverse both directories and identify all added, modified, and deleted files + /// by comparing file hashes or metadata. /// ComparisonResult Compare(string sourcePath, string targetPath); /// - /// 返回仅存在于源目录中的文件(即在新版本中被删除的文件)。 + /// Returns files that exist only in the source directory + /// (i.e., files deleted in the new version). /// - /// 源(旧版本)目录路径。 - /// 目标(新版本)目录路径。 - /// 仅存在于源目录中的 可枚举集合;无差异时返回空集合。 + /// The source (old version) directory path. + /// The target (new version) directory path. + /// An enumerable collection of instances that exist only + /// in the source directory; an empty collection if there are no differences. /// - /// 此方法的结果用于在更新包中生成"删除"指令,指示客户端在应用更新时移除这些文件。 + /// The results of this method are used to generate "delete" instructions in the update package, + /// telling the client to remove these files when applying the update. /// IEnumerable? Except(string sourcePath, string targetPath); /// - /// 尝试从左侧(源)节点集合中找到与指定新文件对应的旧文件节点。 + /// Attempts to find the old file node corresponding to a specified new file + /// from the source (left) node collection. /// - /// 来自目标目录的新文件节点。 - /// 来自源目录的所有文件节点集合。 + /// The new file node from the target directory. + /// The collection of all file nodes from the source directory. /// - /// 匹配到的旧 ;如果未找到匹配则返回 null - /// (表示该文件为全新文件,应直接复制而非生成差异补丁)。 + /// The matched old ; or null if no match is found + /// (indicating the file is entirely new and should be copied directly rather than + /// having a differential patch generated). /// /// - /// 对于成功匹配的文件,后续会使用差异算法(如 bsdiff)生成二进制补丁; - /// 对于未匹配的新文件,将直接打包完整的文件内容到更新包中。 - /// 实现应同时校验文件名称和相对路径,确保匹配的准确性。 + /// For successfully matched files, a differential algorithm (e.g., bsdiff) will be used + /// to generate a binary patch. For unmatched new files, the complete file content will + /// be packaged into the update package. + /// Implementations should verify both the file name and relative path to ensure + /// matching accuracy. /// FileNode? Match(FileNode newFile, IEnumerable leftNodes); } diff --git a/src/c#/GeneralUpdate.Core/Differential/IDirtyMatcher.cs b/src/c#/GeneralUpdate.Core/Differential/IDirtyMatcher.cs index 79f288bc..3d7badfe 100644 --- a/src/c#/GeneralUpdate.Core/Differential/IDirtyMatcher.cs +++ b/src/c#/GeneralUpdate.Core/Differential/IDirtyMatcher.cs @@ -4,39 +4,49 @@ namespace GeneralUpdate.Core.Differential; /// -/// 定义补丁应用(Dirty)阶段的匹配逻辑接口,用于查找与现有应用程序文件对应的差异补丁文件。 +/// Defines the matching logic interface for the Dirty (patch application) stage, +/// used to find the differential patch file corresponding to an existing application file. /// /// /// -/// Dirty 阶段(补丁应用阶段)是差异更新的第二步,它与 -/// 定义的 Clean 阶段(差异生成阶段)相对应: +/// The Dirty stage (patch application stage) is the second step of differential updates. +/// It corresponds to the Clean stage (differential generation stage) defined in +/// : /// /// -/// Clean 阶段:在服务端/构建时执行,分析新旧版本差异并生成补丁包。 -/// Dirty 阶段:在客户端/运行时执行,接收补丁包并应用到现有文件上。 +/// Clean stage: Executed on the server/build machine. Analyzes differences +/// between the old and new versions and generates the patch package. +/// Dirty stage: Executed on the client/at runtime. Receives the patch package +/// and applies it to existing files. /// /// -/// DirtyMatcher 负责将补丁目录中的 .patch 文件与客户端现有的应用程序文件建立对应关系。 -/// 匹配成功后将由差异引擎(如 PatchService)将补丁应用到旧文件上,生成更新后的文件。 +/// DirtyMatcher is responsible for establishing a correspondence between .patch files +/// in the patch directory and the existing application files on the client. +/// After a successful match, the differential engine (e.g., PatchService) applies +/// the patch to the old file, producing the updated file. /// /// -/// 默认实现请参考 ,它通过文件名匹配规则查找补丁文件。 +/// Refer to for the default implementation, which uses +/// file name matching rules to locate patch files. /// /// public interface IDirtyMatcher { /// - /// 从可用的补丁文件集合中查找与指定应用程序文件对应的补丁文件。 + /// Searches the available patch file collection for a patch corresponding to the + /// specified application file. /// - /// 需要被补丁的现有应用程序文件。 - /// 补丁目录中所有可用文件的集合。 + /// The existing application file that needs to be patched. + /// The collection of all available files in the patch directory. /// - /// 匹配到的补丁 ;如果未找到对应的补丁文件则返回 null - /// (表示该文件不需要更新或应直接替换)。 + /// The matched patch ; or null if no corresponding patch file + /// is found (indicating the file does not need an update or should be replaced directly). /// /// - /// 实现应基于文件名、相对路径或其他标识信息建立匹配关系。 - /// 返回 null 时,调用方应直接复制新版本文件替换旧版本文件。 + /// Implementations should establish the match based on file name, relative path, + /// or other identifying information. + /// When null is returned, the caller should directly copy the new version file + /// to replace the old version file. /// FileInfo? Match(FileInfo oldFile, IEnumerable patchFiles); } diff --git a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs index 6c224e5c..5242784f 100644 --- a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadExecutor.cs @@ -5,17 +5,62 @@ namespace GeneralUpdate.Core.Download.Abstractions; -/// Executes a single file download. +/// +/// Defines a contract for executing a single file download operation. +/// Implementations handle the actual data transfer from a remote source to a local file path. +/// +/// +/// +/// Implementations must be thread-safe, as the same executor instance may be shared +/// across parallel download tasks within an . +/// +/// +/// Built-in implementations include HttpDownloadExecutor (HTTP/HTTPS with resume support) +/// and OssDownloadExecutor (OSS signed URL downloads). +/// +/// public interface IDownloadExecutor { + /// + /// Asynchronously executes the download of a single asset to the specified destination path. + /// + /// The describing the resource to download (URL, name, hash, etc.). + /// The full local file path where the downloaded content will be written. + /// An optional receiver for download progress notifications. + /// A to cancel the download operation. + /// A containing the outcome (success/failure, bytes downloaded, duration, etc.). Task ExecuteAsync( DownloadAsset asset, string destPath, IProgress? progress = null, CancellationToken token = default); } -/// Retry / timeout / circuit-breaker policy for downloads. +/// +/// Defines a retry, timeout, or circuit-breaker policy for download operations. +/// +/// +/// +/// Implementations wrap a download delegate and add cross-cutting concerns such as: +/// +/// +/// Retry with exponential backoff for transient failures. +/// Timeout enforcement per attempt. +/// Circuit-breaker logic to stop retrying after a threshold of failures. +/// +/// +/// The default implementation is DefaultRetryPolicy, which retries on timeouts, +/// network I/O errors, and 5xx server errors using exponential backoff. +/// +/// public interface IDownloadPolicy { + /// + /// Executes the specified download action, applying the policy's retry/timeout logic. + /// + /// The return type of the action. + /// The asynchronous operation to execute, accepting a . + /// A to cancel the entire operation including retries. + /// The result of the action if it succeeds within the policy's constraints. + /// Thrown when cancellation is requested via . Task ExecuteAsync(Func> action, CancellationToken token = default); } diff --git a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadOrchestrator.cs b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadOrchestrator.cs index 8e539e0c..cc06a28d 100644 --- a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadOrchestrator.cs +++ b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadOrchestrator.cs @@ -6,13 +6,47 @@ namespace GeneralUpdate.Core.Download.Abstractions; -/// Orchestrates batch downloads with concurrency control. +/// +/// Orchestrates batch downloads with concurrency control, retry logic, and SHA256 verification. +/// Implementations manage the lifecycle of multiple concurrent download tasks +/// and aggregate their results into a single . +/// +/// +/// +/// The orchestrator is the top-level component in the download subsystem. It: +/// +/// +/// Accepts a containing assets to download. +/// Controls maximum parallelism via a semaphore. +/// Creates executors () and pipelines () per asset. +/// Wraps each download in a retry policy (). +/// Reports per-asset progress through . +/// Returns a with per-result details and overall statistics. +/// +/// public interface IDownloadOrchestrator { /// - /// Execute downloads for all assets in the plan. - /// Handles parallelism, retry, and SHA256 verification. + /// Executes the download plan for all assets, managing parallelism, retries, + /// SHA256 verification, and progress reporting. /// + /// The download plan containing the list of assets to download. Must not be null. + /// The destination directory path where files will be saved. + /// + /// The maximum number of concurrent downloads. Defaults to 3. + /// When set to 0 or a negative value, falls back to the value configured in options. + /// + /// An optional receiver for per-asset download progress notifications. + /// A to cancel the operation. + /// + /// A containing: + /// + /// A list of for each asset. + /// Total bytes downloaded across all successful assets. + /// Total elapsed duration. + /// Counts of successful and failed downloads. + /// + /// Task ExecuteAsync( DownloadPlan plan, string destDir, @@ -21,6 +55,14 @@ Task ExecuteAsync( CancellationToken token = default); } +/// +/// Represents the aggregated result of a batch download operation executed by an . +/// +/// The list of per-asset download results. +/// The total number of bytes successfully downloaded across all assets. +/// The total elapsed time of the entire download operation. +/// The number of assets that were downloaded successfully. +/// The number of assets that failed to download. public record DownloadReport( IReadOnlyList Results, long TotalBytes, diff --git a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadSource.cs b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadSource.cs index 4f9be7eb..b428acfd 100644 --- a/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadSource.cs +++ b/src/c#/GeneralUpdate.Core/Download/Abstractions/IDownloadSource.cs @@ -4,14 +4,59 @@ namespace GeneralUpdate.Core.Download.Abstractions; -/// Source of download assets (e.g. HTTP API, OSS bucket, SignalR Hub). +/// +/// Defines a contract for retrieving the list of downloadable assets from a remote source. +/// +/// +/// +/// Implementations encapsulate the logic of connecting to a remote service—such as an HTTP +/// version-validation API, an OSS bucket, or a SignalR hub—and returning a structured list +/// of objects for the orchestrator to process. +/// +/// +/// Built-in implementations: +/// +/// HttpDownloadSource — calls a version-validation API for Client and Upgrade app types. +/// OssDownloadSource — downloads and parses a version JSON file from an object storage URL. +/// +/// +/// public interface IDownloadSource { + /// + /// Asynchronously retrieves the list of downloadable assets. + /// + /// A to cancel the operation. + /// A containing the list of objects + /// and flags indicating whether main or upgrade updates are available. Task ListAsync(CancellationToken token = default); } -/// Post-download processing pipeline (verify, decompress, decrypt). +/// +/// Defines a post-download processing pipeline that transforms or verifies a downloaded file. +/// +/// +/// +/// Implementations perform one or more post-processing steps after a file has been downloaded, +/// such as: +/// +/// +/// SHA256 hash verification against an expected value. +/// Archive decompression (zip, tar, gz). +/// Decryption of an encrypted package. +/// +/// +/// The default implementation is DefaultDownloadPipeline, which performs SHA256 hash verification. +/// +/// public interface IDownloadPipeline { + /// + /// Processes the downloaded file at the specified path, performing verification or transformation. + /// + /// The full path to the downloaded file. + /// A to cancel the operation. + /// The file path after processing (may differ if the file was extracted or transformed). + /// Thrown when verification fails (e.g., hash mismatch). Task ProcessAsync(string downloadedPath, CancellationToken token = default); } diff --git a/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs b/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs index f15c9038..ce44a67c 100644 --- a/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs +++ b/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs @@ -7,50 +7,56 @@ namespace GeneralUpdate.Core.Download; /// -/// 构建下载计划(),从下载资产列表中筛选出需要下载的资产。 -/// 处理冻结包过滤、强制更新标记和 MinClientVersion 兼容性检查。 +/// Builds a by filtering and ordering download assets +/// based on version compatibility, frozen-package filtering, forced-update detection, +/// and MinClientVersion compatibility checks. /// /// /// -/// 此类为静态工具类,负责根据当前客户端版本从服务器返回的资产列表中构建合理的下载计划。 -/// 构建过程遵循以下规则: +/// This static utility class constructs a download plan from server-returned assets +/// based on the current client version. The build process follows these rules: /// /// -/// 冻结包过滤跳过被标记为冻结(IsFreeze = true)的包, -/// 这些包不参与更新下载。 -/// 强制更新检测检测资产列表中是否存在强制更新(IsForcibly = true)标记, -/// 如果存在,整个更新计划被标记为强制更新。 -/// 版本过滤和排序只保留版本高于当前客户端版本的包, -/// 并按版本号升序排列(从低到高依次安装)。 -/// 兼容性检查检查每个包的 MinClientVersion 要求, -/// 如果当前客户端版本低于要求的最小版本,则跳过该包。 +/// Frozen package filteringSkips packages marked as frozen (IsFreeze = true), +/// as they should not participate in the update. +/// Forced update detectionDetects if any asset has the forced-update flag +/// (IsForcibly = true), in which case the entire plan is marked as forced. +/// Version filtering and sortingRetains only packages with versions higher +/// than the current client version, sorted in ascending order (lowest to highest). +/// Compatibility checkChecks each package's MinClientVersion requirement; +/// if the current client version is below the minimum, the package is skipped. /// /// -/// 注意:此构建器不区分跨版本更新和版本链更新,每个包携带自己的 IsCrossVersion 元数据供下游处理。 +/// Note: This builder does not distinguish between cross-version and in-order updates; +/// each package carries its own IsCrossVersion metadata for downstream processing. /// /// public static class DownloadPlanBuilder { /// - /// 从下载资产列表中构建下载计划。 + /// Builds a download plan from a list of download assets, filtering and ordering + /// based on the current client version. /// - /// 从下载源获取的资产列表。 - /// 当前客户端版本字符串。 - /// 包含有序资产的 。如果不需要更新,则返回 + /// The list of assets retrieved from the download source. + /// The current client version string. + /// + /// A containing ordered assets suitable for download. + /// Returns if no update is needed. + /// /// /// - /// 构建流程: + /// Build process: /// /// - /// 验证输入:如果资产列表为 null 或当前版本无法解析,返回空计划。 - /// 过滤冻结包:移除 IsFreeze = true 的资产。 - /// 检查强制更新:如果任一资产标记为强制更新,整个计划标记为强制。 - /// 版本过滤:只保留版本高于当前客户端版本的资产。 - /// 兼容性检查:确保每个资产的 MinClientVersion 与当前版本兼容。 - /// 升序排序:按版本号从小到大排序。 + /// Validates input: if assets is null or the current version cannot be parsed, returns an empty plan. + /// Filters out frozen packages: removes assets with IsFreeze = true. + /// Checks for forced updates: if any asset is marked as forced, the entire plan is marked as forced. + /// Version filtering: retains only assets with versions higher than the current version. + /// Compatibility check: ensures each asset's MinClientVersion is compatible with the current version. + /// Ascending sort: orders assets by version number from lowest to highest. /// /// - /// 如果没有符合条件的资产,返回 DownloadPlan.Empty。 + /// If no assets match the criteria, returns DownloadPlan.Empty. /// /// public static DownloadPlan Build(IEnumerable assets, string currentVersion) @@ -87,12 +93,12 @@ public static DownloadPlan Build(IEnumerable assets, string curre } /// - /// 检查 MinClientVersion 是否与当前客户端版本兼容。 - /// 如果某个包的 MinClientVersion 高于当前版本,则该包不适用于当前客户端。 + /// Checks whether the specified MinClientVersion is compatible with the current client version. + /// If a package's MinClientVersion is higher than the current version, the package is not applicable. /// - /// 包要求的最低客户端版本。如果为 null 或空,则视为兼容。 - /// 当前客户端版本字符串。 - /// 如果当前版本达到或超过最低要求则返回 true,否则返回 false。 + /// The minimum client version required by the package. If null or empty, the package is considered compatible. + /// The current client version string. + /// True if the current version meets or exceeds the minimum requirement; otherwise false. internal static bool IsCompatible(string? minClientVersion, string currentVersion) { if (string.IsNullOrEmpty(minClientVersion)) return true; @@ -102,9 +108,9 @@ internal static bool IsCompatible(string? minClientVersion, string currentVersio return cur >= min; } - /// 解析版本字符串,如果无法解析则返回 null。 - /// 要解析的版本字符串。 - /// 解析后的 对象,如果解析失败则返回 null。 + /// Parses a version string and returns null if the string cannot be parsed. + /// The version string to parse. + /// A parsed object, or null if parsing fails. internal static Version? ParseVersion(string? version) { if (string.IsNullOrWhiteSpace(version)) return null; diff --git a/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs b/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs index 55710ee3..7a521d47 100644 --- a/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Download/Executors/HttpDownloadExecutor.cs @@ -10,31 +10,38 @@ namespace GeneralUpdate.Core.Download.Executors; /// -/// 基于 HTTP 协议的文件下载执行器,支持断点续传(Range 请求头)和分块流式下载。 +/// HTTP-based file download executor supporting resumable downloads via the Range request header +/// and chunked streaming downloads. /// /// /// -/// 此类实现了 接口,提供从 HTTP 端点下载文件的核心功能。 +/// This class implements and provides the core functionality +/// for downloading files from HTTP endpoints. /// /// -/// 主要特性: +/// Key features: /// -/// 断点续传通过 HTTP Range 请求头从上次中断位置继续下载, -/// 支持通过 enableResume 参数启用或禁用。当服务器不支持部分内容响应时, -/// 自动回退到从头下载。 -/// 分块流式下载使用 8KB 缓冲区逐块读取和写入数据流, -/// 避免将整个文件加载到内存中。 -/// 进度报告通过 IProgress<DownloadProgress> -/// 每 250 毫秒报告一次下载进度,包含已下载字节数、总字节数和百分比。 -/// 超时控制支持通过 timeout 参数设置每次请求的超时时间。 -/// 认证支持DownloadAsset 中读取认证方案和令牌, -/// 为需要授权的下载源提供 HTTP Bearer 或自定义认证。 -/// 取消支持通过 CancellationToken 支持取消正在进行的下载操作。 +/// Resumable downloadsUses the HTTP Range header to continue +/// downloads from where they were interrupted. Can be enabled or disabled via the +/// enableResume parameter. Automatically falls back to a full download when the +/// server does not support partial content responses. +/// Chunked streaming downloadReads and writes data streams in +/// 8 KB chunks, avoiding loading the entire file into memory. +/// Progress reportingReports download progress via +/// IProgress<DownloadProgress> every 250 milliseconds, including bytes +/// downloaded, total bytes, and percentage. +/// Timeout controlSupports configurable per-request timeouts +/// via the timeout parameter. +/// Authentication supportReads authentication scheme and token +/// from DownloadAsset to provide HTTP Bearer or custom authentication for +/// authorized download sources. +/// Cancellation supportSupports cancelling in-progress downloads +/// via CancellationToken. /// /// /// -/// 此执行器被 DefaultDownloadOrchestratorOSSUpdateStrategy 等高层组件使用, -/// 作为实际的 HTTP 下载引擎。 +/// This executor is used by higher-level components such as DefaultDownloadOrchestrator +/// and OSSUpdateStrategy as the actual HTTP download engine. /// /// public class HttpDownloadExecutor : IDownloadExecutor @@ -44,12 +51,13 @@ public class HttpDownloadExecutor : IDownloadExecutor private readonly bool _enableResume; /// - /// 使用指定的 HTTP 客户端、超时设置和断点续传选项初始化下载执行器。 + /// Initializes a new instance of the class + /// with the specified HTTP client, timeout, and resume options. /// - /// 用于发送 HTTP 请求的 实例。不能为 null。 - /// 每次 HTTP 请求的超时时间。默认为 30 秒。 - /// 是否启用断点续传功能。默认为 true。 - /// 为 null 时抛出。 + /// The instance used to send HTTP requests. Must not be null. + /// The timeout duration for each HTTP request. Defaults to 30 seconds. + /// Whether to enable resumable download support. Defaults to true. + /// Thrown when is null. public HttpDownloadExecutor(HttpClient client, TimeSpan? timeout = null, bool enableResume = true) { _client = client ?? throw new ArgumentNullException(nameof(client)); @@ -58,29 +66,29 @@ public HttpDownloadExecutor(HttpClient client, TimeSpan? timeout = null, bool en } /// - /// 异步执行单个下载资产的文件下载操作。 + /// Asynchronously executes the download of a single asset file. /// - /// 要下载的资产信息,包含 URL、文件名、大小和哈希值。 - /// 下载文件的目标本地路径。 - /// 可选的进度报告器,用于报告下载进度。 - /// 可选的取消令牌,用于取消下载操作。 - /// 包含下载结果(成功/失败、已下载字节数、耗时等)的 + /// The asset information to download, including URL, file name, size, and hash. + /// The destination local file path for the download. + /// An optional progress reporter for reporting download progress. + /// An optional cancellation token to cancel the download operation. + /// A containing the download outcome (success/failure, bytes downloaded, duration, etc.). /// /// - /// 下载流程如下: + /// Download flow: /// /// - /// 检查目标文件是否已存在部分下载(断点续传支持)。 - /// 创建 HTTP GET 请求,如果启用续传且存在部分文件,则添加 Range 请求头。 - /// 如果资产提供了认证方案和令牌,添加 Authorization 请求头。 - /// 发送请求并读取响应流。 - /// 如果服务器返回非 206 PartialContent 状态码,则从头开始下载。 - /// 使用 分块读取并写入文件。 - /// 下载完成后报告 100% 进度。 + /// Checks whether a partially downloaded file already exists at the destination (for resume support). + /// Creates an HTTP GET request, adding a Range header if resume is enabled and a partial file exists. + /// If the asset provides an authentication scheme and token, adds an Authorization header. + /// Sends the request and reads the response stream. + /// If the server returns a non-206 PartialContent status code, starts the download from the beginning. + /// Reads and writes the file in chunks using with an 8 KB buffer. + /// Reports 100% progress after the download completes. /// /// - /// 如果下载过程中发生异常(除 OperationCanceledException 外), - /// 会返回包含错误信息的 DownloadResult,而不是抛出异常。 + /// If an exception occurs during download (excluding OperationCanceledException), + /// a DownloadResult containing the error information is returned instead of throwing. /// /// public async Task ExecuteAsync( @@ -149,25 +157,28 @@ public async Task ExecuteAsync( } /// - /// 共享的下载循环:从源流读取数据,写入目标流,并报告下载进度。 - /// 此方法被 HTTP 和 OSS 执行器共用,避免重复的缓冲/读取/写入/进度报告逻辑。 + /// Shared download loop: reads data from a source stream, writes to a destination stream, + /// and reports download progress. This method is reused by both the HTTP and OSS executors + /// to avoid duplicating the buffering/reading/writing/progress-reporting logic. /// - /// 源数据流(通常来自 HTTP 响应流)。 - /// 目标文件流。 - /// 服务器报告的内容总大小(可为 -1 表示未知)。 - /// 已存在的部分下载字节数(断点续传支持)。 - /// 目标文件路径(仅用于进度报告中的文件名显示)。 - /// 可选的进度报告器。 - /// 用于测量下载耗时的计时器。 - /// 取消令牌。 - /// 包含实际下载字节数和耗时的元组。 + /// The source data stream (typically from an HTTP response stream). + /// The destination file stream. + /// The total content size reported by the server (may be -1 if unknown). + /// The number of already-downloaded bytes (for resume support). + /// The destination file path (used only for file name display in progress reports). + /// An optional progress reporter. + /// A stopwatch for measuring download duration. + /// A cancellation token. + /// A tuple containing the actual bytes downloaded and the elapsed duration. /// /// - /// 下载循环使用 8192 字节(8KB)的缓冲区进行分块读取,避免内存占用过高。 + /// The download loop uses an 8192-byte (8 KB) buffer for chunked reading to avoid + /// excessive memory consumption. /// /// - /// 进度报告每 250 毫秒触发一次,或在下载完成时立即触发。 - /// 进度信息包含文件名、已下载字节数、总字节数(如果已知)以及完成百分比。 + /// Progress is reported every 250 milliseconds, or immediately upon download completion. + /// Progress information includes the file name, bytes downloaded, total bytes (if known), + /// and completion percentage. /// /// internal static async Task<(long Downloaded, TimeSpan Elapsed)> StreamDownloadAsync( diff --git a/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs b/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs index 62d9aef4..f6d30929 100644 --- a/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Download/Executors/OssDownloadExecutor.cs @@ -8,14 +8,69 @@ namespace GeneralUpdate.Core.Download.Executors; -/// OSS-based download executor. Delegates to HTTP GET for OSS signed URLs. +/// +/// OSS (Object Storage Service) download executor that downloads files via HTTP GET +/// using pre-signed URLs obtained from an OSS provider. +/// +/// +/// +/// This class implements and is designed to work with +/// object storage services such as AliYun OSS, AWS S3, MinIO, and Tencent COS. +/// +/// +/// The executor delegates the actual data transfer to +/// which provides shared buffered reading, writing, and progress-reporting logic. +/// +/// +/// Workflow: +/// +/// Sends an HTTP GET request to the asset's pre-signed URL. +/// Reads the response stream and writes it to the destination file using an 8 KB buffer. +/// Reports download progress through the callback. +/// Returns a with success/failure status, bytes downloaded, and duration. +/// +/// +/// +/// Note: Unlike HttpDownloadExecutor, this executor does not support resume/download-range headers +/// because OSS pre-signed URLs are typically short-lived and single-use. +/// +/// public class OssDownloadExecutor : IDownloadExecutor { private readonly HttpClient _client; + /// + /// Initializes a new instance of the class. + /// + /// The instance used to send HTTP requests. Must not be null. + /// Thrown when is null. public OssDownloadExecutor(HttpClient client) => _client = client ?? throw new ArgumentNullException(nameof(client)); + /// + /// Asynchronously downloads a single asset from an OSS pre-signed URL to the specified local path. + /// + /// The describing the resource to download, including its signed URL. + /// The full local file path where the downloaded content will be written. + /// An optional receiver for download progress notifications. + /// A to cancel the download operation. + /// + /// A containing the outcome of the download, including + /// success/failure status, bytes downloaded, and elapsed duration. + /// + /// + /// + /// Download flow: + /// + /// + /// Sends an HTTP GET with for streaming. + /// Ensures the response status is successful (throws on HTTP error codes). + /// Creates the destination directory if it does not exist. + /// Opens a file stream and reads the HTTP response content in chunks (delegated to ). + /// Reports 100% completion progress when finished. + /// On exception (except ), returns a failed instead of throwing. + /// + /// public async Task ExecuteAsync( DownloadAsset asset, string destPath, IProgress? progress = null, diff --git a/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs b/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs index 2cbf38f3..79681482 100644 --- a/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs +++ b/src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs @@ -17,37 +17,40 @@ namespace GeneralUpdate.Core.Download.Orchestrators; /// -/// 默认下载编排器,支持并行执行、并发限制、SHA256 校验、断点续传和进度报告。 +/// Default download orchestrator supporting parallel execution, concurrency limiting, +/// SHA256 verification, resumable downloads, and progress reporting. /// /// /// -/// 该编排器承载了批量下载的核心业务流程,其工作流程如下: +/// This orchestrator implements the core batch download workflow: /// /// /// -/// 接收 ,其中包含待下载的资源清单及并发设置。 +/// Receives a containing the asset manifest and concurrency settings. /// /// -/// 使用 控制最大并发数,防止资源耗尽。 +/// Uses a to control maximum concurrency and prevent resource exhaustion. /// /// -/// 对每个资源并行执行:创建执行器( 或自定义 ), -/// 创建下载管道(默认执行 SHA256 哈希校验),并包装在重试策略()中。 +/// For each asset, executes in parallel: creates an executor ( or custom ), +/// creates a download pipeline (default performs SHA256 hash verification), and wraps it in a retry policy (). /// /// -/// 每个资源的内部流程:下载文件 -> SHA256 校验 -> 报告进度。 +/// Internal flow per asset: Download file -> SHA256 verification (optional) -> Report progress. /// /// -/// 所有资源完成后,返回 ,包含每个资源的结果、总下载字节数、总耗时等信息。 +/// After all assets complete, returns a containing per-asset results, +/// total bytes downloaded, total duration, and success/failure counts. /// /// /// -/// 所有可配置的行为均由 驱动, -/// 该选项映射自引导层定义的 。 +/// All configurable behaviors are driven by , +/// which maps from the defined in the bootstrap layer. /// /// -/// 注意:自定义执行器()在多个并行下载任务间共享单一实例, -/// 因此实现必须保证线程安全。基于 HttpClient 的执行器满足此要求,因为 HttpClient 设计为支持并发使用。 +/// Note: The custom executor () is shared as a single instance +/// across parallel download tasks, so implementations must be thread-safe. +/// HttpClient-based executors satisfy this requirement because HttpClient is designed for concurrent use. /// /// public class DefaultDownloadOrchestrator : IDownloadOrchestrator @@ -62,28 +65,30 @@ public class DefaultDownloadOrchestrator : IDownloadOrchestrator // satisfy this as HttpClient is designed for concurrent use. /// - /// 初始化 的新实例。 + /// Initializes a new instance of the class. /// - /// 用于 HTTP 下载的 实例。不能为 null。 + /// The instance used for HTTP downloads. Must not be null. /// - /// 下载编排器选项,包含并发数、超时、校验、断点续传等设置。 - /// 为 null 时将使用 的默认值。 + /// Download orchestrator options including concurrency, timeout, verification, and resume settings. + /// If null, default values will be used. /// /// - /// 重试策略。为 null 时将使用 ,重试次数和间隔取自 。 + /// The retry policy. If null, a will be used with retry count + /// and interval taken from . /// /// - /// 自定义下载执行器。当需要非 HTTP 的下载方式(如 FTP、本地文件复制)时传入。 - /// 为 null 时将根据 创建 。 + /// A custom download executor for non-HTTP download methods (e.g., FTP, local file copy). + /// If null, an will be created from . /// - /// 警告:该实例会在所有并行下载任务间共享,实现必须保证线程安全。 + /// WARNING: This instance is shared across all parallel download tasks; implementations must be thread-safe. /// /// /// - /// 自定义下载管道的工厂委托,接收资源的 SHA256 值作为参数,返回 。 - /// 为 null 时将使用 执行 SHA256 校验。 + /// A factory delegate for creating custom download pipelines. Receives the asset's SHA256 value + /// and returns an . If null, + /// is used for SHA256 verification. /// - /// 为 null 时抛出。 + /// Thrown when is null. public DefaultDownloadOrchestrator( HttpClient httpClient, DownloadOrchestratorOptions? options = null, @@ -99,71 +104,80 @@ public DefaultDownloadOrchestrator( } /// - /// 执行下载计划中的所有资源,支持并行下载、并发控制、断点续传和 SHA256 校验。 + /// Executes all assets in the download plan, supporting parallel downloads, concurrency control, + /// resumable downloads, and SHA256 verification. /// /// /// - /// 整体执行流程如下: + /// Overall execution flow: /// /// /// - /// 验证 是否包含有效资源;若为空或无资源,直接返回空报告。 + /// Validates contains valid assets; returns an empty report if null or empty. /// /// - /// 创建目标目录 (如不存则自动创建)。 + /// Creates the destination directory (auto-creates if it does not exist). /// /// - /// 确定有效并发数:若 则强制串行(并发数为 1); - /// 否则取 与选项配置值的较大者。 + /// Determines effective concurrency: if is + /// , forces serial execution (concurrency = 1); otherwise uses the greater of + /// and the configured value. /// /// - /// 使用 限制同时进行的下载任务数量。 + /// Uses to limit the number of simultaneous download tasks. /// /// - /// 对每个资源并行执行以下步骤: + /// For each asset, executes the following steps in parallel: /// /// - /// 解析文件名(参见 )。 + /// Resolves the file name (see ). /// /// - /// 创建执行器:优先使用自定义执行器,否则创建 (支持断点续传)。 + /// Creates the executor: uses the custom executor if provided, otherwise creates + /// an (with resume support). /// /// - /// 创建下载管道:优先使用工厂委托,否则创建 执行 SHA256 哈希校验。 + /// Creates the download pipeline: uses the factory delegate if provided, otherwise creates + /// a for SHA256 hash verification. /// /// - /// 通过重试策略执行:下载 -> 条件性 SHA256 校验(当 为 true 时)。 + /// Executes through the retry policy: Download -> Conditional SHA256 verification + /// (when is true). /// /// - /// 每个步骤均通过 报告器上报以资源名为维度的进度信息。 + /// Each step reports progress via using the asset name as the dimension. /// /// - /// 所有资源完成后,触发一次性完成事件,并汇总返回 + /// After all assets complete, fires a one-time completion event and returns + /// the aggregated . /// /// /// - /// 关于断点续传:当 为 true 时, - /// 会在 HTTP 请求中附加 Range 头,从上次中断处继续下载。 + /// About resumable downloads: When is true, + /// attaches a Range header to the HTTP request to continue + /// downloading from where it was interrupted. /// /// - /// 关于 SHA256 校验:当 为 false 时, - /// 校验步骤将被跳过以提升性能。建议在生产环境中始终保持校验开启。 + /// About SHA256 verification: When is false, + /// the verification step is skipped for improved performance. It is recommended to keep verification + /// enabled in production environments. /// /// - /// 下载计划,包含待下载的资源列表。 - /// 文件下载到的目标目录路径。 + /// The download plan containing the list of assets to download. + /// The destination directory path where files will be saved. /// - /// 最大并发下载数。默认值为 3。当值小于等于 0 时将回退到 。 + /// Maximum number of concurrent downloads. Defaults to 3. When set to 0 or a negative value, + /// falls back to . /// - /// 进度报告器,用于接收每个资源的下载进度。 - /// 用于取消操作的 。 + /// A progress reporter for receiving per-asset download progress. + /// A to cancel the operation. /// - /// ,包含: + /// A containing: /// - /// 每个资源的详细结果()。 - /// 所有成功下载的总字节数。 - /// 总耗时。 - /// 成功和失败的数量。 + /// Detailed results for each asset (). + /// Total bytes successfully downloaded. + /// Total elapsed duration. + /// Counts of successful and failed downloads. /// /// public async Task ExecuteAsync( @@ -275,27 +289,27 @@ public async Task ExecuteAsync( } /// - /// 根据资源信息解析最终的文件名。 + /// Resolves the final file name from the asset information. /// /// /// - /// 文件名解析优先级如下: + /// The file name resolution priority is as follows: /// /// /// - /// 优先使用 ,并追加 对应的扩展名 - /// (若名称尚未以该扩展名结尾)。 + /// First uses and appends the extension corresponding to + /// (if the name does not already end with that extension). /// /// - /// 若名称为空,尝试从 的 URI 路径中提取文件名。 + /// If the name is empty, attempts to extract the file name from the URI path of . /// /// - /// 若上述均失败,返回格式为 "{Name}.{Version}" 的回退文件名。 + /// If all the above fail, returns a fallback file name in the format "{Name}.{Version}". /// /// /// - /// 下载资源信息,包含名称、URL 和版本号。 - /// 解析后的目标文件名。 + /// The download asset information containing name, URL, and version. + /// The resolved destination file name. private string GetFileName(DownloadAsset asset) { if (!string.IsNullOrEmpty(asset.Name)) diff --git a/src/c#/GeneralUpdate.Core/Download/Pipeline/DefaultDownloadPipeline.cs b/src/c#/GeneralUpdate.Core/Download/Pipeline/DefaultDownloadPipeline.cs index 5d851288..96a2b6fd 100644 --- a/src/c#/GeneralUpdate.Core/Download/Pipeline/DefaultDownloadPipeline.cs +++ b/src/c#/GeneralUpdate.Core/Download/Pipeline/DefaultDownloadPipeline.cs @@ -7,26 +7,28 @@ namespace GeneralUpdate.Core.Download.Pipeline; /// -/// 默认的下载后处理管道,对下载的文件执行 SHA256 哈希校验, -/// 确保文件完整性与服务器端提供的预期哈希值一致。 +/// Default post-download processing pipeline that performs SHA256 hash verification +/// on downloaded files to ensure their integrity matches the expected hash value +/// provided by the server. /// /// /// -/// 此类实现了 接口,在下载完成后对文件进行完整性验证。 +/// This class implements and provides integrity verification +/// after a file has been downloaded. /// /// -/// 工作流程: +/// Workflow: /// -/// 检查是否配置了预期哈希值。如果没有配置,则跳过校验直接返回文件路径。 -/// 使用 SHA256 算法计算下载文件的哈希值。 -/// 将计算出的哈希值与预期的哈希值进行不区分大小写的比较。 -/// 如果哈希值匹配,返回原始文件路径。 -/// 如果哈希值不匹配,抛出 +/// Checks whether an expected hash value has been configured. If not, skips verification and returns the file path directly. +/// Computes the SHA256 hash of the downloaded file. +/// Compares the computed hash against the expected hash using a case-insensitive comparison. +/// If the hashes match, returns the original file path. +/// If the hashes do not match, throws with details of the mismatch. /// /// /// -/// SHA256 计算在后台线程上执行(通过 Task.Run),避免阻塞调用线程。 -/// 同时通过 CancellationToken 支持取消哈希计算操作。 +/// The SHA256 computation is offloaded to a background thread via Task.Run +/// to avoid blocking the calling thread. Cancellation is supported through CancellationToken. /// /// public class DefaultDownloadPipeline : IDownloadPipeline @@ -34,24 +36,35 @@ public class DefaultDownloadPipeline : IDownloadPipeline private readonly string? _expectedHash; /// - /// 使用预期的 SHA256 哈希值初始化下载管道。 + /// Initializes a new instance of the class + /// with the expected SHA256 hash value. /// - /// 预期的 SHA256 哈希值(十六进制字符串,不区分大小写)。 - /// 如果为 null 或空,则跳过哈希校验。 + /// + /// The expected SHA256 hash value as a hexadecimal string (case-insensitive). + /// If null or empty, hash verification is skipped. + /// public DefaultDownloadPipeline(string? expectedHash = null) => _expectedHash = expectedHash; /// - /// 对已下载的文件进行处理,执行 SHA256 哈希验证。 + /// Processes the downloaded file by performing SHA256 hash verification + /// against the expected hash value. /// - /// 已下载文件的完整路径。 - /// 用于取消哈希计算的取消令牌。 - /// 如果哈希验证通过,返回原始文件路径。 - /// 当计算出的 SHA256 哈希值与预期值不匹配时抛出。 - /// 当操作通过取消令牌被取消时抛出。 + /// The full path to the downloaded file. + /// A to cancel the hash computation. + /// The original file path if hash verification passes or is skipped. + /// Thrown when the computed SHA256 hash does not match the expected value. + /// Thrown when the operation is cancelled via the . /// - /// 如果 _expectedHash 为 null 或空字符串,则跳过哈希校验直接返回文件路径。 - /// 哈希比较不区分大小写。 + /// + /// Processing flow: + /// + /// + /// If _expectedHash is null or empty, the file path is returned immediately without verification. + /// Otherwise, the SHA256 hash of the file is computed asynchronously via . + /// The computed hash is compared with the expected hash using case-insensitive ordinal comparison. + /// If the hashes match, the file path is returned. Otherwise, an is thrown. + /// /// public async Task ProcessAsync(string downloadedPath, CancellationToken token = default) { @@ -66,14 +79,19 @@ public async Task ProcessAsync(string downloadedPath, CancellationToken } /// - /// 计算指定文件的 SHA256 哈希值。 + /// Computes the SHA256 hash of the specified file. /// - /// 要计算哈希的文件路径。 - /// 用于取消操作的取消令牌。 - /// 文件的小写十六进制 SHA256 哈希字符串。 + /// The file path for which to compute the hash. + /// A to cancel the operation. + /// A lowercase hexadecimal SHA256 hash string without separators (e.g., "a1b2c3d4..."). /// - /// 哈希计算在后台线程上执行,以避免阻塞调用线程。 - /// 返回的哈希字符串为小写字母且不含分隔符(如 "a1b2c3d4...")。 + /// + /// The hash computation is performed on a background thread via Task.Run + /// to avoid blocking the calling thread. + /// + /// + /// The returned hash string is lowercase with no hyphens or separators. + /// /// private static async Task ComputeSha256Async(string path, CancellationToken token) { diff --git a/src/c#/GeneralUpdate.Core/Download/Policy/DefaultRetryPolicy.cs b/src/c#/GeneralUpdate.Core/Download/Policy/DefaultRetryPolicy.cs index 5c183d64..67e8b24d 100644 --- a/src/c#/GeneralUpdate.Core/Download/Policy/DefaultRetryPolicy.cs +++ b/src/c#/GeneralUpdate.Core/Download/Policy/DefaultRetryPolicy.cs @@ -9,33 +9,41 @@ namespace GeneralUpdate.Core.Download.Policy; /// -/// 默认的下载重试策略,基于指数退避算法。 -/// 对于可恢复的临时故障(超时、网络 I/O 错误、5xx 服务器错误)进行重试。 -/// 对于永久性故障(4xx 客户端错误、SSL/认证错误)不进行重试。 +/// Default download retry policy based on exponential backoff. +/// Retries on recoverable transient failures (timeouts, network I/O errors, 5xx server errors). +/// Does not retry on permanent failures (4xx client errors, SSL/authentication errors). /// /// /// -/// 此类实现了 接口,为下载操作提供可配置的重试机制。 +/// This class implements and provides a configurable retry mechanism +/// for download operations. /// /// -/// 重试策略特性: +/// Retry policy features: /// -/// 指数退避每次重试的延迟时间按指数增长。 -/// 延迟计算公式:initialDelay * backoffMultiplier^attempt。 -/// 例如,初始延迟 1 秒、倍率 2.0 时,重试间隔为 1s、2s、4s、8s…… -/// 可配置最大重试次数默认最多重试 3 次(即总共最多执行 3 次尝试)。 -/// 可重试异常判断通过 IsRetryable 方法精确判断哪些异常值得重试, -/// 避免对 4xx 客户端错误等永久故障进行无效重试。 -/// 取消支持重试间隔期间会响应 CancellationToken 的取消请求。 +/// Exponential backoffDelay between retries grows exponentially. +/// Formula: initialDelay * backoffMultiplier^attempt. +/// For example, with an initial delay of 1 second and multiplier 2.0, retry intervals are 1s, 2s, 4s, 8s, etc. +/// Configurable maximum retriesDefaults to 3 attempts maximum. +/// Retryable exception detectionUses IsRetryable to precisely identify +/// which exceptions warrant a retry, avoiding wasted retries on permanent failures like 4xx client errors. +/// Cancellation supportResponds to CancellationToken cancellation requests during retry delays. /// /// /// -/// 可重试的异常类型: +/// Retryable exception types: /// -/// TaskCanceledException — 任务被取消(可能是超时引起)。 -/// TimeoutException — 操作超时。 -/// IOException — 网络 I/O 错误。 -/// 包含 timeout/500/502/503/504 状态码的 HttpRequestException +/// TaskCanceledException — Task was cancelled (possibly due to timeout). +/// TimeoutException — Operation timed out. +/// IOException — Network I/O error. +/// HttpRequestException containing timeout, 500, 502, 503, or 504 status codes. +/// +/// +/// +/// Non-retryable exception types: +/// +/// OperationCanceledException — User-initiated cancellation. +/// 4xx client errors (thrown as HttpRequestException that will not match the retryable conditions). /// /// /// @@ -46,11 +54,12 @@ public class DefaultRetryPolicy : IDownloadPolicy private readonly double _backoffMultiplier; /// - /// 使用指定的重试次数、初始延迟和退避倍率初始化重试策略。 + /// Initializes a new instance of the class + /// with the specified retry count, initial delay, and backoff multiplier. /// - /// 最大重试次数(包含首次尝试)。默认值为 3。 - /// 每次重试前的初始延迟时间。默认值为 1 秒。 - /// 退避倍率,每次重试的延迟时间按此倍率增长。默认值为 2.0。 + /// Maximum number of retry attempts (including the initial attempt). Defaults to 3. + /// Initial delay before the first retry. Defaults to 1 second. + /// Backoff multiplier applied to the delay after each retry. Defaults to 2.0. public DefaultRetryPolicy(int maxRetries = 3, TimeSpan? initialDelay = null, double backoffMultiplier = 2.0) { _maxRetries = maxRetries; @@ -59,22 +68,25 @@ public DefaultRetryPolicy(int maxRetries = 3, TimeSpan? initialDelay = null, dou } /// - /// 异步执行指定的操作,并在发生可重试异常时根据指数退避策略进行重试。 + /// Asynchronously executes the specified operation, retrying on retryable exceptions + /// using exponential backoff. /// - /// 操作返回的类型。 - /// 要执行的操作,接受 并返回 。 - /// 用于取消操作的取消令牌。 - /// 操作执行结果。 - /// 当操作通过取消令牌被取消时抛出(此异常不会被重试)。 + /// The return type of the operation. + /// The operation to execute, accepting a and returning a . + /// A to cancel the entire operation including retries. + /// The result of the operation on success. + /// Thrown when the operation is cancelled via (this exception is never retried). /// /// - /// 执行流程: + /// Execution flow: /// /// - /// 执行传入的操作。 - /// 如果操作成功,直接返回结果。 - /// 如果操作抛出可重试异常且尚未达到最大重试次数,记录警告日志,等待退避延迟后重试。 - /// 如果操作抛出不可重试异常或已达到最大重试次数,异常会向上传播(不被捕获)。 + /// Executes the provided operation. + /// If the operation succeeds, returns the result immediately. + /// If the operation throws a retryable exception and the maximum retry count has not been reached, + /// logs a warning, waits for the computed backoff delay, and retries. + /// If the operation throws a non-retryable exception or the maximum retry count has been reached, + /// the exception propagates upward (not caught). /// /// public async Task ExecuteAsync(Func> action, CancellationToken token = default) @@ -95,22 +107,22 @@ public async Task ExecuteAsync(Func> action, Ca } /// - /// 判断指定异常是否可重试。 + /// Determines whether the specified exception is retryable. /// - /// 要检查的异常。 - /// 如果异常属于可重试类型(超时、网络 I/O、5xx 服务器错误)则返回 true;否则返回 false。 + /// The exception to examine. + /// True if the exception is of a retryable type (timeout, network I/O, 5xx server error); otherwise false. /// - /// 以下异常被认为是可重试的: + /// The following exceptions are considered retryable: /// - /// TaskCanceledException — 任务被取消(通常由超时引起)。 - /// TimeoutException — 操作超时。 - /// IOException — 网络 I/O 错误。 - /// 包含 "timeout"、"500"、"502"、"503" 或 "504" 的 HttpRequestException + /// TaskCanceledException — Task was cancelled (typically caused by a timeout). + /// TimeoutException — Operation timed out. + /// IOException — Network I/O error. + /// HttpRequestException with a message containing "timeout", "500", "502", "503", or "504". /// - /// 以下异常被认为是不可重试的: + /// The following exceptions are NOT considered retryable: /// - /// OperationCanceledException — 用户主动取消。 - /// 4xx 客户端错误(由 HttpRequestException 抛出时不会匹配上述条件)。 + /// OperationCanceledException — User-initiated cancellation. + /// 4xx client errors (when thrown as HttpRequestException, they will not match the retryable conditions above). /// /// private static bool IsRetryable(Exception ex) diff --git a/src/c#/GeneralUpdate.Core/Download/Progress/DownloadProgressReporter.cs b/src/c#/GeneralUpdate.Core/Download/Progress/DownloadProgressReporter.cs index 24369e89..ab99eb2f 100644 --- a/src/c#/GeneralUpdate.Core/Download/Progress/DownloadProgressReporter.cs +++ b/src/c#/GeneralUpdate.Core/Download/Progress/DownloadProgressReporter.cs @@ -7,23 +7,25 @@ namespace GeneralUpdate.Core.Download.Progress; /// -/// 下载进度报告器,将 进度事件桥接到 -/// 事件系统,为传统事件监听器提供向后兼容的订阅方式。 +/// Download progress reporter that bridges progress events +/// to the event system, providing backwards-compatible +/// subscription for legacy event listeners. /// /// /// -/// 此类实现了 接口(其中 T 为 DownloadProgress), -/// 在报告下载进度时,同时触发以下事件: +/// This class implements (where T is DownloadProgress) +/// and triggers the following events when reporting download progress: /// /// -/// ProgressEventArgs每次报告进度时触发,包含下载百分比、已下载字节数等信息。 -/// MultiDownloadCompletedEventArgs当下载状态为 Completed 时触发。 -/// MultiDownloadErrorEventArgs当下载状态为 Failed 时触发。 -/// MultiAllDownloadCompletedEventArgs通过 DispatchAllCompleted 静态方法触发,表示所有下载任务已完成。 +/// ProgressEventArgsFired on every progress report, +/// containing the download percentage, bytes downloaded, and other information. +/// MultiDownloadCompletedEventArgsFired when the download status is Completed. +/// MultiDownloadErrorEventArgsFired when the download status is Failed. +/// MultiAllDownloadCompletedEventArgsFired via the static DispatchAllCompleted method when all download tasks are finished. /// /// -/// 此类同时也支持直接注入 onProgressonCompleted 回调委托, -/// 作为除 EventManager 之外的另一条通知通道。 +/// This class also supports direct injection of onProgress and onCompleted +/// callback delegates as an alternative notification channel alongside EventManager. /// /// public class DownloadProgressReporter : IProgress @@ -32,10 +34,11 @@ public class DownloadProgressReporter : IProgress private readonly Action? _onCompleted; /// - /// 使用可选的进度回调和完成回调初始化进度报告器。 + /// Initializes a new instance of the class + /// with optional progress and completion callbacks. /// - /// 每次报告进度时调用的回调委托。 - /// 下载完成时调用的回调委托。 + /// A callback delegate invoked on each progress report. + /// A callback delegate invoked when the download completes. public DownloadProgressReporter( Action? onProgress = null, Action? onCompleted = null) @@ -45,9 +48,10 @@ public DownloadProgressReporter( } /// - /// 报告下载进度。触发进度回调、EventManager 事件,并根据下载状态触发完成或失败事件。 + /// Reports download progress. Invokes the progress callback, fires EventManager events, + /// and triggers completion or failure events based on the download status. /// - /// 包含当前下载进度信息的 实例。 + /// A instance containing the current download progress information. public void Report(Models.DownloadProgress value) { _onProgress?.Invoke(value); @@ -70,12 +74,13 @@ public void Report(Models.DownloadProgress value) } /// - /// 触发所有下载完成事件。此方法应在所有下载任务完成后调用一次,而不是每个资产完成后调用。 - /// 通常由下载编排器(Download Orchestrator)在全部下载完成后调用。 + /// Dispatches the all-downloads-completed event. This method should be called once + /// after all download tasks are finished, not after each individual asset completes. + /// Typically called by the download orchestrator after all downloads have completed. /// - /// 事件发送者。 - /// 所有下载是否全部成功。 - /// 每个下载资产的详情列表,包含资产对象和文件名。 + /// The event sender. + /// Whether all downloads completed successfully. + /// The list of details for each downloaded asset, containing the asset object and file name. public static void DispatchAllCompleted(object sender, bool success, List<(object, string)> details) { EventManager.Instance.Dispatch(sender, @@ -83,12 +88,14 @@ public static void DispatchAllCompleted(object sender, bool success, List<(objec } /// - /// 创建一个将进度事件分发给 EventManager 的 实例。 + /// Creates an instance that dispatches progress events + /// to the EventManager. /// - /// 一个新的 实例,用于桥接进度报告到事件系统。 + /// A new instance bridging progress reporting to the event system. /// - /// 此工厂方法创建的报告器不包含自定义回调,仅通过 EventManager 分发事件。 - /// 适合只需订阅 EventManager 事件而不需要直接回调的场景。 + /// This factory method creates a reporter without custom callbacks, dispatching events + /// exclusively through EventManager. Suitable for scenarios where only EventManager + /// subscriptions are needed without direct callbacks. /// public static IProgress CreateEventBridge() => new DownloadProgressReporter(); diff --git a/src/c#/GeneralUpdate.Core/Download/Reporting/IUpdateReporter.cs b/src/c#/GeneralUpdate.Core/Download/Reporting/IUpdateReporter.cs index 1bdb27d8..316b8712 100644 --- a/src/c#/GeneralUpdate.Core/Download/Reporting/IUpdateReporter.cs +++ b/src/c#/GeneralUpdate.Core/Download/Reporting/IUpdateReporter.cs @@ -9,68 +9,79 @@ namespace GeneralUpdate.Core.Download.Reporting; /// -/// 更新状态报告器接口,用于向远程服务器(兼容 GeneralSpacestation API)报告更新生命周期事件。 +/// Defines a contract for reporting update lifecycle events to a remote server +/// compatible with the GeneralSpacestation API. /// /// /// -/// 实现此接口的类负责将更新过程中的状态变化上报到远程服务,以便跟踪更新进度和结果。 -/// 典型的更新状态包括: +/// Implementations are responsible for sending update status changes to a remote service +/// so that the update progress and outcome can be tracked centrally. +/// Typical update statuses include: +/// /// -/// — 更新正在进行中。 -/// — 更新成功完成。 -/// — 更新失败。 +/// — The update is in progress. +/// — The update completed successfully. +/// — The update failed. /// -/// /// -/// 默认实现为 (空操作),当未配置 ReportUrl 时使用。 -/// 标准实现为 ,通过 HTTP POST 将更新状态发送到指定端点。 +/// The default no-op implementation is used when no ReportUrl is configured. +/// The standard implementation sends status via HTTP POST to a configured endpoint. /// /// public interface IUpdateReporter { /// - /// 异步报告更新状态到远程服务器。 + /// Asynchronously reports the update status to the remote server. /// - /// 包含记录 ID、状态码和类型的更新报告数据。 - /// 用于取消报告操作的取消令牌。 - /// 表示异步操作的任务。 + /// The containing the record ID, status code, and type. + /// A to cancel the report operation. + /// A task representing the asynchronous operation. Task ReportAsync(UpdateReport report, CancellationToken token = default); } /// -/// 更新状态枚举,匹配 GeneralSpacestation API 的 ReportDTO 协定。 -/// 1 = 正在更新,2 = 成功,3 = 失败。 +/// Enumerates update status values that match the GeneralSpacestation API ReportDTO contract. /// +/// +/// Maps to the following integer codes: +/// +/// 1 = — The update is currently in progress. +/// 2 = — The update completed successfully. +/// 3 = — The update failed. +/// +/// public enum UpdateStatus { Updating = 1, Success = 2, Failure = 3 } /// -/// 与 GeneralSpacestation 兼容的更新报告记录。 -/// 包含版本验证返回的记录 ID(RecordId)、状态码(Status)和类型(Type)。 +/// Represents an update report record compatible with the GeneralSpacestation API. +/// Contains the record ID returned from version validation, the status code, and the update type. /// -/// 版本验证时返回的记录 ID,用于标识本次更新记录。 -/// 更新状态码:1=正在更新,2=成功,3=失败。默认为 1(正在更新)。 -/// 更新类型:1=升级,2=推送。默认为 1(升级)。 +/// The record ID returned from version validation, used to identify this update record. +/// The update status code: 1 = Updating, 2 = Success, 3 = Failure. Defaults to 1 (Updating). +/// The update type: 1 = Upgrade, 2 = Push. Defaults to 1 (Upgrade). /// -/// 此记录序列化为 JSON 时使用 camelCase 命名策略。 -/// 示例 JSON:{"recordId": 123, "status": 1, "type": 1} +/// This record is serialized to JSON using camelCase naming policy. +/// Example JSON: {"recordId": 123, "status": 1, "type": 1} /// public record UpdateReport(int RecordId, int Status = 1, int Type = 1); /// -/// 基于 HTTP POST 的更新状态报告器,将 序列化为 JSON -/// 并发送到指定的远程端点,格式与 GeneralSpacestation ReportDTO 兼容。 +/// An HTTP POST-based update status reporter that serializes to JSON +/// and sends it to a configured remote endpoint. Compatible with the GeneralSpacestation ReportDTO format. /// /// /// -/// 此类实现了 接口,是标准的 HTTP 更新状态上报实现。 +/// This class implements and provides the standard HTTP implementation +/// for reporting update status to a remote server. /// /// -/// 工作流程: +/// Workflow: /// -/// UpdateReport 序列化为 JSON 字符串(使用 camelCase 命名策略)。 -/// 创建 HTTP POST 请求,设置 Content-Type 为 application/json。 -/// 发送请求到配置的报告 URL。 -/// 如果请求失败(如网络错误),记录警告日志但不抛出异常,避免影响更新主流程。 +/// Serializes the to a JSON string using camelCase naming policy. +/// Creates an HTTP POST request with Content-Type set to application/json. +/// Sends the request to the configured report URL. +/// If the request fails (e.g., network error), logs a warning without throwing an exception, +/// to avoid disrupting the main update flow. /// /// /// @@ -80,10 +91,10 @@ public class HttpUpdateReporter : IUpdateReporter private readonly string _reportUrl; /// - /// 使用指定的 HTTP 客户端和报告 URL 初始化 HTTP 报告器。 + /// Initializes a new instance of the class. /// - /// 用于发送 HTTP 请求的 实例。 - /// 接收更新状态报告的远程 URL。 + /// The instance used to send HTTP requests. + /// The remote URL that receives the update status reports. public HttpUpdateReporter(HttpClient client, string reportUrl) { _client = client; @@ -109,13 +120,19 @@ public async Task ReportAsync(UpdateReport report, CancellationToken token = def } /// -/// 空操作(No-op)更新状态报告器,当未配置 ReportUrl 时使用。 -/// 所有报告操作都立即返回已完成的任务,不执行任何实际操作。 +/// A no-op (null object) update status reporter that performs no actual work. +/// Used when no ReportUrl is configured. /// /// -/// 此类实现了空对象模式(Null Object Pattern), -/// 避免在使用方代码中需要判空处理。 -/// 当不需要向服务器报告更新状态时(如本地测试或未配置报告端点),使用此实现。 +/// +/// This class implements the Null Object Pattern, eliminating the need for null checks +/// in consumer code. Every report operation returns a completed task immediately without +/// performing any actual data transmission. +/// +/// +/// Use this implementation when no remote status reporting is needed, +/// such as during local testing or when the report endpoint is not configured. +/// /// public class NoOpUpdateReporter : IUpdateReporter { diff --git a/src/c#/GeneralUpdate.Core/Download/Sources/HttpDownloadSource.cs b/src/c#/GeneralUpdate.Core/Download/Sources/HttpDownloadSource.cs index 5cd47d17..4a9714a7 100644 --- a/src/c#/GeneralUpdate.Core/Download/Sources/HttpDownloadSource.cs +++ b/src/c#/GeneralUpdate.Core/Download/Sources/HttpDownloadSource.cs @@ -9,27 +9,30 @@ namespace GeneralUpdate.Core.Download.Sources; /// -/// HTTP 下载源,通过调用版本验证 API 获取更新资产列表。 -/// 分别对客户端(Client)和升级端(Upgrade)执行版本验证, -/// 并将服务器响应转换为 列表。 +/// HTTP download source that retrieves update asset lists by calling a version-validation API. +/// Performs version validation for both Client and Upgrade app types, +/// then maps the server responses into lists. /// /// /// -/// 此类实现了 接口,是标准 HTTP 更新流程的下载源实现。 +/// This class implements and serves as the standard HTTP-based +/// download source for the update workflow. /// /// -/// 工作流程: +/// Workflow: /// -/// 调用 VersionService.Validate 分别对 AppType.ClientAppType.Upgrade -/// 进行版本验证。升级端版本优先使用 upgradeClientVersion,如果未配置则使用 clientVersion -/// 将两次验证返回的版本信息映射为 对象。 -/// 按 URL 对资产进行去重(两个验证调用可能返回相同的包)。 -/// 返回包含资产列表和更新标志的 +/// Calls VersionService.Validate for AppType.Client to validate the main application version. +/// Calls VersionService.Validate for AppType.Upgrade to validate the upgrade client version. +/// The upgrade client version uses upgradeClientVersion if provided; otherwise falls back to clientVersion. +/// Maps the version information returned by both validations into objects. +/// Deduplicates assets by URL (both validation calls may return the same package). +/// Returns a containing the asset list and update flags. /// /// /// -/// 两次验证调用的设计是为了支持客户端自身更新和主应用程序更新的场景: -/// 客户端更新包和应用程序更新包可能来自同一个版本服务器,但属于不同的应用类型。 +/// The two-step validation design supports scenarios where the client's own update package +/// and the main application's update package come from the same version server but belong +/// to different application types. /// /// public class HttpDownloadSource : Abstractions.IDownloadSource @@ -44,17 +47,20 @@ public class HttpDownloadSource : Abstractions.IDownloadSource private readonly string? _token; /// - /// 使用指定的更新配置初始化 HTTP 下载源。 + /// Initializes a new instance of the class + /// with the specified update configuration parameters. /// - /// 版本验证 API 的 URL。 - /// 当前客户端版本号。 - /// 升级端(Upgrade 应用)的当前版本号。 - /// 如果为 null 或为空,则使用 。 - /// 应用密钥,用于 API 身份验证。 - /// 目标平台类型(Windows、Linux、macOS 等)。 - /// 可选的产品 ID。 - /// 可选的认证方案。 - /// 可选的认证令牌。 + /// The URL of the version-validation API. + /// The current client version string. + /// + /// The current version string of the Upgrade application. + /// If null or empty, is used instead. + /// + /// The application secret key used for API authentication. + /// The target platform type (Windows, Linux, macOS, etc.). + /// An optional product identifier. + /// An optional authentication scheme. + /// An optional authentication token. public HttpDownloadSource( string updateUrl, string clientVersion, @@ -76,24 +82,28 @@ public HttpDownloadSource( } /// - /// 异步获取下载资产列表,通过对客户端和升级端分别执行版本验证, - /// 合并结果并按 URL 去重。 + /// Asynchronously retrieves the list of downloadable assets by performing version validation + /// for both Client and Upgrade app types, merging the results, and deduplicating by URL. /// - /// 可选的取消令牌。 - /// 包含资产列表和更新标志(HasMainUpdate、HasUpgradeUpdate)的 - /// + /// An optional to cancel the operation. + /// + /// A containing the merged and deduplicated list of + /// objects, along with flags indicating whether main and/or + /// upgrade updates are available. + /// /// /// - /// 此方法会调用 VersionService.Validate 两次: + /// This method calls VersionService.Validate twice: /// /// - /// 第一次使用 AppType.Client 验证主应用的更新。 - /// 第二次使用 AppType.Upgrade 验证升级程序自身的更新。 + /// First with AppType.Client to validate the main application update. + /// Second with AppType.Upgrade to validate the upgrade program's own update. /// /// - /// 两次调用使用相同的 _updateUrl_appSecretKey_platform, - /// 但使用不同的 AppType。这允许服务器根据应用类型返回不同的更新包。 - /// 结果中的资产列表会按 URL 去重,因为两个验证调用可能返回相同的包。 + /// Both calls use the same _updateUrl, _appSecretKey, and _platform, + /// but with different AppType values. This allows the server to return different + /// update packages depending on the application type. + /// The resulting asset list is deduplicated by URL, as both validation calls may return the same packages. /// /// public async Task ListAsync(CancellationToken token = default) @@ -135,11 +145,13 @@ public async Task ListAsync(CancellationToken token = defa } /// - /// 将服务器返回的 对象映射为 对象。 + /// Maps a server-returned object to a object. /// - /// 服务器返回的版本信息。 - /// 转换后的 实例,包含名称、URL、大小、哈希值、 - /// 版本号、强制更新标志、冻结标志、认证信息等属性。 + /// The version information returned by the server. + /// + /// A instance populated with the name, URL, size, hash value, + /// version number, forced-update flag, freeze flag, authentication information, and other metadata. + /// private static DownloadAsset MapVersionInfo(VersionInfo v) { return new DownloadAsset( diff --git a/src/c#/GeneralUpdate.Core/Download/Sources/OssDownloadSource.cs b/src/c#/GeneralUpdate.Core/Download/Sources/OssDownloadSource.cs index 23df4668..cd2ecb62 100644 --- a/src/c#/GeneralUpdate.Core/Download/Sources/OssDownloadSource.cs +++ b/src/c#/GeneralUpdate.Core/Download/Sources/OssDownloadSource.cs @@ -13,13 +13,28 @@ namespace GeneralUpdate.Core.Download.Sources; /// -/// OSS (Object Storage Service) download source. -/// Downloads the version configuration JSON from a remote URL, -/// parses it, and returns a list of for the orchestrator. +/// OSS (Object Storage Service) download source that retrieves a version configuration JSON +/// from a remote URL, parses it, and returns a list of objects. /// /// -/// Supports AliYun, AWS S3, MinIO, and Tencent COS via signed URLs. +/// +/// This class implements and supports object storage services +/// such as AliYun OSS, AWS S3, MinIO, and Tencent COS via pre-signed URLs. /// The version JSON format uses records. +/// +/// +/// Workflow: +/// +/// Downloads the version JSON file from the configured URL via HTTP GET. +/// Parses the JSON into a list of records using source-generated serialization context. +/// Converts each record into a with name, URL, hash, and version. +/// Orders the assets by publish time (PubTime) in ascending order. +/// Returns a containing the ordered asset list. +/// +/// +/// +/// Each asset's file name is derived from PacketName (or Version if null) with a ".zip" extension. +/// /// public class OssDownloadSource : IDownloadSource { @@ -27,6 +42,13 @@ public class OssDownloadSource : IDownloadSource private readonly string _versionJsonUrl; private readonly TimeSpan _timeout; + /// + /// Initializes a new instance of the class. + /// + /// The instance used to download the version JSON. Must not be null. + /// The URL of the version configuration JSON file. Must not be null or empty. + /// Optional timeout for the HTTP request. Defaults to 60 seconds if not specified. + /// Thrown when or is null. public OssDownloadSource(HttpClient httpClient, string versionJsonUrl, TimeSpan? timeout = null) { _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); @@ -34,7 +56,29 @@ public OssDownloadSource(HttpClient httpClient, string versionJsonUrl, TimeSpan? _timeout = timeout ?? TimeSpan.FromSeconds(60); } - /// + /// + /// Asynchronously retrieves the list of downloadable assets by downloading and parsing + /// the version configuration JSON from the OSS URL. + /// + /// A to cancel the operation. + /// + /// A containing the list of objects + /// ordered by publish time, and flags indicating whether any updates are available. + /// + /// Thrown when a version record has a null or empty download URL. + /// + /// + /// The method performs the following steps: + /// + /// + /// Creates a linked cancellation token with the configured timeout. + /// Sends an HTTP GET request to download the complete version JSON response. + /// Deserializes the JSON into a list of records. + /// If no records are found, returns an empty result. + /// Otherwise, orders records by PubTime, converts each to a , + /// and returns the result with both HasMainUpdate and HasUpgradeUpdate set to true. + /// + /// public async Task ListAsync(CancellationToken token = default) { // Download and parse the version JSON from OSS diff --git a/src/c#/GeneralUpdate.Core/Event/EventManager.cs b/src/c#/GeneralUpdate.Core/Event/EventManager.cs index a90d9332..e58c7973 100644 --- a/src/c#/GeneralUpdate.Core/Event/EventManager.cs +++ b/src/c#/GeneralUpdate.Core/Event/EventManager.cs @@ -7,29 +7,31 @@ namespace GeneralUpdate.Core.Event { /// - /// 线程安全的事件管理器(发布-订阅模式),基于 实现。 - /// 支持无锁竞争地添加、移除和分发事件处理器。 + /// Thread-safe event manager implementing the publish-subscribe pattern, backed by + /// . + /// Supports lock-free addition, removal, and dispatching of event handlers. /// /// /// - /// EventManager 使用单例模式(通过 属性), - /// 是整个 GeneralUpdate 更新生命周期中所有事件的统一调度中心。 + /// EventManager uses the singleton pattern (via the property) + /// and serves as the central dispatch hub for all events throughout the GeneralUpdate + /// update lifecycle. /// /// - /// 核心设计原则: + /// Core design principles: /// - /// 单例:全局唯一实例,确保所有组件共享同一个事件总线。 - /// 线程安全:使用 ConcurrentDictionary 避免锁竞争。 - /// 泛型事件:以 Action<object, TEventArgs> 为事件委托类型, - /// 按 TEventArgs 类型自动路由。 - /// 错误隔离:单个处理器的异常不会影响其他处理器。 - /// IDisposable:支持释放时清除所有已注册的处理器。 + /// Singleton: A single global instance ensures all components share the same event bus. + /// Thread-safe: Uses ConcurrentDictionary to avoid lock contention. + /// Generic events: Uses Action<object, TEventArgs> as the event delegate type, + /// automatically routing by TEventArgs type. + /// Error isolation: An exception in one handler does not affect other handlers. + /// IDisposable: Clears all registered handlers on disposal. /// /// /// - /// 此管理器被 内部使用, - /// 在更新流程的关键节点(如下载进度、异常、完成等)触发事件。 - /// 外部可以通过 接口批量订阅所有事件类型。 + /// This manager is used internally by + /// to fire events at key points in the update flow (e.g., download progress, exceptions, completion). + /// External consumers can subscribe to all event types in bulk via the interface. /// /// public class EventManager : IDisposable @@ -41,23 +43,24 @@ public class EventManager : IDisposable private EventManager() { } /// - /// 获取 的单例实例。 + /// Gets the singleton instance of . /// - /// 全局唯一的事件管理器实例。 + /// The global unique event manager instance. /// - /// 使用 实现线程安全的延迟初始化。 + /// Uses for thread-safe lazy initialization. /// public static EventManager Instance => _lazy.Value; - /// - /// 注册一个指定事件类型的监听器。 + /// + /// Registers a listener for a specified event type. /// - /// 事件参数的类型,必须是 的子类。 - /// 要注册的事件处理器委托。 - /// null 时抛出。 + /// The type of the event argument, which must be a subclass of . + /// The event handler delegate to register. + /// Thrown when is null. /// - /// 同一类型的事件支持注册多个监听器,内部通过 实现多播委托。 - /// 重复添加同一监听器实例会导致该监听器被调用多次。 + /// Multiple listeners can be registered for the same event type. Internally, this uses + /// to build a multicast delegate. + /// Adding the same listener instance multiple times will cause it to be invoked multiple times. /// public void AddListener(Action listener) where TEventArgs : EventArgs { @@ -69,13 +72,14 @@ public void AddListener(Action listener) where T } /// - /// 移除指定事件类型的监听器。 + /// Removes a listener for a specified event type. /// - /// 事件参数的类型,必须是 的子类。 - /// 要移除的事件处理器委托。 - /// null 时抛出。 + /// The type of the event argument, which must be a subclass of . + /// The event handler delegate to remove. + /// Thrown when is null. /// - /// 如果移除后该事件类型的委托列表为空,则自动从字典中移除该事件类型的条目。 + /// If the delegate list for this event type becomes empty after removal, the entry is + /// automatically removed from the dictionary. /// public void RemoveListener(Action listener) where TEventArgs : EventArgs { @@ -92,19 +96,21 @@ public void RemoveListener(Action listener) wher } /// - /// 分发指定类型的事件到所有已注册的监听器。 + /// Dispatches an event of the specified type to all registered listeners. /// - /// 事件参数的类型,必须是 的子类。 - /// 事件发送者。 - /// 事件参数。 - /// null 时抛出。 + /// The type of the event argument, which must be a subclass of . + /// The event sender. + /// The event arguments. + /// Thrown when or is null. /// /// - /// 分发策略: + /// Dispatch strategy: /// - /// 通过 TEventArgs 类型自动查找对应的委托列表。 - /// 逐个调用每个已注册的监听器,确保一个监听器的异常不会影响其他监听器。 - /// 监听器内发生的异常会被记录到 ,不会被重新抛出。 + /// Looks up the delegate list automatically by the TEventArgs type. + /// Invokes each registered listener individually, ensuring that an exception + /// in one listener does not prevent others from being called. + /// Exceptions thrown inside listeners are logged via + /// and are not rethrown. /// /// /// @@ -133,19 +139,21 @@ public void Dispatch(object sender, TEventArgs eventArgs) where TEve } /// - /// 清除所有已注册的事件监听器。 + /// Clears all registered event listeners. /// /// - /// 此操作不可逆,调用后所有已注册的 注册的处理器将被移除。 + /// This operation is irreversible. After calling this method, all handlers registered via + /// are removed. /// public void Clear() => _dicDelegates.Clear(); /// - /// 释放事件管理器,清除所有已注册的监听器。 + /// Releases all resources used by the and clears all registered listeners. /// /// - /// 实现 接口,确保在组件生命周期结束时清理事件订阅, - /// 防止内存泄漏。多次调用是安全的,第二次及后续调用不会重复清理。 + /// Implements to ensure event subscriptions are cleaned up when the + /// component lifecycle ends, preventing memory leaks. Multiple calls are safe; subsequent calls + /// will not perform any cleanup. /// public void Dispose() { diff --git a/src/c#/GeneralUpdate.Core/Event/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Core/Event/ExceptionEventArgs.cs index 1f7b526f..cc64f6fe 100644 --- a/src/c#/GeneralUpdate.Core/Event/ExceptionEventArgs.cs +++ b/src/c#/GeneralUpdate.Core/Event/ExceptionEventArgs.cs @@ -3,29 +3,32 @@ namespace GeneralUpdate.Core.Event; /// -/// 更新流程中的异常事件参数,封装异常对象和自定义错误消息。 +/// Event arguments for exceptions that occur during the update flow, +/// encapsulating the exception object and a custom error message. /// /// /// -/// 当更新流程中的某个环节(如下载、解压、文件操作等)发生异常时, -/// 会通过 分发此事件参数。 +/// When an exception occurs in any stage of the update process (e.g., download, +/// extraction, file operations), this event argument is dispatched through +/// . /// /// -/// 事件接收方可以通过 属性获取详细的异常信息, -/// 通过 属性获取可读的错误描述。 +/// Event receivers can obtain detailed exception information via the +/// property and a human-readable error description +/// via the property. /// /// public class ExceptionEventArgs(Exception? exception = null, string? message = null) : EventArgs { /// - /// 获取与事件关联的异常对象。 + /// Gets the exception object associated with the event. /// - /// 可能为 null,如果异常信息仅通过文本消息传递。 + /// May be null if the error information is conveyed only through a text message. public Exception Exception { get; private set; } = exception; /// - /// 获取自定义的错误描述消息。 + /// Gets the custom error description message. /// - /// 可能为 null,如果未提供自定义消息。 + /// May be null if no custom message was provided. public string Message { get; private set; } = message; -} \ No newline at end of file +} diff --git a/src/c#/GeneralUpdate.Core/Event/IUpdateEventListener.cs b/src/c#/GeneralUpdate.Core/Event/IUpdateEventListener.cs index bf0a7d07..845468a5 100644 --- a/src/c#/GeneralUpdate.Core/Event/IUpdateEventListener.cs +++ b/src/c#/GeneralUpdate.Core/Event/IUpdateEventListener.cs @@ -3,85 +3,89 @@ namespace GeneralUpdate.Core.Event; /// -/// 更新事件监听器接口,定义了更新生命周期中所有事件类型的批量注册契约。 +/// Defines the update event listener interface, which establishes the contract +/// for bulk subscription to all event types in the update lifecycle. /// /// /// -/// 实现此接口并通过 new GeneralUpdateBootstrap().AddEventListener<MyListener>() 注册, -/// 即可接收更新流程中各个阶段的事件通知。 +/// Implement this interface and register it via +/// new GeneralUpdateBootstrap().AddEventListener<MyListener>() +/// to receive event notifications at each stage of the update process. /// /// -/// 事件类型覆盖完整的更新生命周期: +/// The event types cover the full update lifecycle: /// -/// :发现可用更新版本信息时触发。 -/// / :下载过程中的统计和进度信息。 -/// / :下载完成通知。 -/// :下载出错时触发。 -/// :更新流程中发生异常时触发。 +/// : Fires when update version information is found. +/// / : Download statistics and progress information. +/// / : Download completion notifications. +/// : Fires when a download error occurs. +/// : Fires when an exception occurs during the update flow. /// /// /// -/// 如果只需要关注部分事件,建议继承 基类, -/// 仅重写需要处理的方法。 +/// If you only need to handle a subset of events, consider inheriting from +/// and overriding only the methods you care about. /// /// public interface IUpdateEventListener { /// - /// 所有下载任务全部完成时触发。 + /// Fires when all download tasks have completed. /// - /// 包含所有下载完成状态的事件参数。 + /// Event arguments containing the overall download completion status. void OnAllDownloadCompleted(MultiAllDownloadCompletedEventArgs args); /// - /// 单个下载任务完成时触发。 + /// Fires when a single download task has completed. /// - /// 包含单个下载完成状态的事件参数。 + /// Event arguments containing the completion status of a single download. void OnDownloadCompleted(MultiDownloadCompletedEventArgs args); /// - /// 下载过程中发生错误时触发。 + /// Fires when an error occurs during a download. /// - /// 包含下载错误信息的事件参数。 + /// Event arguments containing download error information. void OnDownloadError(MultiDownloadErrorEventArgs args); /// - /// 下载统计数据更新时触发。 + /// Fires when download statistics are updated. /// - /// 包含下载统计信息(速度、进度等)的事件参数。 + /// Event arguments containing download statistics (speed, progress, etc.). void OnDownloadStatistics(MultiDownloadStatisticsEventArgs args); /// - /// 获取到更新版本信息时触发。 + /// Fires when update version information is retrieved. /// - /// 包含更新版本信息的事件参数。 + /// Event arguments containing the update version information. void OnUpdateInfo(UpdateInfoEventArgs args); /// - /// 更新流程中发生异常时触发。 + /// Fires when an exception occurs during the update flow. /// - /// 包含异常信息的事件参数。 + /// Event arguments containing exception information. void OnException(ExceptionEventArgs args); /// - /// 实时下载进度更新时触发。 + /// Fires when real-time download progress is updated. /// - /// 包含下载进度数据的事件参数。 + /// Event arguments containing download progress data. void OnProgress(ProgressEventArgs args); } /// -/// 的基类,提供所有事件方法的空实现。 -/// 继承此类并仅重写需要处理的事件方法,避免实现接口时必须实现所有方法的负担。 +/// Base class for providing empty default implementations +/// for all event methods. +/// Inherit from this class and override only the event methods you wish to handle, +/// avoiding the burden of implementing every method in the interface. /// /// -/// 使用示例: +/// Usage example: /// /// public class MyListener : UpdateEventListenerBase /// { /// public override void OnProgress(ProgressEventArgs args) /// { -/// Console.WriteLine($"进度: {args.Progress?.ProgressValue}%"); +/// Console.WriteLine($"Progress: {args.Progress?.ProgressValue}%"); /// } /// } /// @@ -89,44 +93,51 @@ public interface IUpdateEventListener public abstract class UpdateEventListenerBase : IUpdateEventListener { /// - /// 所有下载任务完成时的空实现。重写此方法以处理该事件。 + /// Empty default implementation for the all-downloads-completed event. + /// Override this method to handle the event. /// - /// 事件参数。 + /// Event arguments. public virtual void OnAllDownloadCompleted(MultiAllDownloadCompletedEventArgs args) { } /// - /// 单个下载任务完成时的空实现。重写此方法以处理该事件。 + /// Empty default implementation for the single-download-completed event. + /// Override this method to handle the event. /// - /// 事件参数。 + /// Event arguments. public virtual void OnDownloadCompleted(MultiDownloadCompletedEventArgs args) { } /// - /// 下载错误事件的空实现。重写此方法以处理该事件。 + /// Empty default implementation for the download error event. + /// Override this method to handle the event. /// - /// 事件参数。 + /// Event arguments. public virtual void OnDownloadError(MultiDownloadErrorEventArgs args) { } /// - /// 下载统计信息更新的空实现。重写此方法以处理该事件。 + /// Empty default implementation for the download statistics update event. + /// Override this method to handle the event. /// - /// 事件参数。 + /// Event arguments. public virtual void OnDownloadStatistics(MultiDownloadStatisticsEventArgs args) { } /// - /// 更新信息可用时的空实现。重写此方法以处理该事件。 + /// Empty default implementation for the update information available event. + /// Override this method to handle the event. /// - /// 事件参数。 + /// Event arguments. public virtual void OnUpdateInfo(UpdateInfoEventArgs args) { } /// - /// 异常事件发生的空实现。重写此方法以处理该事件。 + /// Empty default implementation for the exception event. + /// Override this method to handle the event. /// - /// 事件参数。 + /// Event arguments. public virtual void OnException(ExceptionEventArgs args) { } /// - /// 下载进度更新的空实现。重写此方法以处理该事件。 + /// Empty default implementation for the download progress update event. + /// Override this method to handle the event. /// - /// 事件参数。 + /// Event arguments. public virtual void OnProgress(ProgressEventArgs args) { } } diff --git a/src/c#/GeneralUpdate.Core/Event/ProgressEventArgs.cs b/src/c#/GeneralUpdate.Core/Event/ProgressEventArgs.cs index 545d0042..a6e6f9b0 100644 --- a/src/c#/GeneralUpdate.Core/Event/ProgressEventArgs.cs +++ b/src/c#/GeneralUpdate.Core/Event/ProgressEventArgs.cs @@ -5,48 +5,56 @@ namespace GeneralUpdate.Core.Event; /// -/// 进度事件参数,封装下载进度或差异补丁进度的快照信息。 +/// Event arguments for progress updates, encapsulating a snapshot of +/// download progress or differential patch progress. /// /// /// -/// ProgressEventArgs 承载两种可能的进度数据类型: +/// ProgressEventArgs carries one of two possible progress data types: /// -/// :文件下载过程中的进度信息(下载速度、已完成字节数、总字节数等)。 -/// :差异补丁生成或应用过程中的进度信息。 +/// : Progress information during file download +/// (download speed, bytes completed, total bytes, etc.). +/// : Progress information during differential patch +/// generation or application. /// /// /// -/// 事件接收方应检查 哪个不为 null, -/// 以确定当前进度事件的类型。两个属性不会同时有值。 +/// Event receivers should check which of and +/// is not null to determine the current +/// progress event type. Both properties will never be set simultaneously. /// /// public class ProgressEventArgs : EventArgs { /// - /// 获取下载进度的快照对象。 + /// Gets the download progress snapshot object. /// - /// 如果当前事件是下载进度更新,则返回 实例;否则为 null + /// A instance if this event is a download progress update; + /// otherwise null. public DownloadProgress? Progress { get; } /// - /// 获取差异补丁进度的快照对象。 + /// Gets the differential patch progress snapshot object. /// - /// 如果当前事件是差异补丁进度更新,则返回 实例;否则为 null + /// A instance if this event is a differential patch progress update; + /// otherwise null. public DiffProgress? DiffProgress { get; } /// - /// 使用下载进度数据初始化 的新实例。 + /// Initializes a new instance of the class + /// with download progress data. /// - /// 下载进度快照。 + /// The download progress snapshot. public ProgressEventArgs(DownloadProgress progress) { Progress = progress; } /// - /// 使用差异补丁进度数据初始化 的新实例。 + /// Initializes a new instance of the class + /// with differential patch progress data. /// - /// 差异补丁进度快照。 + /// The differential patch progress snapshot. public ProgressEventArgs(DiffProgress diffProgress) { DiffProgress = diffProgress; diff --git a/src/c#/GeneralUpdate.Core/FileSystem/ComparisonResult.cs b/src/c#/GeneralUpdate.Core/FileSystem/ComparisonResult.cs index dc05a489..c79e8ee4 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/ComparisonResult.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/ComparisonResult.cs @@ -3,18 +3,18 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 两个目录比较操作的结果,包含左侧(基准)和右侧(目标)的完整节点列表以及差异节点列表。 +/// Represents the result of a directory comparison operation, containing the full node lists for the left (base) and right (target) directories, as well as the list of differing nodes. /// /// /// -/// ComparisonResult 由 方法生成, -/// 它将比较结果组织为三个维度: +/// ComparisonResult is generated by the method. +/// It organizes the comparison results into three dimensions: /// /// -/// :左侧目录中所有文件的节点列表。 -/// :右侧目录中所有文件的节点列表。 -/// :经 算法识别出的哈希值 -/// 或名称存在差异的节点集合(包含新增、修改和删除的文件)。 +/// : The list of all file nodes in the left directory. +/// : The list of all file nodes in the right directory. +/// : The collection of nodes identified by the algorithm +/// as having differing hash values or names (including added, modified, and deleted files). /// /// public class ComparisonResult @@ -24,7 +24,7 @@ public class ComparisonResult private List _differentNodes; /// - /// 初始化 的新实例,所有内部列表为空。 + /// Initializes a new instance of the class with all internal lists set to empty. /// public ComparisonResult() { @@ -34,42 +34,42 @@ public ComparisonResult() } /// - /// 获取左侧目录(基准版本)中所有文件的只读列表。 + /// Gets a read-only list of all files in the left directory (base version). /// - /// 左侧文件的 只读集合。 + /// A read-only collection of instances for the left directory. public IReadOnlyList LeftNodes => _leftNodes.AsReadOnly(); /// - /// 获取右侧目录(目标版本)中所有文件的只读列表。 + /// Gets a read-only list of all files in the right directory (target version). /// - /// 右侧文件的 只读集合。 + /// A read-only collection of instances for the right directory. public IReadOnlyList RightNodes => _rightNodes.AsReadOnly(); /// - /// 获取左右两侧目录之间存在差异的文件的只读列表。 + /// Gets a read-only list of files that differ between the left and right directories. /// - /// 差异文件的 只读集合。 + /// A read-only collection of instances for the differing files. /// - /// 差异节点是通过对左右两棵 进行递归比较得到的, - /// 包括哈希值不同或仅在某一侧存在的文件。 + /// Differing nodes are obtained by recursively comparing the left and right instances, + /// including files with different hash values or files that exist in only one directory. /// public IReadOnlyList DifferentNodes => _differentNodes.AsReadOnly(); /// - /// 向左侧节点列表中添加文件节点。 + /// Adds file nodes to the left nodes list. /// - /// 要添加的文件节点集合。 + /// The collection of file nodes to add. public void AddToLeft(IEnumerable files) => _leftNodes.AddRange(files); /// - /// 向右侧节点列表中添加文件节点。 + /// Adds file nodes to the right nodes list. /// - /// 要添加的文件节点集合。 + /// The collection of file nodes to add. public void AddToRight(IEnumerable files) => _rightNodes.AddRange(files); /// - /// 向差异节点列表中添加文件节点。 + /// Adds file nodes to the differing nodes list. /// - /// 要添加的差异文件节点集合。 + /// The collection of differing file nodes to add. public void AddDifferent(IEnumerable files) => _differentNodes.AddRange(files); } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/FileSystem/DefaultBlackListMatcher.cs b/src/c#/GeneralUpdate.Core/FileSystem/DefaultBlackListMatcher.cs index e4c8f9fe..cb41d93c 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/DefaultBlackListMatcher.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/DefaultBlackListMatcher.cs @@ -6,20 +6,20 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 基于 Glob 模式的黑名单匹配器默认实现,由 驱动。 +/// Default implementation of a Glob-pattern-based blacklist matcher, driven by . /// /// /// -/// DefaultBlackListMatcher 实现了 接口,提供三种类型的黑名单过滤: +/// DefaultBlackListMatcher implements the interface and provides three types of blacklist filtering: /// /// -/// 黑名单文件:按照 Glob 模式匹配合文件名(如 *.log 匹配所有日志文件)。 -/// 黑名单格式:按照文件扩展名进行不区分大小写的精确匹配。 -/// 跳过目录:按照子字符串包含匹配(不区分大小写)判断是否跳过子目录。 +/// Blacklist files: Matches file names using Glob patterns (e.g., *.log matches all log files). +/// Blacklist formats: Performs case-insensitive exact matching by file extension. +/// Skip directories: Uses case-insensitive substring containment matching to determine whether to skip subdirectories. /// /// -/// 此类的实例通常通过 工厂方法从 创建, -/// 或通过构造函数直接传入 配置对象。 +/// Instances of this class are typically created from via the factory method, +/// or constructed directly by passing a configuration object. /// /// public class DefaultBlackListMatcher : IBlackListMatcher @@ -27,21 +27,21 @@ public class DefaultBlackListMatcher : IBlackListMatcher private readonly BlackListConfig _config; /// - /// 使用指定的黑名单配置初始化 的新实例。 + /// Initializes a new instance of the class with the specified blacklist configuration. /// - /// 黑名单配置对象,包含要排除的文件名模式、扩展名和目录名。 - /// null 时抛出。 + /// The blacklist configuration object containing file name patterns, extensions, and directory names to exclude. + /// Thrown when is null. public DefaultBlackListMatcher(BlackListConfig config) => _config = config ?? throw new ArgumentNullException(nameof(config)); /// - /// 从 中的黑名单属性创建匹配器实例。 + /// Creates a matcher instance from the blacklist properties in . /// - /// 全局配置信息对象。 - /// 配置好的 实例。 + /// The global configuration information object. + /// A configured instance. /// - /// 此工厂方法只会在相应列表有元素(Count > 0)时才会设置对应的黑名单规则, - /// 空列表会被视为 null(即不启用该类型的过滤)。 + /// This factory method only sets the corresponding blacklist rules when the respective list has elements (Count > 0); + /// empty lists are treated as null (meaning that type of filtering is disabled). /// public static DefaultBlackListMatcher FromConfigInfo(GlobalConfigInfo config) { @@ -53,17 +53,17 @@ public static DefaultBlackListMatcher FromConfigInfo(GlobalConfigInfo config) } /// - /// 判断指定文件是否被黑名单匹配规则排除。 + /// Determines whether the specified file is excluded by the blacklist matching rules. /// - /// 文件的相对路径或文件名。 - /// 如果文件匹配黑名单规则则返回 true,否则返回 false + /// The relative path or file name of the file. + /// true if the file matches the blacklist rules; otherwise, false. /// - /// 匹配逻辑依次检查: + /// The matching logic checks in the following order: /// - /// 文件名是否匹配 BlackFiles 中的任一 Glob 模式。 - /// 文件扩展名是否匹配 BlackFormats 中的任一格式。 + /// Whether the file name matches any Glob pattern in BlackFiles. + /// Whether the file extension matches any format in BlackFormats. /// - /// 只要满足任一条件即视为黑名单文件。 + /// The file is considered blacklisted if any condition is met. /// public bool IsBlacklisted(string relativeFilePath) { @@ -76,40 +76,40 @@ public bool IsBlacklisted(string relativeFilePath) } /// - /// 判断指定的文件扩展名是否在黑名单格式列表中。 + /// Determines whether the specified file extension is in the blacklist format list. /// - /// 文件扩展名(如 .log.tmp)。 - /// 如果扩展名在黑名单中则返回 true,否则返回 false + /// The file extension (e.g., .log, .tmp). + /// true if the extension is in the blacklist; otherwise, false. /// - /// 比较时使用不区分大小写的字符串比较。 + /// Uses case-insensitive string comparison for evaluation. /// public bool IsBlacklistedFormat(string extension) => _config.BlackFormats?.Any(f => string.Equals(f, extension, StringComparison.OrdinalIgnoreCase)) == true; /// - /// 判断指定的目录名是否应该被跳过(即不进入该目录进行遍历)。 + /// Determines whether the specified directory name should be skipped (i.e., not entered during traversal). /// - /// 目录名称。 - /// 如果目录名匹配任一跳过规则则返回 true,否则返回 false + /// The directory name. + /// true if the directory name matches any skip rule; otherwise, false. /// - /// 使用不区分大小写的子字符串包含匹配(string.IndexOf)进行判断。 - /// 只要目录名中包含 SkipDirectorys 列表中的任一字符串,即判定为应该跳过。 + /// Uses case-insensitive substring containment matching (string.IndexOf) for evaluation. + /// The directory is considered skippable if its name contains any string from the SkipDirectorys list. /// public bool ShouldSkipDirectory(string directoryName) => _config.SkipDirectorys?.Any(d => directoryName.IndexOf(d, StringComparison.OrdinalIgnoreCase) >= 0) == true; /// - /// 使用简单的 Glob 模式匹配文件名。 + /// Matches a file name against a simple Glob pattern. /// - /// 要匹配的文件名。 - /// Glob 模式(支持 *.xxx 通配前缀匹配和精确匹配)。 - /// 如果文件名匹配模式则返回 true,否则返回 false + /// The file name to match. + /// The Glob pattern (supports *.xxx wildcard prefix matching and exact matching). + /// true if the file name matches the pattern; otherwise, false. /// - /// 目前支持两种 Glob 模式: + /// Currently supports two Glob patterns: /// - /// *.log:以 .log 结尾的通配匹配。 - /// filename:不区分大小写的精确文件名匹配。 + /// *.log: Wildcard matching for names ending with .log. + /// filename: Case-insensitive exact file name matching. /// /// private static bool MatchGlob(string input, string pattern) diff --git a/src/c#/GeneralUpdate.Core/FileSystem/FileNode.cs b/src/c#/GeneralUpdate.Core/FileSystem/FileNode.cs index 443c877c..fd8f324d 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/FileNode.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/FileNode.cs @@ -3,21 +3,21 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 文件节点类,表示文件树(二叉排序树)中的一个节点。 -/// 同时作为文件快照的基本数据单元,存储文件的元数据信息和树形结构引用。 +/// Represents a file node within a file tree (binary search tree). +/// Also serves as the fundamental data unit for file snapshots, storing file metadata and tree structure references. /// /// /// -/// FileNode 在 GeneralUpdate 中有两个核心用途: +/// FileNode has two core purposes in GeneralUpdate: /// /// -/// 文件元数据容器:记录文件的名称、完整路径、相对路径、SHA-256 哈希值等。 -/// 二叉排序树节点:通过 作为排序键构建二叉排序树, -/// 支持 等树操作。 +/// File metadata container: Records the file name, full path, relative path, SHA-256 hash, and other metadata. +/// Binary search tree node: Uses as the sort key to build a binary search tree, +/// supporting tree operations such as , , and . /// /// -/// 方法基于 进行不区分大小写的比较, -/// 用于 算法中判断两个节点是否代表同一个文件。 +/// The method performs a case-insensitive comparison based on and , +/// and is used in the algorithm to determine whether two nodes represent the same file. /// /// public class FileNode @@ -25,63 +25,63 @@ public class FileNode #region Public Properties /// - /// 文件节点的唯一标识符(二叉排序树的排序键)。 + /// Gets or sets the unique identifier for the file node (the sort key for the binary search tree). /// - /// 方法以线程安全的方式自增分配。 + /// Assigned in a thread-safe auto-incrementing manner by the method. public long Id { get; set; } /// - /// 文件名称(不含路径部分)。 + /// Gets or sets the file name (without the path portion). /// /// example.dll public string Name { get; set; } /// - /// 文件的完整绝对路径。 + /// Gets or sets the full absolute path of the file. /// /// C:\App\bin\example.dll public string FullName { get; set; } /// - /// 文件所在目录的路径。 + /// Gets or sets the directory path where the file is located. /// public string Path { get; set; } /// - /// 文件的 SHA-256 哈希值(十六进制字符串形式)。 + /// Gets or sets the SHA-256 hash value of the file (in hexadecimal string format). /// /// - /// 用于文件内容比较和完整性验证。由 计算。 + /// Used for file content comparison and integrity verification. Computed by . /// public string Hash { get; set; } /// - /// 二叉排序树的左子节点引用(存储 Id 小于当前节点的节点)。 + /// Gets or sets the left child node reference in the binary search tree (stores nodes with Id less than the current node). /// public FileNode Left { get; set; } /// - /// 二叉排序树的右子节点引用(存储 Id 大于等于当前节点的节点)。 + /// Gets or sets the right child node reference in the binary search tree (stores nodes with Id greater than or equal to the current node). /// public FileNode Right { get; set; } /// - /// 左侧树中节点的类型标记(保留字段,用于差异分析中的分类标记)。 + /// Gets or sets a type marker for the node in the left tree (reserved field for classification in differential analysis). /// public int LeftType { get; set; } /// - /// 右侧树中节点的类型标记(保留字段,用于差异分析中的分类标记)。 + /// Gets or sets a type marker for the node in the right tree (reserved field for classification in differential analysis). /// public int RightType { get; set; } /// - /// 文件相对于根目录的相对路径。 + /// Gets or sets the relative path of the file with respect to the root directory. /// /// bin/example.dll /// - /// 使用 URI 相对路径格式(使用正斜杠 / 作为目录分隔符),确保跨平台兼容性。 - /// 此属性在 中通过 Uri.MakeRelativeUri 计算。 + /// Uses URI relative path format (forward slash / as directory separator) to ensure cross-platform compatibility. + /// This property is computed via Uri.MakeRelativeUri in . /// public string RelativePath { get; set; } @@ -90,16 +90,16 @@ public class FileNode #region Constructors /// - /// 初始化 的新实例,所有属性使用默认值。 + /// Initializes a new instance of the class with default property values. /// public FileNode() { } /// - /// 初始化 的新实例并设置节点 ID。 + /// Initializes a new instance of the class with the specified node ID. /// - /// 文件节点的唯一标识符。 + /// The unique identifier for the file node. public FileNode(int id) { Id = id; @@ -110,19 +110,19 @@ public FileNode(int id) #region Public Methods /// - /// 向以当前节点为根的二叉排序树中添加一个新节点。 + /// Adds a new node to the binary search tree rooted at the current node. /// - /// 要添加的 实例。 + /// The instance to add. /// /// - /// 添加规则: + /// Insertion rules: /// - /// 如果新节点的 Id 小于当前节点的 Id,递归插入到左子树。 - /// 如果新节点的 Id 大于等于当前节点的 Id,递归插入到右子树。 + /// If the new node's Id is less than the current node's Id, recursively insert into the left subtree. + /// If the new node's Id is greater than or equal to the current node's Id, recursively insert into the right subtree. /// /// /// - /// 如果 null,不执行任何操作。 + /// If is null, no operation is performed. /// /// public void Add(FileNode node) @@ -154,18 +154,18 @@ public void Add(FileNode node) } /// - /// 在以当前节点为根的二叉排序树中搜索指定 ID 的节点。 + /// Searches for a node with the specified ID in the binary search tree rooted at the current node. /// - /// 要搜索的节点 ID。 + /// The node ID to search for. /// - /// 如果找到则返回对应的 实例;否则返回 null。 + /// The matching instance if found; otherwise, null. /// /// - /// 利用二叉排序树的特性进行二分查找,平均时间复杂度为 O(log n): + /// Uses the binary search tree property for binary search with an average time complexity of O(log n): /// - /// 如果 等于当前节点 ID,返回当前节点。 - /// 如果 小于当前节点 ID,递归搜索左子树。 - /// 如果 大于当前节点 ID,递归搜索右子树。 + /// If equals the current node's ID, returns the current node. + /// If is less than the current node's ID, recursively searches the left subtree. + /// If is greater than the current node's ID, recursively searches the right subtree. /// /// public FileNode Search(long id) @@ -187,18 +187,18 @@ public FileNode Search(long id) } /// - /// 在二叉排序树中查找指定 ID 节点的父节点(用于删除操作)。 + /// Finds the parent node of a node with the specified ID in the binary search tree (used for deletion operations). /// - /// 目标节点的 ID。 + /// The ID of the target node. /// - /// 如果找到则返回目标节点的父 ;否则返回 null。 + /// The parent of the target node if found; otherwise, null. /// /// - /// 此方法在 中用于定位待删除节点的父节点, - /// 从而更新父节点的左/右子引用。判断逻辑: + /// This method is used in to locate the parent of the node to be deleted, + /// so that the parent's left/right child reference can be updated. The determination logic: /// - /// 如果当前节点的左子或右子的 ID 等于目标 ID,则当前节点即为父节点。 - /// 否则,根据 ID 大小关系递归搜索左子树或右子树。 + /// If the current node's left or right child has an ID equal to the target ID, the current node is the parent. + /// Otherwise, recursively searches the left or right subtree based on ID comparison. /// /// public FileNode SearchParent(long id) diff --git a/src/c#/GeneralUpdate.Core/FileSystem/FileTree.cs b/src/c#/GeneralUpdate.Core/FileSystem/FileTree.cs index 137fc150..f575f44a 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/FileTree.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/FileTree.cs @@ -4,20 +4,20 @@ namespace GeneralUpdate.Core.FileSystem; /// - /// 简单的文件二叉排序树,以 为排序键组织文件节点。 + /// A simple binary search tree for files, organized by as the sort key. /// /// /// - /// FileTree 封装了一个二叉排序树(Binary Search Tree),主要用途: + /// FileTree encapsulates a Binary Search Tree (BST) with the following primary uses: /// /// - /// 将扁平的文件节点列表组织为树形结构以支持高效的搜索和比较操作。 - /// 配合 方法对两棵树进行递归比较,识别差异节点。 - /// 支持节点的添加、搜索、删除等标准二叉排序树操作。 + /// Organizes a flat list of file nodes into a tree structure to support efficient search and comparison operations. + /// Works with the method to recursively compare two trees and identify differing nodes. + /// Supports standard BST operations including node addition, search, and deletion. /// /// - /// 此树由 方法内部使用,用于将两个版本的 - /// 文件快照组织为树形结构后进行递归差异比较。 + /// This tree is used internally by the method to organize file snapshots + /// of two versions into tree structures before performing recursive difference comparison. /// /// public class FileTree @@ -31,15 +31,15 @@ public class FileTree #region Constructors /// - /// 初始化 的新实例,树为空。 + /// Initializes a new instance of the class with an empty tree. /// public FileTree() { } /// - /// 使用指定的节点集合初始化 ,并将所有节点添加到树中。 + /// Initializes a new instance of the class with the specified node collection and adds all nodes to the tree. /// - /// 要添加到树中的文件节点集合。 + /// The collection of file nodes to add to the tree. public FileTree(IEnumerable nodes) { foreach (var node in nodes) Add(node); @@ -50,12 +50,12 @@ public FileTree(IEnumerable nodes) #region Public Methods /// - /// 向树中添加一个文件节点。 + /// Adds a file node to the tree. /// - /// 要添加的 实例。 + /// The instance to add. /// - /// 如果树为空(根节点为 null),则将 设为根节点; - /// 否则委托给根节点的 方法进行递归插入。 + /// If the tree is empty (root is null), is set as the root; + /// otherwise, the insertion is delegated to the root node's method for recursive insertion. /// public void Add(FileNode node) { @@ -70,27 +70,28 @@ public void Add(FileNode node) } /// - /// 在树中搜索指定 ID 的文件节点。 + /// Searches the tree for a file node with the specified ID. /// - /// 要搜索的节点 ID。 - /// 找到的 实例;如果未找到则返回 null + /// The node ID to search for. + /// The instance if found; otherwise, null. public FileNode Search(long id) => _root == null ? null : _root.Search(id); /// - /// 在树中搜索指定 ID 节点的父节点。 + /// Searches the tree for the parent node of the node with the specified ID. /// - /// 目标节点的 ID。 - /// 目标节点的父 ;如果未找到则返回 null + /// The ID of the target node. + /// The parent of the target node; null if not found. public FileNode SearchParent(long id) => _root == null ? null : _root.SearchParent(id); /// - /// 删除右子树中的最小节点(用于删除具有两个子节点的节点时的替换操作)。 + /// Deletes the minimum node from the right subtree (used as a replacement operation when deleting a node with two children). /// - /// 要从中查找并删除最小节点的右子树根节点。 - /// 被删除的最小节点的 ID。 + /// The root node of the right subtree from which to find and delete the minimum node. + /// The ID of the deleted minimum node. /// - /// 在二叉排序树的删除操作中,当待删除节点有两个子节点时, - /// 需要找到右子树中的最小节点来替换待删除节点。此方法执行此操作并返回被删除的最小节点的 ID。 + /// In a BST deletion operation, when the node to delete has two children, + /// the minimum node from the right subtree must be found to replace the node being deleted. + /// This method performs this operation and returns the ID of the deleted minimum node. /// public long DelRightTreeMin(FileNode node) { @@ -104,20 +105,20 @@ public long DelRightTreeMin(FileNode node) } /// - /// 从树中删除指定 ID 的节点。 + /// Deletes the node with the specified ID from the tree. /// - /// 要删除的节点 ID。 + /// The ID of the node to delete. /// /// - /// 标准的二叉排序树删除操作,包含三种情况: + /// Standard BST deletion operation handling three cases: /// - /// 叶子节点:直接将其父节点的对应子引用置为 null - /// 只有一个子节点:用子节点替换待删除节点。 - /// 有两个子节点:找到右子树的最小节点替换待删除节点,然后删除该最小节点。 + /// Leaf node: Directly sets the parent's corresponding child reference to null. + /// Single child: Replaces the node with its child. + /// Two children: Finds the minimum node in the right subtree to replace the node, then deletes that minimum node. /// /// /// - /// 如果树为空或节点不存在,不执行任何操作。 + /// If the tree is empty or the node does not exist, no operation is performed. /// /// public void DelNode(long id) @@ -185,23 +186,23 @@ public void DelNode(long id) } /// - /// 从指定节点开始,递归比较两棵二叉排序树的对应子节点,收集差异节点。 + /// Recursively compares corresponding child nodes of two binary search trees starting from the specified node, collecting differing nodes. /// - /// 当前树(左侧/基准)的节点。 - /// 目标树(右侧/新版本)的对应节点。 - /// 用于收集差异节点的列表引用。 + /// The node from the current tree (left/base). + /// The corresponding node from the target tree (right/new version). + /// The list reference used to collect differing nodes. /// /// - /// 比较逻辑: + /// Comparison logic: /// - /// 如果左树节点存在且不为空,递归比较其左子节点。 - /// 如果左树节点为空但右树对应节点存在,将右树节点视为新增加入差异列表。 - /// 对于右子树执行对称的操作。 - /// 当发现对应节点不等价(通过 比较 Hash 和 Name)时,将右树节点加入差异列表。 + /// If the left tree node exists and is not null, recursively compare its left child. + /// If the left tree node is null but the corresponding right tree node exists, treat the right node as added and add it to the diff list. + /// Performs symmetric operations for the right subtree. + /// When corresponding nodes are found to be non-equivalent (compared via by Hash and Name), the right tree node is added to the diff list. /// /// /// - /// 此方法通过 ref 传递差异列表,避免在递归过程中频繁创建新对象。 + /// This method passes the diff list by ref to avoid frequent object creation during recursion. /// /// public void Compare(FileNode node, FileNode node0, ref List nodes) @@ -234,9 +235,9 @@ public void Compare(FileNode node, FileNode node0, ref List nodes) } /// - /// 获取树的根节点。 + /// Gets the root node of the tree. /// - /// ;如果树为空则返回 null + /// The root ; null if the tree is empty. public FileNode GetRoot() => _root; #endregion Public Methods diff --git a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeComparer.cs b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeComparer.cs index 24597656..e715fa39 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeComparer.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeComparer.cs @@ -5,24 +5,24 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 两个文件树快照的比较结果,包含新增、修改和删除的文件列表。 +/// Represents the comparison result of two file tree snapshots, containing lists of added, modified, and deleted files. /// -/// 在新快照中新增的文件条目列表。 -/// 在旧快照和新快照之间发生变化的文件条目列表(大小或修改时间不同)。 -/// 在旧快照中存在但在新快照中被删除的相对路径列表。 +/// The list of file entries added in the new snapshot. +/// The list of file entries that changed between the old and new snapshots (different size or last write time). +/// The list of relative paths for files that existed in the old snapshot but were removed in the new snapshot. /// /// -/// FileTreeDiff 由 方法生成, -/// 为差异更新提供三组清晰的变更集合: +/// FileTreeDiff is generated by the method, +/// providing three clear change sets for differential updates: /// /// -/// Added:新增的文件需要直接打包到更新包中。 -/// Modified:修改的文件需要通过差异补丁或完整替换的方式更新。 -/// Deleted:删除的文件需要在更新时从客户端清除。 +/// Added: New files that need to be packaged directly into the update package. +/// Modified: Changed files that need to be updated via differential patch or full replacement. +/// Deleted: Removed files that need to be cleaned up from the client during update. /// /// -/// 使用 快速检查是否有任何变更, -/// 使用 获取变更总数。 +/// Use to quickly check for any changes, +/// and to get the total number of changes. /// /// public readonly record struct FileTreeDiff( @@ -32,56 +32,58 @@ IReadOnlyList Deleted ) { /// - /// 获取一个值,指示是否存在任何变更(新增、修改或删除)。 + /// Gets a value indicating whether there are any changes (added, modified, or deleted). /// public bool HasChanges => Added.Count > 0 || Modified.Count > 0 || Deleted.Count > 0; /// - /// 获取变更总数(新增 + 修改 + 删除)。 + /// Gets the total number of changes (added + modified + deleted). /// public int TotalChanges => Added.Count + Modified.Count + Deleted.Count; /// - /// 获取一个空的 实例,表示无任何变更。 + /// Gets an empty instance representing no changes. /// public static FileTreeDiff Empty { get; } = new( Array.Empty(), Array.Empty(), Array.Empty()); } /// -/// 比较两个 实例,生成 差异结果。 -/// 识别出两个文件快照之间新增、修改和删除的文件。 +/// Compares two instances and generates a difference result. +/// Identifies files that have been added, modified, or deleted between the two file snapshots. /// /// /// -/// FileTreeComparer 是差异更新流程的关键组件,提供了两种比较方式: +/// FileTreeComparer is a key component in the differential update workflow, providing two comparison methods: /// /// -/// :完整比较,返回所有新增、修改和删除的文件列表。 -/// :快速检查, -/// 一旦发现第一个差异立即返回 true,适用于只需要判断"是否有更新"的场景。 +/// : Performs a full comparison and returns all lists of added, modified, and deleted files. +/// : A quick check that +/// returns true as soon as the first difference is found, suitable for scenarios where only an "update available" check is needed. /// /// -/// 比较算法基于文件相对路径的字典映射,使用 OrdinalIgnoreCase 字符串比较确保跨平台兼容性。 -/// 文件是否修改通过比较 判定。 +/// The comparison algorithm is based on a dictionary mapping of file relative paths, using OrdinalIgnoreCase string comparison +/// to ensure cross-platform compatibility. Whether a file has been modified is determined by comparing +/// and . /// /// public static class FileTreeComparer { /// - /// 比较两个文件系统快照,返回完整的差异结果。 + /// Compares two file system snapshots and returns the complete difference result. /// - /// 旧版本(基准)的文件系统快照。 - /// 新版本(目标)的文件系统快照。 - /// 包含新增、修改和删除文件的 结构。 - /// null 时抛出。 + /// The old (base) file system snapshot. + /// The new (target) file system snapshot. + /// A structure containing the lists of added, modified, and deleted files. + /// Thrown when or is null. /// /// - /// 比较步骤: + /// Comparison steps: /// - /// 将新旧快照分别转换为以 RelativePath 为键的字典。 - /// 遍历新快照:如果路径在旧快照中不存在,标记为"新增";如果存在但大小或时间不同,标记为"修改"。 - /// 遍历旧快照:如果路径在新快照中不存在,标记为"删除"。 + /// Converts both the old and new snapshots into dictionaries keyed by RelativePath. + /// Iterates the new snapshot: if a path does not exist in the old snapshot, it is marked as "Added"; + /// if it exists but has a different size or time, it is marked as "Modified". + /// Iterates the old snapshot: if a path does not exist in the new snapshot, it is marked as "Deleted". /// /// /// @@ -124,15 +126,16 @@ public static FileTreeDiff Compare(FileTreeSnapshot old, FileTreeSnapshot update } /// - /// 快速检查两个文件系统快照之间是否存在任何变化(短路模式)。 + /// Quickly checks whether there are any changes between two file system snapshots (short-circuit mode). /// - /// 旧版本(基准)的文件系统快照。 - /// 新版本(目标)的文件系统快照。 - /// 如果存在任何文件变化则返回 true,否则返回 false + /// The old (base) file system snapshot. + /// The new (target) file system snapshot. + /// true if there are any file changes; otherwise, false. /// - /// 与 不同,此方法在发现第一个差异时立即返回 true, - /// 不会继续遍历其余文件,因此在仅需判断"是否有更新"的场景下性能更优。 - /// 首先通过比较条目总数进行快速预判,然后再逐项检查。 + /// Unlike , this method returns true as soon as the first difference is found + /// without continuing to traverse the remaining files. This makes it more performant in scenarios where + /// only a boolean "update available" check is needed. + /// It first performs a quick pre-check by comparing the total entry count, then examines each entry individually. /// public static bool HasChanges(FileTreeSnapshot old, FileTreeSnapshot updated) { diff --git a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeDiffer.cs b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeDiffer.cs index 8997bcec..57e4cc77 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeDiffer.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeDiffer.cs @@ -5,45 +5,45 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 根据 差异结果生成增量更新所需的数据包。 -/// 用于 Pipeline 的 PatchMiddleware 进行差异补丁的构建和应用。 +/// Generates the data packages required for incremental updates based on the difference result. +/// Used by the Pipeline's PatchMiddleware for constructing and applying differential patches. /// /// /// -/// FileTreeDiffer 将文件快照比较结果转换为实际的增量更新操作指令: +/// FileTreeDiffer converts file snapshot comparison results into actual incremental update operation instructions: /// /// -/// :基于差异结果生成需要打包的文件路径对。 -/// :提取需要在客户端删除的文件列表。 -/// :根据变更比例决策使用增量更新还是全量更新。 +/// : Generates file path pairs that need to be packaged based on the diff result. +/// : Extracts the list of files that need to be deleted on the client side. +/// : Decides whether to use incremental or full update based on the change ratio. /// /// -/// 增量更新策略核心原则:变更比例低于阈值时使用增量补丁,高于阈值时建议使用全量包, -/// 以避免大量小补丁导致的性能开销。 +/// The core principle of the incremental update strategy: use incremental patches when the change ratio is below the threshold, +/// and recommend a full package when the ratio exceeds the threshold, to avoid the performance overhead of numerous small patches. /// /// public static class FileTreeDiffer { /// - /// 根据差异结果生成需要打包的文件路径对。 + /// Generates file path pairs that need to be packaged based on the difference result. /// - /// 新旧快照的差异结果。 - /// 新版本文件的根目录路径。 + /// The difference result between the old and new snapshots. + /// The root directory path of the new version files. /// - /// 路径对的只读列表,每对包含 (源文件完整路径, 相对路径), - /// 其中源文件路径指向新版本文件,相对路径用于在更新包中定位。 + /// A read-only list of path pairs, each containing (source file full path, relative path), + /// where the source path points to the new version file and the relative path is used for locating it in the update package. /// /// /// - /// 生成逻辑: + /// Generation logic: /// - /// 新增文件(Added):直接包含到增量包中。 - /// 修改文件(Modified):包含到增量包中。 - /// 删除文件(Deleted):不在此处处理,由 单独提取。 + /// Added files: Included directly in the delta package. + /// Modified files: Included in the delta package. + /// Deleted files: Not handled here; extracted separately by . /// /// /// - /// 对于每对路径,会检查源文件是否确实存在于磁盘上,不存在则跳过。 + /// For each path pair, the method checks whether the source file actually exists on disk and skips it if not. /// /// public static IReadOnlyList<(string SourcePath, string RelativePath)> ProduceDeltaPaths( @@ -73,34 +73,36 @@ public static class FileTreeDiffer } /// - /// 提取差异结果中需要删除的文件相对路径列表。 + /// Extracts the list of relative paths for files that need to be deleted from the difference result. /// - /// 新旧快照的差异结果。 - /// 需要在客户端删除的文件相对路径只读列表。 + /// The difference result between the old and new snapshots. + /// A read-only list of relative paths for files that need to be deleted on the client side. /// - /// 此方法直接返回 列表,该列表包含 - /// 在旧版本中存在但在新版本中已被移除的所有文件相对路径。 + /// This method directly returns the list, which contains + /// the relative paths of all files that existed in the old version but have been removed in the new version. /// public static IReadOnlyList ProduceDeletes(FileTreeDiff diff) => diff.Deleted; /// - /// 根据变更比例决策最优更新模式:变更比例低时推荐增量更新(delta),比例高时推荐全量更新。 + /// Decides the optimal update mode based on the change ratio: recommends incremental (delta) update + /// when the change ratio is low, and full update when the ratio is high. /// - /// 新旧快照的差异结果。 - /// 应用程序的文件总数。 - /// 增量更新阈值百分比。当变更文件比例小于等于此值时推荐增量更新,默认为 0.5(50%)。 - /// 如果推荐使用增量更新(delta patching)则返回 true,否则返回 false + /// The difference result between the old and new snapshots. + /// The total number of files in the application. + /// The delta update threshold percentage. When the change file ratio is less than or equal to this value, + /// incremental update is recommended. Default is 0.5 (50%). + /// true if delta patching is recommended; otherwise, false. /// /// - /// 决策逻辑:变更比例 = (新增 + 修改 + 删除) / 文件总数。 + /// Decision logic: change ratio = (added + modified + deleted) / total file count. /// /// - /// 变更比例 <= thresholdPercent:推荐增量更新。少量变更使用补丁更高效。 - /// 变更比例 > thresholdPercent:推荐全量更新。大量变更时增量补丁的优势丧失。 + /// Change ratio <= thresholdPercent: Recommends incremental update. Fewer changes make patching more efficient. + /// Change ratio > thresholdPercent: Recommends full update. The advantage of incremental patches diminishes with many changes. /// /// - /// 当 为 0 时返回 false。 + /// Returns false when is 0. /// /// public static bool ShouldUseDeltaPatching(FileTreeDiff diff, int totalFileCount, double thresholdPercent = 0.5) diff --git a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeSnapshot.cs b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeSnapshot.cs index 677aaf05..cb406aa8 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeSnapshot.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeCore/FileTreeSnapshot.cs @@ -5,16 +5,16 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 不可变的文件条目记录,表示目录树中的一个文件快照。 -/// 记录文件的相对路径、大小和最后修改时间,用于文件差异比较。 +/// Represents an immutable file entry record containing a snapshot of a single file within a directory tree. +/// Records the file's relative path, size, and last write time, used for file difference comparison. /// -/// 文件相对于根目录的路径。 -/// 文件大小(字节数)。 -/// 文件最后写入时间的 UTC 表示。 +/// The relative path of the file with respect to the root directory. +/// The file size in bytes. +/// The last write time of the file in UTC. /// -/// FileEntry 是 的基本数据单元, -/// 通过比较 来判断文件是否被修改。 -/// 此记录类型为只读结构,保证了快照的不可变性。 +/// FileEntry is the fundamental data unit of . +/// File modification is determined by comparing and . +/// This record type is a read-only struct, guaranteeing snapshot immutability. /// public readonly record struct FileEntry( string RelativePath, @@ -23,45 +23,45 @@ DateTime LastWriteTimeUtc ); /// -/// 不可变的目录树快照,记录某一时刻目录中所有文件的元数据状态。 +/// An immutable directory tree snapshot that records the metadata state of all files in a directory at a specific point in time. /// /// /// -/// FileTreeSnapshot 由 配合 -/// 创建,文件遍历过程中自动跳过黑名单中的文件和目录。 +/// FileTreeSnapshot is created by in conjunction with , +/// automatically skipping blacklisted files and directories during file traversal. /// /// -/// 典型使用流程: +/// Typical usage flow: /// -/// 使用 方法从根目录创建快照。 -/// 将快照通过 JSON 序列化后储存或传输。 -/// 在差异更新时,使用 对比新旧快照。 +/// Create a snapshot from a root directory using the method. +/// Serialize the snapshot to JSON for storage or transmission. +/// During differential updates, use to compare old and new snapshots. /// /// /// public sealed class FileTreeSnapshot { /// - /// 获取快照创建时的 UTC 时间戳。 + /// Gets the UTC timestamp when the snapshot was created. /// public DateTime CreatedAt { get; } = DateTime.UtcNow; /// - /// 获取被快照的根目录路径。 + /// Gets the root directory path that was snapshotted. /// public string RootPath { get; } /// - /// 获取快照中包含的所有文件条目只读列表。 + /// Gets a read-only list of all file entries contained in the snapshot. /// public IReadOnlyList Entries { get; } /// - /// 使用指定的根目录路径和文件条目初始化 的新实例。 + /// Initializes a new instance of the class with the specified root directory path and file entries. /// - /// 被快照的根目录路径。 - /// 文件条目集合。 - /// null 时抛出。 + /// The root directory path that was snapshotted. + /// The collection of file entries. + /// Thrown when is null. public FileTreeSnapshot(string rootPath, IEnumerable entries) { RootPath = rootPath ?? throw new ArgumentNullException(nameof(rootPath)); @@ -69,16 +69,16 @@ public FileTreeSnapshot(string rootPath, IEnumerable entries) } /// - /// 使用 遍历指定根目录,创建文件系统快照。 + /// Creates a file system snapshot by traversing the specified root directory using a . /// - /// 要创建快照的根目录路径。 - /// 配置了黑名单规则的 实例。 - /// 包含目录中所有(未被黑名单过滤的)文件的新快照。 + /// The root directory path to create a snapshot of. + /// A instance configured with blacklist rules. + /// A new snapshot containing all files (not filtered by the blacklist) in the directory. /// - /// 此方法会枚举根目录下的所有文件(跳过黑名单中的文件和目录), - /// 为每个文件创建 记录(含大小和最后修改时间), - /// 然后封装为 返回。 - /// 快照创建时会记录当前 UTC 时间作为 。 + /// This method enumerates all files under the root directory (skipping blacklisted files and directories), + /// creates a record for each file (including size and last write time), + /// and then wraps them into a to return. + /// The current UTC time is recorded as when the snapshot is created. /// public static FileTreeSnapshot FromEnumerator(string rootPath, FileTreeEnumerator enumerator) { @@ -100,9 +100,9 @@ public static FileTreeSnapshot FromEnumerator(string rootPath, FileTreeEnumerato } /// - /// 创建一个空的文件系统快照(不含任何文件条目),用于表示空目录或占位。 + /// Creates an empty file system snapshot (containing no file entries), used to represent an empty directory or as a placeholder. /// - /// 根目录路径。 - /// 空的 实例。 + /// The root directory path. + /// An empty instance. public static FileTreeSnapshot Empty(string rootPath) => new(rootPath, Array.Empty()); } diff --git a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeEnumerator.cs b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeEnumerator.cs index fdc9f5c9..0bc08a97 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/FileTreeEnumerator.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/FileTreeEnumerator.cs @@ -6,20 +6,20 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 递归枚举指定目录中的所有文件,同时通过 应用黑名单过滤。 +/// Recursively enumerates all files in a specified directory while applying blacklist filtering via . /// /// /// -/// FileTreeEnumerator 是一个轻量级的文件遍历器,核心流程为: +/// FileTreeEnumerator is a lightweight file traverser. The core flow is: /// /// -/// 从根目录开始逐层递归遍历。 -/// 对每个文件,调用 IBlackListMatcher.IsBlacklisted 判断是否跳过。 -/// 对每个子目录,调用 IBlackListMatcher.ShouldSkipDirectory 判断是否进入。 +/// Begins recursive traversal from the root directory level by level. +/// For each file, calls IBlackListMatcher.IsBlacklisted to determine whether to skip it. +/// For each subdirectory, calls IBlackListMatcher.ShouldSkipDirectory to determine whether to enter it. /// /// -/// 此遍历器常用于创建 的输入源。 -/// 可通过 工厂方法直接从 创建。 +/// This traverser is commonly used as an input source for creating instances. +/// It can be created directly from a via the factory method. /// /// public class FileTreeEnumerator @@ -27,31 +27,31 @@ public class FileTreeEnumerator private readonly IBlackListMatcher _matcher; /// - /// 使用指定的黑名单匹配器初始化 的新实例。 + /// Initializes a new instance of the class with the specified blacklist matcher. /// - /// 黑名单匹配器实例。 - /// null 时抛出。 + /// The blacklist matcher instance. + /// Thrown when is null. public FileTreeEnumerator(IBlackListMatcher matcher) { _matcher = matcher ?? throw new ArgumentNullException(nameof(matcher)); } /// - /// 枚举根目录下的所有文件,跳过黑名单中的文件和目录。 + /// Enumerates all files under the root directory, skipping blacklisted files and directories. /// - /// 要枚举的根目录路径。 - /// 所有未跳过文件的完整路径集合。 + /// The root directory path to enumerate. + /// A collection of full paths for all files that were not skipped. /// /// - /// 枚举逻辑: + /// Enumeration logic: /// - /// 首先枚举根目录中的所有文件,跳过后缀名匹配黑名单的文件。 - /// 然后枚举根目录中的所有子目录,跳过匹配黑名单的目录。 - /// 对未跳过的子目录递归执行相同操作。 + /// First enumerates all files in the root directory, skipping files whose extensions match the blacklist. + /// Then enumerates all subdirectories in the root directory, skipping those that match the blacklist. + /// Recursively performs the same operations on subdirectories that were not skipped. /// /// /// - /// 此方法使用 yield return 延迟执行,每次只处理一个文件。 + /// This method uses yield return for deferred execution, processing one file at a time. /// /// public IEnumerable EnumerateFiles(string rootPath) @@ -78,12 +78,12 @@ public IEnumerable EnumerateFiles(string rootPath) } /// - /// 从 配置快速创建 实例。 + /// Quickly creates a instance from a configuration. /// - /// 黑名单配置对象。 - /// 配置好的 实例。 + /// The blacklist configuration object. + /// A configured instance. /// - /// 此工厂方法内部使用 作为黑名单匹配器实现。 + /// This factory method uses as the underlying blacklist matcher implementation. /// public static FileTreeEnumerator FromConfig(BlackListConfig config) => new(new DefaultBlackListMatcher(config)); diff --git a/src/c#/GeneralUpdate.Core/FileSystem/IBlackListMatcher.cs b/src/c#/GeneralUpdate.Core/FileSystem/IBlackListMatcher.cs index 77dd66b7..4b017681 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/IBlackListMatcher.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/IBlackListMatcher.cs @@ -3,52 +3,53 @@ namespace GeneralUpdate.Core.FileSystem; /// -/// 黑名单匹配器接口,定义用于排除特定文件和目录的匹配规则。 +/// Defines the blacklist matcher interface for excluding specific files and directories from traversal. /// /// /// -/// 此接口是 GeneralUpdate 文件系统过滤层的核心抽象,允许实现者自定义从文件遍历中排除 -/// 哪些文件和目录的规则。主要用于: +/// This interface is the core abstraction for the GeneralUpdate file system filtering layer, +/// allowing implementers to define custom rules for excluding files and directories from file traversal. +/// It is primarily used in: /// /// -/// 方法在遍历文件系统时过滤文件。 -/// 在枚举文件时应用黑名单规则。 -/// 备份和差异更新场景中跳过不需要处理的文件。 +/// The method to filter files during file system traversal. +/// The to apply blacklist rules during file enumeration. +/// Backup and differential update scenarios to skip files that do not need processing. /// /// -/// 默认实现请参考 。 +/// For the default implementation, refer to . /// /// public interface IBlackListMatcher { /// - /// 判断指定的文件是否应该被黑名单排除。 + /// Determines whether the specified file should be excluded by the blacklist. /// - /// 文件的相对路径或文件名。 - /// 如果文件应被排除则返回 true,否则返回 false + /// The relative path or file name of the file. + /// true if the file should be excluded; otherwise, false. /// - /// 实现通常应提取文件名和扩展名进行匹配检查。此方法在每次遇到文件时都会调用, - /// 因此实现应保持高效。 + /// Implementations should typically extract the file name and extension for matching checks. + /// This method is called for every file encountered, so implementations should remain efficient. /// bool IsBlacklisted(string relativeFilePath); /// - /// 判断指定的文件扩展名是否在黑名单中。 + /// Determines whether the specified file extension is in the blacklist. /// - /// 文件扩展名(包含前导点号,如 .log)。 - /// 如果扩展名在黑名单中则返回 true,否则返回 false + /// The file extension (including leading dot, e.g., .log). + /// true if the extension is in the blacklist; otherwise, false. /// - /// 此方法专门用于快速检查文件格式/类型级别的黑名单规则。 + /// This method is specifically designed for quick checks against file format/type-level blacklist rules. /// bool IsBlacklistedFormat(string extension); /// - /// 判断指定的目录名是否应该在文件遍历时被跳过。 + /// Determines whether the specified directory name should be skipped during file traversal. /// - /// 目录名称。 - /// 如果目录应被跳过则返回 true,否则返回 false + /// The directory name. + /// true if the directory should be skipped; otherwise, false. /// - /// 当此方法返回 true 时,遍历器将不会进入该目录及其子目录。 + /// When this method returns true, the traverser will not enter the directory or its subdirectories. /// bool ShouldSkipDirectory(string directoryName); } diff --git a/src/c#/GeneralUpdate.Core/FileSystem/StorageManager.cs b/src/c#/GeneralUpdate.Core/FileSystem/StorageManager.cs index 5c3f2e30..8f30c921 100644 --- a/src/c#/GeneralUpdate.Core/FileSystem/StorageManager.cs +++ b/src/c#/GeneralUpdate.Core/FileSystem/StorageManager.cs @@ -10,24 +10,27 @@ namespace GeneralUpdate.Core.FileSystem { /// - /// 存储管理器,提供文件系统操作的静态工具类。 - /// 支持备份、恢复、目录比较、文件遍历、哈希校验以及黑名单过滤等核心功能。 - /// 该类是所有文件系统操作的统一入口,在更新流程中负责版本目录的快照生成与差异比较。 + /// Storage manager providing static utility methods for file system operations. + /// Supports backup, restore, directory comparison, file traversal, hash verification, and blacklist filtering. + /// This class is the unified entry point for all file system operations and is responsible for + /// generating version directory snapshots and performing difference comparisons during the update workflow. /// /// /// - /// StorageManager 是整个更新框架中文件操作的核心枢纽,主要职责包括: + /// StorageManager is the central hub for file operations within the entire update framework. + /// Its primary responsibilities include: /// /// - /// Backup / Restore:创建和恢复应用程序的完整备份。 - /// Compare:对两个目录进行递归比较,识别新增、修改和删除的文件。 - /// HashEquals:使用 SHA-256 哈希算法验证两个文件是否相同。 - /// GetAllFiles:递归获取指定目录下的所有文件,支持跳过指定目录。 - /// CleanBackup / ListBackups:管理历史备份版本,支持保留最近 N 个版本。 + /// Backup / Restore: Create and restore full application backups. + /// Compare: Recursively compare two directories to identify added, modified, and deleted files. + /// HashEquals: Verify whether two files are identical using the SHA-256 hash algorithm. + /// GetAllFiles: Recursively retrieve all files under a specified directory, with support for skipping directories. + /// CleanBackup / ListBackups: Manage historical backup versions with support for retaining the most recent N versions. /// /// - /// 可通过 静态属性设置黑名单匹配器,在文件遍历时排除特定文件或目录。 - /// 所有公开方法均为线程安全的静态方法,但 方法内部使用实例状态,注意并发调用。 + /// A blacklist matcher can be set via the static property to exclude specific files or directories during file traversal. + /// All public methods are thread-safe static methods; however, the method uses instance state internally, + /// so concurrent calls should be avoided on the same instance. /// /// public sealed class StorageManager @@ -35,23 +38,23 @@ public sealed class StorageManager private long _fileCount = 0; /// - /// 备份目录的默认前缀名称。 + /// The default prefix name for backup directories. /// /// - /// 备份目录的命名格式为 "app-{版本号}",此常量定义了前缀部分。 + /// Backup directories are named in the format "app-{version}". This constant defines the prefix portion. /// public const string DirectoryName = "app-"; /// - /// 获取或设置可选的路径/文件黑名单匹配器。 + /// Gets or sets the optional path/file blacklist matcher. /// /// - /// 实现 接口的实例,用于在文件遍历时排除黑名单中的文件或目录。 - /// 必须在执行任何文件操作之前设置。 + /// An instance implementing the interface, used to exclude blacklisted files or directories during file traversal. + /// Must be set before any file operations are performed. /// /// - /// 如果设置了此属性, 方法会在遍历文件系统时自动跳过匹配的文件和目录。 - /// 设置方式示例:StorageManager.BlackListMatcher = new DefaultBlackListMatcher(config); + /// When this property is set, the method automatically skips files and directories that match the blacklist during file system traversal. + /// Example of setting: StorageManager.BlackListMatcher = new DefaultBlackListMatcher(config); /// public static IBlackListMatcher? BlackListMatcher { get; set; } @@ -60,17 +63,19 @@ public sealed class StorageManager #region Public Methods /// - /// 以左侧目录为基准,找出左侧有但右侧没有的文件集合(即被删除的文件)。 + /// Finds the set of files present in the left directory but not in the right directory (i.e. files that have been deleted). /// - /// 基准(旧版本)目录路径。 - /// 目标(新版本)目录路径。 + /// The base (old version) directory path. + /// The target (new version) directory path. /// - /// 存在于左侧但不存在于右侧的 集合;如果两侧文件列表完全一致,则返回空集合。 + /// A collection of instances that exist in the left directory but not in the right directory; + /// returns an empty collection if both file lists are identical. /// /// - /// 此方法将左右两侧目录分别序列化为 列表, - /// 然后以 RelativePath 为键构建哈希表进行差集运算。 - /// 适用于差异更新场景中识别需要删除的旧文件。 + /// This method serializes both the left and right directories into lists, + /// then builds a hash table keyed by RelativePath to perform a set difference operation. + /// It is suitable for identifying old files that need to be deleted in a differential update scenario. + /// /// public IEnumerable? Except(string leftPath, string rightPath) { @@ -81,25 +86,25 @@ public sealed class StorageManager } /// - /// 比较两个目录,识别出其中不同的文件。 + /// Compares two directories and identifies the files that differ between them. /// - /// 基准(旧版本)目录路径。 - /// 目标(新版本)目录路径。 + /// The base (old version) directory path. + /// The target (new version) directory path. /// - /// 对象,包含左侧节点、右侧节点以及差异节点的集合。 + /// A object containing collections of left nodes, right nodes, and differing nodes. /// /// /// - /// 比较流程如下: + /// Comparison flow: /// - /// 重置内部文件 ID 计数器。 - /// 递归读取左右两个目录中的所有文件节点,生成 列表。 - /// 分别构建左右两棵 二叉排序树。 - /// 从根节点开始递归对比两棵树的同名节点,收集哈希值或名称不同的节点。 + /// Resets the internal file ID counter. + /// Recursively reads all file nodes from both directories, generating lists. + /// Constructs left and right binary search trees. + /// Recursively compares corresponding nodes of the two trees starting from the root, collecting nodes with differing hash values or names. /// /// /// - /// 注意:该方法使用实例内部的 ComparisonResult 状态,应避免在多线程环境中并发调用同一实例。 + /// Note: This method uses the instance-level ComparisonResult state and should not be called concurrently on the same instance in a multi-threaded environment. /// /// public ComparisonResult Compare(string leftDir, string rightDir) @@ -119,16 +124,16 @@ public ComparisonResult Compare(string leftDir, string rightDir) } /// - /// 将对象序列化为 JSON 文件并写入指定路径。 + /// Serializes an object to JSON and writes it to the specified path. /// - /// 要序列化的对象类型,必须是引用类型。 - /// 目标 JSON 文件的完整路径。 - /// 要序列化的对象实例。 - /// 可选的 JSON 类型信息元数据,用于支持源生成器序列化。 - /// 不包含有效的目录路径时抛出。 + /// The type of the object to serialize. Must be a reference type. + /// The full path of the target JSON file. + /// The object instance to serialize. + /// Optional JSON type info metadata for source generator serialization support. + /// Thrown when does not contain a valid directory path. /// - /// 如果目标文件的目录不存在,会自动创建。支持通过 JsonTypeInfo 进行源生成器模式, - /// 在 AOT 编译场景中可避免运行时反射。 + /// If the directory of the target file does not exist, it will be created automatically. + /// Supports source generator mode via JsonTypeInfo, which avoids runtime reflection in AOT compilation scenarios. /// public static void CreateJson(string targetPath, T obj, JsonTypeInfo? typeInfo = null) where T : class { @@ -143,15 +148,15 @@ public static void CreateJson(string targetPath, T obj, JsonTypeInfo? type } /// - /// 从指定路径读取 JSON 文件并反序列化为指定类型的对象。 + /// Reads a JSON file from the specified path and deserializes it into the specified type. /// - /// 要反序列化的目标类型,必须是引用类型。 - /// JSON 文件的完整路径。 - /// 可选的 JSON 类型信息元数据,用于支持源生成器反序列化。 - /// 反序列化后的对象实例;如果文件不存在则返回 default + /// The target type for deserialization. Must be a reference type. + /// The full path of the JSON file. + /// Optional JSON type info metadata for source generator deserialization support. + /// The deserialized object instance; returns default if the file does not exist. /// - /// 如果文件不存在,不会抛出异常而是返回 null。 - /// 支持通过 JsonTypeInfo 进行源生成器模式。 + /// If the file does not exist, no exception is thrown and null is returned. + /// Supports source generator mode via JsonTypeInfo. /// public static T? GetJson(string path, JsonTypeInfo? typeInfo = null) where T : class { @@ -169,13 +174,13 @@ public static void CreateJson(string targetPath, T obj, JsonTypeInfo? type } /// - /// 在系统临时目录中创建一个带有唯一名称的子目录,用于存放临时更新文件。 + /// Creates a uniquely named subdirectory in the system temporary directory for storing temporary update files. /// - /// 用于标识临时目录用途的自定义名称。 - /// 创建的临时目录的完整路径。 + /// A custom name identifying the purpose of the temporary directory. + /// The full path of the created temporary directory. /// - /// 目录命名格式为 generalupdate_{时间戳}_{进程ID}_{name}。 - /// 如果目录已存在不会重复创建。调用方负责在不再需要时清理此目录。 + /// The directory naming format is generalupdate_{timestamp}_{processId}_{name}. + /// If the directory already exists, it will not be recreated. The caller is responsible for cleaning up this directory when it is no longer needed. /// public static string GetTempDirectory(string name) { @@ -190,12 +195,12 @@ public static string GetTempDirectory(string name) } /// - /// 递归删除指定目录及其所有子目录和文件。 + /// Recursively deletes the specified directory and all of its subdirectories and files. /// - /// 要删除的目标目录路径。 + /// The path to the target directory to delete. /// - /// 在删除前会将每个文件的属性重置为 , - /// 以避免因只读属性导致删除失败。此操作不可恢复,请谨慎使用。 + /// Before deletion, each file's attributes are reset to + /// to prevent deletion failures caused by read-only attributes. This operation is irreversible, use with caution. /// public static void DeleteDirectory(string targetDir) { @@ -214,14 +219,14 @@ public static void DeleteDirectory(string targetDir) } /// - /// 递归获取指定目录下的所有文件,支持跳过黑名单中的子目录。 + /// Recursively retrieves all files under the specified directory, with support for skipping subdirectories via the blacklist. /// - /// 要遍历的根目录路径。 - /// 需要跳过的子目录名称列表(包含匹配)。 - /// 所有未跳过的文件的 集合。 + /// The root directory path to traverse. + /// The list of subdirectory names to skip (uses containment matching). + /// A collection of instances for all files that were not skipped. /// - /// 此方法仅跳过第一层子目录(不递归跳过),适用于备份和全量文件枚举场景。 - /// 如果遍历过程中因权限等原因发生异常,会返回空集合而不是抛出异常。 + /// This method only skips first-level subdirectories (does not recursively skip) and is suitable for backup and full file enumeration scenarios. + /// If an exception occurs during traversal (e.g., due to permissions), an empty collection is returned instead of throwing an exception. /// public static List GetAllFiles(string path, List skipDirectorys) { @@ -256,13 +261,13 @@ public static List GetAllFiles(string path, List skipDirectory } /// - /// 私有递归方法,获取指定路径下的所有文件(无黑名单过滤)。 + /// Private recursive method that retrieves all files under the specified path (without blacklist filtering). /// - /// 要遍历的目录路径。 - /// 目录中所有文件的 集合。 + /// The directory path to traverse. + /// A collection of instances for all files in the directory. /// - /// 与 不同,此方法不包含目录跳过逻辑。 - /// 如果遍历过程中因权限等原因发生异常,会返回空集合而不是抛出异常。 + /// Unlike , this method does not include directory-skipping logic. + /// If an exception occurs during traversal (e.g., due to permissions), an empty collection is returned instead of throwing an exception. /// private static List GetAllfiles(string path) { @@ -285,14 +290,14 @@ private static List GetAllfiles(string path) } /// - /// 使用 SHA-256 哈希算法比较两个文件的内容是否完全相同。 + /// Compares the contents of two files to determine whether they are identical using the SHA-256 hash algorithm. /// - /// 第一个文件的完整路径。 - /// 第二个文件的完整路径。 - /// 如果两个文件的哈希值相同则返回 true,否则返回 false + /// The full path of the first file. + /// The full path of the second file. + /// true if the hash values of the two files are equal; otherwise, false. /// - /// 此方法计算两个文件的 SHA-256 哈希值并进行字节序列比较。 - /// 适用于大文件比较场景,比逐字节读取更高效。 + /// This method computes the SHA-256 hash of both files and compares the resulting byte sequences. + /// It is suitable for large file comparisons and is more efficient than byte-by-byte reading. /// public static bool HashEquals(string leftPath, string rightPath) { @@ -303,22 +308,22 @@ public static bool HashEquals(string leftPath, string rightPath) } /// - /// 备份整个应用程序目录到指定位置。 + /// Backs up the entire application directory to the specified location. /// - /// 源应用程序目录路径。 - /// 目标备份目录路径。 - /// 需要跳过的子目录名称列表(包含匹配)。 + /// The source application directory path. + /// The target backup directory path. + /// The list of subdirectory names to skip (uses containment matching). /// /// - /// 备份流程: + /// Backup flow: /// - /// 如果备份目录已存在,先删除它。 - /// 创建新的备份目录。 - /// 递归复制源目录中的所有文件和子目录,跳过 中匹配的目录。 + /// If the backup directory already exists, delete it first. + /// Create a new backup directory. + /// Recursively copy all files and subdirectories from the source directory, skipping directories that match . /// /// /// - /// 此方法会覆盖目标目录中已存在的文件。 + /// This method overwrites existing files in the target directory. /// /// public static void Backup(string sourcePath, string backupPath, IReadOnlyList directoryNames) @@ -351,13 +356,14 @@ private static void CopyDirectory(string sourceDir, string targetDir, IReadOnlyL } /// - /// 从备份目录恢复整个应用程序到指定位置。 + /// Restores the entire application from a backup directory to the specified location. /// - /// 备份目录路径。 - /// 要恢复到的目标应用程序目录路径。 + /// The backup directory path. + /// The target application directory path to restore to. /// - /// 如果目标目录不存在,会自动创建。恢复操作会完整复制备份目录中所有文件和子目录到目标位置, - /// 并覆盖已存在的同名文件。此方法不包含黑名单过滤逻辑,会完整恢复所有备份内容。 + /// If the target directory does not exist, it will be created automatically. The restore operation copies all files and subdirectories + /// from the backup directory to the target location, overwriting any existing files with the same name. + /// This method does not include blacklist filtering logic and restores all backup content completely. /// public static void Restore(string backupPath, string sourcePath) { @@ -386,15 +392,15 @@ private static void CopyDirectory(string sourceDir, string targetDir) } /// - /// 异步备份整个应用程序目录(将同步备份操作调度到线程池执行)。 + /// Asynchronously backs up the entire application directory (offloads the synchronous backup operation to the thread pool). /// - /// 源应用程序目录路径。 - /// 目标备份目录路径。 - /// 需要跳过的子目录名称列表。 - /// 表示异步备份操作的任务。 + /// The source application directory path. + /// The target backup directory path. + /// The list of subdirectory names to skip. + /// A task representing the asynchronous backup operation. /// - /// 此方法通过 Task.Run 调用调度到线程池, - /// 适用于 UI 应用程序中避免阻塞主线程的场景。 + /// This method offloads the call to the thread pool via Task.Run, + /// making it suitable for UI applications where blocking the main thread should be avoided. /// public static async System.Threading.Tasks.Task BackupAsync(string sourcePath, string backupPath, System.Collections.Generic.IReadOnlyList directoryNames) { @@ -402,13 +408,13 @@ public static async System.Threading.Tasks.Task BackupAsync(string sourcePath, s } /// - /// 异步从备份目录恢复应用程序(将同步恢复操作调度到线程池执行)。 + /// Asynchronously restores the application from a backup directory (offloads the synchronous restore operation to the thread pool). /// - /// 备份目录路径。 - /// 要恢复到的目标应用程序目录路径。 - /// 表示异步恢复操作的任务。 + /// The backup directory path. + /// The target application directory path to restore to. + /// A task representing the asynchronous restore operation. /// - /// 此方法通过 Task.Run 调用调度到线程池。 + /// This method offloads the call to the thread pool via Task.Run. /// public static async System.Threading.Tasks.Task RestoreAsync(string backupPath, string sourcePath) { @@ -416,13 +422,13 @@ public static async System.Threading.Tasks.Task RestoreAsync(string backupPath, } /// - /// 异步清理旧版本备份,仅保留最近的 N 个版本(将同步清理操作调度到线程池执行)。 + /// Asynchronously cleans up old backup versions, retaining only the most recent N versions (offloads the synchronous cleanup to the thread pool). /// - /// 应用程序安装根目录路径。 - /// 要保留的最新备份版本数量,默认为 3。 - /// 表示异步清理操作的任务。 + /// The application installation root directory path. + /// The number of most recent backup versions to retain. Default is 3. + /// A task representing the asynchronous cleanup operation. /// - /// 此方法通过 Task.Run 调用调度到线程池。 + /// This method offloads the call to the thread pool via Task.Run. /// public static async System.Threading.Tasks.Task CleanBackupAsync(string installPath, int keepVersions = 3) { @@ -433,22 +439,22 @@ public static async System.Threading.Tasks.Task CleanBackupAsync(string installP #region Private Methods /// - /// 私有递归方法,读取指定目录下的所有文件并转换为 列表。 + /// Private recursive method that reads all files under the specified directory and converts them into a list of instances. /// - /// 当前要遍历的目录路径。 - /// 根目录路径,用于计算相对路径。如果为 null,则使用 作为根目录。 - /// 目录中所有 的集合。 + /// The directory path currently being traversed. + /// The root directory path used for calculating relative paths. If null, is used as the root. + /// A collection of instances for all files in the directory. /// /// - /// 遍历逻辑: + /// Traversal logic: /// - /// 枚举当前目录中的所有文件,计算每个文件的 SHA-256 哈希值和相对路径。 - /// 如果设置了 ,会跳过匹配黑名单的文件。 - /// 递归遍历所有子目录,跳过匹配黑名单的子目录。 + /// Enumerates all files in the current directory, computing the SHA-256 hash and relative path for each file. + /// If is set, files matching the blacklist are skipped. + /// Recursively traverses all subdirectories, skipping those that match the blacklist. /// /// /// - /// 相对路径的计算使用 Uri.MakeRelativeUri 方法,确保跨平台兼容性。 + /// Relative paths are computed using the Uri.MakeRelativeUri method to ensure cross-platform compatibility. /// /// private IEnumerable ReadFileNode(string path, string rootPath = null) @@ -490,32 +496,32 @@ private IEnumerable ReadFileNode(string path, string rootPath = null) } /// - /// 获取自增的文件树节点 ID,使用线程安全的交错增量操作。 + /// Gets an auto-incrementing file tree node ID using a thread-safe interlocked increment operation. /// - /// 下一个可用的文件节点 ID。 + /// The next available file node ID. /// - /// 此方法通过 保证多线程环境下的 ID 唯一性。 + /// This method uses to guarantee ID uniqueness in multi-threaded environments. /// private long GetId() => Interlocked.Increment(ref _fileCount); /// - /// 重置文件树节点 ID 计数器为 0,使用线程安全的交错交换操作。 + /// Resets the file tree node ID counter to 0 using a thread-safe interlocked exchange operation. /// /// - /// 在每次新的 操作开始时调用,以确保每个比较操作使用独立的 ID 序列。 + /// Called at the start of each new operation to ensure each comparison uses an independent ID sequence. /// private void ResetId() => Interlocked.Exchange(ref _fileCount, 0); /// - /// 清理旧的备份版本,仅保留最近的 N 个版本。 + /// Cleans up old backup versions, retaining only the most recent N versions. /// - /// 应用程序安装根目录路径。 - /// 要保留的最新备份版本数量,默认为 3。 + /// The application installation root directory path. + /// The number of most recent backup versions to retain. Default is 3. /// - /// 备份目录位于 {installPath}/__backups 下,每个子目录以版本号命名。 - /// 此方法按照版本号降序排列,保留前 N 个版本,删除其余所有版本。 - /// 如果版本号解析失败,视为 0.0 版本(会优先被删除)。 - /// 如果 __backups 目录不存在,则不执行任何操作。 + /// Backup directories are located under {installPath}/__backups, with each subdirectory named by version. + /// This method sorts directories by version in descending order, retains the top N versions, and deletes all others. + /// If a version cannot be parsed, it is treated as version 0.0 (and will be deleted first). + /// If the __backups directory does not exist, no operation is performed. /// public static void CleanBackup(string installPath, int keepVersions = 3) { @@ -536,13 +542,13 @@ public static void CleanBackup(string installPath, int keepVersions = 3) } /// - /// 列出所有备份版本及其元数据信息。 + /// Lists all backup versions and their metadata information. /// - /// 应用程序安装根目录路径。 - /// 所有备份版本的 只读集合。 + /// The application installation root directory path. + /// A read-only collection of for all backup versions. /// - /// 每个备份条目包含版本号、完整路径、创建时间和总大小(字节)。 - /// 如果 __backups 目录不存在,返回空集合。 + /// Each backup entry contains the version name, full path, creation time, and total size in bytes. + /// If the __backups directory does not exist, an empty collection is returned. /// public static IReadOnlyList ListBackups(string installPath) { @@ -561,55 +567,55 @@ public static IReadOnlyList ListBackups(string installPath) } /// - /// 备份配置项,用于控制备份行为。 + /// Backup configuration for controlling backup behavior. /// /// /// - /// 通过配置 可以控制: + /// Configuring controls the following: /// - /// :保留的历史备份版本数量。 - /// :自定义备份根目录(可选)。 - /// :备份时需要跳过的子目录列表。 - /// :是否启用备份功能。 + /// : The number of historical backup versions to retain. + /// : Custom backup root directory (optional). + /// : The list of subdirectory names to skip during backup. + /// : Whether the backup feature is enabled. /// /// /// public sealed class BackupConfig { /// - /// 要保留的最新备份版本数量,默认为 3。 + /// The number of most recent backup versions to retain. Default is 3. /// public int KeepVersions { get; set; } = 3; /// - /// 自定义备份根目录路径。如果为 null,则使用默认备份位置。 + /// Custom backup root directory path. If null, the default backup location is used. /// public string? BackupRoot { get; set; } /// - /// 备份时需要跳过的子目录名称列表。 + /// The list of subdirectory names to skip during backup. /// /// - /// 使用包含匹配(string.Contains)进行判断,只要目录名包含列表中的任一字符串即被跳过。 + /// Uses containment matching (string.Contains) for evaluation. A directory is skipped if its name contains any string in the list. /// public List SkipDirectories { get; set; } = new(); /// - /// 是否启用备份功能,默认为 true。 + /// Whether the backup feature is enabled. Default is true. /// public bool Enabled { get; set; } = true; } /// - /// 备份版本元数据记录。 + /// Represents metadata for a backup version. /// - /// 备份版本的名称(一般为版本号字符串)。 - /// 备份目录的完整路径。 - /// 备份的创建时间。 - /// 备份的总大小(字节数)。 + /// The name of the backup version (typically a version string). + /// The full path to the backup directory. + /// The creation time of the backup. + /// The total size of the backup in bytes. /// - /// 此记录类型用于 方法的返回结果, - /// 提供每个备份版本的摘要信息以便用户查看和管理历史备份。 + /// This record type is used as the return type for the method, + /// providing summary information for each backup version so users can view and manage historical backups. /// public record BackupInfo(string Version, string Path, DateTime CreatedAt, long SizeBytes); } diff --git a/src/c#/GeneralUpdate.Core/Network/HttpClientProvider.cs b/src/c#/GeneralUpdate.Core/Network/HttpClientProvider.cs index 67071a5c..ca949303 100644 --- a/src/c#/GeneralUpdate.Core/Network/HttpClientProvider.cs +++ b/src/c#/GeneralUpdate.Core/Network/HttpClientProvider.cs @@ -11,6 +11,8 @@ public static class HttpClientProvider { private static readonly HttpClient _shared = new(); - /// Shared instance. Do NOT dispose. + /// + /// Gets the shared instance. Do NOT dispose. + /// public static HttpClient Shared => _shared; } diff --git a/src/c#/GeneralUpdate.Core/Network/VersionService.cs b/src/c#/GeneralUpdate.Core/Network/VersionService.cs index bc88cd9e..7c1dcfb7 100644 --- a/src/c#/GeneralUpdate.Core/Network/VersionService.cs +++ b/src/c#/GeneralUpdate.Core/Network/VersionService.cs @@ -16,43 +16,50 @@ namespace GeneralUpdate.Core.Network { /// - /// 版本服务,提供与更新服务器的 HTTP 通信能力,包括版本校验和状态上报。 + /// Version service providing HTTP communication with the update server, + /// including version validation and status reporting. /// /// /// - /// 该类是 GeneralUpdate 框架的 HTTP 通信层,其核心设计要点如下: + /// This class is the HTTP communication layer of the GeneralUpdate framework. + /// Its key design points are as follows: /// /// /// - /// 使用静态共享的 实例(_sharedClient),避免套接字耗尽, - /// 并通过 支持可配置的 SSL 证书验证策略。 + /// Uses a static shared instance (_sharedClient) + /// to avoid socket exhaustion, and supports configurable SSL certificate validation + /// policies via . /// /// - /// 提供两套静态 API( - /// 和 ), - /// 内部自动创建实例并调用对应的异步方法。这些静态方式为向后兼容而保留。 + /// Provides two sets of static convenience APIs + /// ( + /// and ), + /// which internally create instances and call the corresponding async methods. + /// These static methods are retained for backward compatibility. /// /// - /// 支持可插拔的认证提供者(), - /// 内置支持 Bearer Token、API Key、HMAC 等认证方式,也可通过 自定义扩展。 + /// Supports a pluggable authentication provider () + /// with built-in support for Bearer Token, API Key, HMAC, and extensibility + /// through . /// /// - /// 具备指数退避重试机制:在 中捕获可重试的异常, - /// 等待时间按 2^attempt * 1000 毫秒递增。 + /// Implements exponential backoff retry: in , + /// retryable exceptions trigger a wait of 2^attempt * 1000 milliseconds. /// /// - /// 支持全局 SSL 策略()和全局认证提供者 - /// ()配置。当设置了全局认证提供者时, - /// 它将覆盖工厂方法 创建的认证实例。 + /// Supports global SSL policy (via ) + /// and global authentication provider (via ). + /// When a global auth provider is set, it overrides the factory method + /// . /// /// /// - /// 典型使用场景: + /// Typical usage scenarios: /// - /// 启动时调用 - /// 检查服务器是否有新版本。 - /// 下载完成后调用 - /// 上报更新状态。 + /// At startup, call + /// to check whether the server has a new version. + /// After download completes, call + /// to report the update status. /// /// /// @@ -67,12 +74,18 @@ public class VersionService private readonly int _maxRetries; /// - /// 初始化 的静态成员。 + /// Static constructor: initializes the static members of . /// /// - /// 创建带有自定义 SSL 验证回调的 , - /// 并使用该 handler 初始化静态共享的 实例。 - /// SSL 验证逻辑委托给 ,可通过 全局替换。 + /// + /// Execution flow: + /// + /// Creates an with a custom SSL validation callback. + /// The SSL validation logic is delegated to , + /// which can be replaced globally via . + /// Initializes the static shared instance using the handler. + /// + /// /// static VersionService() { @@ -82,30 +95,33 @@ static VersionService() } /// - /// 设置全局 SSL 证书验证策略。 + /// Sets the global SSL certificate validation policy. /// /// - /// 该策略会影响所有 实例的 HTTPS 请求。 - /// 默认使用 ,即严格模式。 - /// 可通过传入自定义的 实现来放宽或替换验证逻辑。 + /// This policy affects all HTTPS requests made by instances. + /// The default is , i.e., strict mode. + /// Pass a custom implementation to relax or replace + /// the validation logic. /// - /// SSL 验证策略实例。不能为 null。 - /// 为 null 时抛出。 + /// The SSL validation policy instance. Must not be null. + /// Thrown when is null. public static void SetSslValidationPolicy(ISslValidationPolicy policy) => _globalSslPolicy = policy ?? throw new ArgumentNullException(nameof(policy)); /// - /// 设置全局默认的 HTTP 认证提供者。 + /// Sets the global default HTTP authentication provider. /// /// - /// 当设置了全局认证提供者后,所有通过静态 API( - /// 和 )发起的请求将优先使用该提供者, - /// 覆盖 所创建的认证实例。 + /// When a global authentication provider is set, all requests made via the static APIs + /// ( + /// and ) + /// will preferentially use this provider, overriding the authentication instance + /// created by . /// - /// 传入 null 可清除全局认证提供者,此时将回退到工厂方法创建的认证实例。 + /// Passing null clears the global authentication provider, reverting to the factory method. /// /// - /// 全局认证提供者实例,或 null 以清除全局配置。 + /// The global authentication provider instance, or null to clear the global configuration. public static void SetDefaultAuthProvider(IHttpAuthProvider? provider) => _globalAuthProvider = provider; @@ -114,46 +130,50 @@ private static bool SharedCertValidation(HttpRequestMessage m, X509Certificate2? => _globalSslPolicy.ValidateCertificate(c, ch, e); /// - /// 初始化 的新实例。 + /// Initializes a new instance of the class. /// /// - /// 实例方法()使用该实例的认证提供者和超时设置。 - /// 为 null 时,默认使用 (不执行任何认证)。 + /// Instance methods ( and ) use + /// this instance's authentication provider and timeout settings. + /// When is null, (no authentication) is used by default. /// - /// HTTP 认证提供者。为 null 时使用 。 - /// 请求超时时间。为 null 时默认 30 秒。 - /// 最大重试次数,默认值为 3。 + /// The HTTP authentication provider. If null, is used. + /// The request timeout. If null, defaults to 30 seconds. + /// The maximum number of retry attempts. Defaults to 3. public VersionService(IHttpAuthProvider? auth = null, TimeSpan? timeout = null, int maxRetries = 3) { _auth = auth ?? new NoOpAuthProvider(); _timeout = timeout ?? TimeSpan.FromSeconds(30); _maxRetries = maxRetries; } + /// - /// 向服务器上报指定记录的更新状态。 + /// Reports the update status to the server for a specified record. /// /// /// - /// 这是一个向后兼容的静态便捷方法,内部自动创建 实例并调用 。 + /// This is a backward-compatible static convenience method that internally creates + /// a instance and calls . /// /// - /// 执行流程: + /// Execution flow: /// - /// 解析认证提供者:优先使用全局认证提供者(),否则通过 - /// 创建。 - /// 创建临时 实例。 - /// 调用 执行上报。 + /// Resolves the authentication provider: uses the global provider + /// () first; otherwise creates one via + /// . + /// Creates a temporary instance. + /// Calls to perform the report. /// /// /// - /// 服务器 API 地址。 - /// 更新记录标识符。 - /// 当前状态码。 - /// 更新类型(可为 null)。 - /// 认证方案(如 "bearer"、"apikey"、"hmac"),用于创建认证提供者。当设置了全局认证提供者时此参数无效。 - /// 认证令牌或密钥,与 配合使用。 - /// 用于取消操作的 。 - /// 表示异步操作的任务。 + /// The server API URL. + /// The update record identifier. + /// The current status code. + /// The update type (may be null). + /// The authentication scheme (e.g., "bearer", "apikey", "hmac"), used to create the auth provider. Ignored when a global auth provider is set. + /// The authentication token or key, used together with . + /// A for cancelling the operation. + /// A task representing the asynchronous operation. public static Task Report(string url, int recordId, int status, int? type, string scheme = null, string token = null, CancellationToken ct = default) { @@ -162,33 +182,37 @@ public static Task Report(string url, int recordId, int status, int? type, } /// - /// 向服务器校验当前版本,查询是否有可用更新。 + /// Validates the current version against the server to check for available updates. /// /// /// - /// 这是推荐的强类型重载。内部自动创建 实例并调用 。 + /// This is the recommended strongly-typed overload. It internally creates a + /// instance and calls . /// /// - /// 执行流程: + /// Execution flow: /// - /// 解析认证提供者:优先使用全局认证提供者(),否则通过 - /// 创建。 - /// 创建临时 实例。 - /// 构造包含版本、应用类型、平台等信息的请求参数。 - /// 通过 POST 请求将参数发送至服务器,反序列化响应为 + /// Resolves the authentication provider: uses the global provider + /// () first; otherwise creates one via + /// . + /// Creates a temporary instance. + /// Constructs request parameters containing the version, app type, platform, etc. + /// Sends the parameters to the server via a POST request and deserializes + /// the response into a . /// /// /// - /// 服务器版本校验 API 地址。 - /// 当前客户端版本号。 - /// 应用类型(如主程序、补丁等)。 - /// 应用密钥,用于服务端鉴权。 - /// 目标平台(Windows、Linux、macOS 等)。 - /// 产品标识符。 - /// 认证方案(如 "bearer"、"apikey"、"hmac"),用于创建认证提供者。当设置了全局认证提供者时此参数无效。 - /// 认证令牌或密钥,与 配合使用。 - /// 用于取消操作的 。 - /// 包含版本校验结果(如是否存在更新、下载地址等)的 + /// The server version validation API URL. + /// The current client version string. + /// The application type (e.g., main program, patch, etc.). + /// The application key used for server-side authentication. + /// The target platform (Windows, Linux, macOS, etc.). + /// The product identifier. + /// The authentication scheme (e.g., "bearer", "apikey", "hmac"), used to create the auth provider. Ignored when a global auth provider is set. + /// The authentication token or key, used together with . + /// A for cancelling the operation. + /// A containing the version validation result + /// (e.g., whether an update exists, download URL, etc.). public static Task Validate(string url, string version, AppType appType, string appKey, PlatformType platform, string productId, string scheme = null, string token = null, CancellationToken ct = default) @@ -198,38 +222,50 @@ public static Task Validate(string url, string version, } /// - /// 向服务器校验当前版本(使用整数参数的向后兼容重载)。 + /// Validates the current version against the server (backward-compatible overload using integer parameters). /// /// /// - /// 该重载将整数参数转换为对应的枚举类型后,委托给强类型重载 执行。 - /// 为保持与旧调用方的二进制兼容性而保留。 + /// This overload converts the integer parameters to their corresponding enum types + /// and delegates to the strongly-typed overload + /// . + /// Retained for binary compatibility with older callers. /// /// - /// 服务器版本校验 API 地址。 - /// 当前客户端版本号。 - /// 应用类型(整数形式,将转换为 )。 - /// 应用密钥。 - /// 目标平台(整数形式,将转换为 )。 - /// 产品标识符。 - /// 认证方案。 - /// 认证令牌或密钥。 - /// 用于取消操作的 。 - /// 包含版本校验结果的 + /// The server version validation API URL. + /// The current client version string. + /// The application type (as an integer, will be cast to ). + /// The application key. + /// The target platform (as an integer, will be cast to ). + /// The product identifier. + /// The authentication scheme. + /// The authentication token or key. + /// A for cancelling the operation. + /// A containing the version validation result. public static Task Validate(string url, string version, int appType, string appKey, int platform, string productId, string scheme = null, string token = null, CancellationToken ct = default) => Validate(url, version, (AppType)appType, appKey, (PlatformType)platform, productId, scheme, token, ct); /// - /// 异步上报更新记录的状态。 + /// Asynchronously reports the update record status to the server. /// - /// 服务器 API 地址。 - /// 更新记录标识符。 - /// 当前状态码。 - /// 更新类型(可为 null)。 - /// 用于取消操作的 。 - /// 表示异步操作的任务。 + /// + /// + /// Execution flow: + /// + /// Constructs a parameter dictionary with the record ID, status, and type. + /// Sends the parameters via a POST request using . + /// Deserializes the response into a of type . + /// + /// + /// + /// The server API URL. + /// The update record identifier. + /// The current status code. + /// The update type (may be null). + /// A for cancelling the operation. + /// A task representing the asynchronous operation. private async Task ReportAsync(string url, int recordId, int status, int? type, CancellationToken t = default) { var p = new Dictionary { ["recordId"] = recordId, ["status"] = status, ["type"] = type }; @@ -237,16 +273,27 @@ private async Task ReportAsync(string url, int recordId, int status, int? type, } /// - /// 异步校验版本,向服务器查询可用更新。 + /// Asynchronously validates the version by querying the server for available updates. /// - /// 服务器版本校验 API 地址。 - /// 当前客户端版本号。 - /// 应用类型的整数值。 - /// 应用密钥。 - /// 平台类型的整数值。 - /// 产品标识符。 - /// 用于取消操作的 。 - /// 包含版本校验结果的 + /// + /// + /// Execution flow: + /// + /// Constructs a parameter dictionary with the version, app type, app key, + /// platform, product ID, and upgrade mode. + /// Sends the parameters via a POST request using . + /// Deserializes the response into a . + /// + /// + /// + /// The server version validation API URL. + /// The current client version string. + /// The application type as an integer value. + /// The application key. + /// The platform type as an integer value. + /// The product identifier. + /// A for cancelling the operation. + /// A containing the version validation result. private async Task ValidateAsync(string url, string v, int at, string appKey, int pf, string pid, CancellationToken t = default) { @@ -255,38 +302,40 @@ private async Task ValidateAsync(string url, string v, int at, s } /// - /// 执行带有指数退避重试机制的 HTTP POST 请求。 + /// Executes an HTTP POST request with exponential backoff retry logic. /// /// /// - /// 本方法封装了重试逻辑,流程如下: + /// This method encapsulates the retry logic. The execution flow is as follows: /// /// /// - /// 调用 发送 POST 请求。 + /// Calls to send the POST request. /// /// - /// 若请求成功,直接返回反序列化后的结果。 + /// If the request succeeds, the deserialized result is returned directly. /// /// - /// 若抛出可重试的异常(参见 )且未达到最大重试次数, - /// 则等待指数递增的时间(2^attempt * 1000 毫秒)后重试。 + /// If a retryable exception (see ) is thrown and the + /// maximum retry count has not been reached, waits for an exponentially increasing + /// interval (2^attempt * 1000 milliseconds) before retrying. /// /// - /// 不可重试的异常(如 )会立即向上传播。 + /// Non-retryable exceptions (such as ) + /// propagate immediately. /// /// /// - /// 重试等待期间会通过 释放线程, - /// 并响应取消令牌。 + /// During retry waits, the thread is released via + /// , and the cancellation token is respected. /// /// - /// 响应数据的反序列化目标类型。 - /// 请求的目标 URL。 - /// POST 请求体参数字典。 - /// 用于源代码生成器(source generator)的 JSON 类型信息元数据,可为 null(此时使用反射反序列化)。 - /// 用于取消操作的 。 - /// 反序列化后的响应数据。 + /// The deserialization target type for the response data. + /// The target URL for the request. + /// The POST body parameter dictionary. + /// The JSON type info metadata for source generator (may be null, in which case reflection-based deserialization is used). + /// A for cancelling the operation. + /// The deserialized response data. private async Task PostAsync(string url, Dictionary p, JsonTypeInfo? ti, CancellationToken t) { @@ -302,40 +351,45 @@ private async Task PostAsync(string url, Dictionary p, } /// - /// 执行单个 HTTP POST 请求,包含认证注入和超时控制。 + /// Executes a single HTTP POST request, including authentication injection and timeout control. /// /// /// - /// 本方法负责单次 HTTP 请求的全过程: + /// This method handles the full lifecycle of a single HTTP request: /// /// /// - /// 构造 ,设置 URL、方法(POST)和 Accept 头。 + /// Constructs an with the URL, method (POST), + /// and Accept header. /// /// - /// 将参数字典序列化为 JSON 字符串,设置为请求内容。 + /// Serializes the parameter dictionary to JSON and sets it as the request content. /// /// - /// 调用 注入认证信息(如 Bearer Token)。 + /// Calls to inject authentication + /// information (e.g., Bearer Token). /// /// - /// 通过 将传入的取消令牌与超时令牌关联, - /// 确保超时或取消任一触发时请求立即中止。 + /// Uses to link + /// the incoming cancellation token with a timeout token, ensuring the request is aborted + /// when either the timeout elapses or cancellation is requested. /// /// - /// 使用静态共享的 发送请求,并调用 EnsureSuccessStatusCode 验证响应状态。 + /// Sends the request using the static shared and calls + /// EnsureSuccessStatusCode to validate the response status. /// /// - /// 读取响应内容为字符串,并通过 或反射反序列化为目标类型 + /// Reads the response content as a string and deserializes it into the target + /// type using or reflection. /// /// /// - /// 响应数据的反序列化目标类型。 - /// 请求的目标 URL。 - /// POST 请求体参数字典。 - /// 用于源代码生成器的 JSON 类型信息元数据,可为 null。 - /// 用于取消操作的 。 - /// 反序列化后的响应数据。 + /// The deserialization target type for the response data. + /// The target URL for the request. + /// The POST body parameter dictionary. + /// The JSON type info metadata for source generator (may be null). + /// A for cancelling the operation. + /// The deserialized response data. private async Task SendAsync(string url, Dictionary p, JsonTypeInfo? ti, CancellationToken t) { @@ -353,6 +407,28 @@ private async Task SendAsync(string url, Dictionary p, return ti == null ? JsonSerializer.Deserialize(rj) : JsonSerializer.Deserialize(rj, ti); } + /// + /// Determines whether an exception is retryable. + /// + /// The exception to evaluate. + /// true if the exception is retryable; otherwise false. + /// + /// + /// Retryable exceptions: + /// + /// + /// + /// + /// with a message containing "timeout" + /// + /// + /// + /// Non-retryable exceptions: + /// + /// + /// + /// + /// private static bool IsRetryable(Exception ex) { if (ex is OperationCanceledException) return false; diff --git a/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs index b5ecd0aa..eb605fe1 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs @@ -7,52 +7,52 @@ namespace GeneralUpdate.Core.Pipeline; /// -/// 解压缩中间件,负责将下载的压缩包解压到目标目录。 +/// Decompression middleware responsible for extracting the downloaded archive to the target directory. /// /// /// -/// 此中间件从 中读取以下键: +/// This middleware reads the following keys from : /// -/// "Format" 压缩包格式(如 ZIP、GZip)。 -/// "ZipFilePath" — 压缩包源文件路径。 -/// "PatchPath" — 差异补丁临时目录路径。 -/// "Encoding" 解压时使用的字符编码。 -/// "SourcePath" — 应用程序安装目标路径。 -/// "PatchEnabled" — 是否启用了差异补丁模式。 +/// "Format" The archive format (e.g., ZIP, GZip). +/// "ZipFilePath" — The source archive file path. +/// "PatchPath" — The differential patch temporary directory path. +/// "Encoding" The character encoding used for decompression. +/// "SourcePath" — The application installation target path. +/// "PatchEnabled" — Whether the differential patch mode is enabled. /// /// /// -/// 工作流程: +/// Workflow: /// -/// 读取上下文中的配置参数。 -/// 根据 "PatchEnabled" 的值决定解压目标路径: -/// 如果启用了补丁,解压到 "PatchPath";否则直接解压到 "SourcePath" -/// 调用 执行实际的解压操作。 +/// Reads configuration parameters from the context. +/// Determines the decompression target path based on the value of "PatchEnabled": +/// If patching is enabled, decompress to "PatchPath"; otherwise, decompress directly to "SourcePath". +/// Calls to perform the actual decompression operation. /// /// /// -/// 此中间件应在 之后(确保压缩包完整性已验证)且 -/// 在 之前(如果需要应用差异补丁)注册。 +/// This middleware should be registered after (to ensure the archive integrity +/// has been verified) and before (if differential patching is needed). /// /// public class CompressMiddleware : IMiddleware { /// - /// 异步执行解压缩操作。 + /// Asynchronously executes the decompression operation. /// - /// 管道上下文,包含压缩包路径、格式、编码及目标路径等配置。 - /// 表示异步操作的任务。 - /// 解压缩过程中发生的任何异常。异常信息会通过 记录。 + /// The pipeline context containing the archive path, format, encoding, and target path configuration. + /// A task that represents the asynchronous operation. + /// Any exception that occurs during decompression. Exception details are logged via . /// /// - /// 解压缩操作在后台线程上执行(通过 ),避免阻塞调用线程。 - /// 如果 "PatchEnabled"true,则压缩包被解压到 "PatchPath" 目录, - /// 随后由 将补丁应用到 "SourcePath"。 - /// 如果为 false,压缩包直接解压到 "SourcePath",完成更新。 + /// The decompression operation is performed on a background thread (via ) to avoid blocking + /// the calling thread. If "PatchEnabled" is true, the archive is extracted to the "PatchPath" directory, + /// and will subsequently apply the patches to "SourcePath". + /// If false, the archive is extracted directly to "SourcePath", completing the update. /// /// - /// 注意:此方法不直接处理异常,而是让异常向上传播,由 - /// 负责中断管道执行。 + /// Note: This method does not handle exceptions directly; exceptions propagate upward for + /// to halt pipeline execution. /// /// public Task InvokeAsync(PipelineContext context) diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DiffPipeline.cs b/src/c#/GeneralUpdate.Core/Pipeline/DiffPipeline.cs index 032be76a..fbf36775 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DiffPipeline.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DiffPipeline.cs @@ -15,54 +15,55 @@ namespace GeneralUpdate.Core.Pipeline; /// -/// 并行差异管道,支持可配置的并行度、进度报告、可插拔匹配器和取消令牌。 -/// 提供"清理"(生成补丁)和"脏"(应用补丁)两种操作模式。 +/// Parallel differential pipeline supporting configurable parallelism, progress reporting, pluggable matchers, +/// and cancellation tokens. Provides "Clean" (generate patches) and "Dirty" (apply patches) operation modes. /// /// /// -/// 是 GeneralUpdate 差异更新机制的核心执行引擎。 -/// 它使用二进制差异算法(如 HDiffPatch)生成和应用程序文件之间的二进制差异补丁, -/// 从而显著减少更新包的大小。 +/// is the core execution engine of the GeneralUpdate differential update mechanism. +/// It uses binary differ algorithms (such as HDiffPatch) to generate and apply binary differential patches +/// between application files, significantly reducing the size of update packages. /// /// -/// 两种主要操作模式: +/// Two primary operation modes: /// /// -/// 模式 -/// 方法 -/// 说明 +/// Mode +/// Method +/// Description /// /// -/// 清理模式(Clean) +/// Clean Mode /// /// -/// 比较旧版本(source)和新版本(target)目录,为发生变化的文件生成 .patch 补丁文件。 -/// 新增文件直接复制,删除的文件记录到删除清单中。此模式在服务端/发布端使用。 +/// Compares the old version (source) and new version (target) directories, generating .patch files for +/// files that have changed. New files are copied directly, and deleted files are recorded in a deletion +/// manifest. This mode is used on the server/publishing side. /// /// /// -/// 脏模式(Dirty) +/// Dirty Mode /// /// -/// 将补丁文件并行应用到客户端的旧版本文件上,生成更新后的文件。 -/// 此模式在客户端更新时使用。 +/// Applies patch files to the client's old version files in parallel, producing updated files. +/// This mode is used during client-side updates. /// /// /// /// /// -/// 使用 进行流畅配置,或直接调用构造函数创建实例。 -/// 两种操作均支持通过 控制并发度(通过 配置), -/// 通过 报告文件级进度, -/// 以及通过 支持取消操作。 +/// Use for fluent configuration, or instantiate directly via constructors. +/// Both operations support concurrency control via (configured through +/// ), file-level progress reporting via +/// , and cancellation via . /// /// -/// 文件处理策略: +/// File processing strategy: /// -/// 变化的文件:生成/应用二进制补丁。 -/// 新增的文件:直接复制。 -/// 删除的文件:在 generalupdate_delete_files.json 中记录,脏模式执行时删除。 -/// 未变化的文件:跳过处理。 +/// Changed files: Generate/apply binary patches. +/// New files: Copy directly. +/// Deleted files: Recorded in generalupdate_delete_files.json; removed during dirty mode execution. +/// Unchanged files: Skipped. /// /// /// @@ -78,12 +79,13 @@ public class DiffPipeline private const string DeleteListFileName = "generalupdate_delete_files.json"; /// - /// 使用默认选项、默认差异比较器()和默认匹配器初始化管道实例。 + /// Initializes a new pipeline instance with default options, default binary differ + /// (), and default matchers. /// /// - /// 此构造函数适用于大多数场景,无需额外配置即可使用。 - /// 默认并行度为 2(通过 的默认值), - /// 默认使用 作为二进制差异算法。 + /// This constructor is suitable for most scenarios and requires no additional configuration. + /// The default parallelism is 2 (via defaults), + /// and the default binary differ is . /// public DiffPipeline() : this(new DiffPipelineOptions(), new StreamingHdiffDiffer(), null, null, null) @@ -91,11 +93,12 @@ public DiffPipeline() } /// - /// 使用指定的选项和默认差异比较器初始化管道实例。 + /// Initializes a new pipeline instance with the specified options and the default binary differ. /// - /// 管道选项,用于配置并行度等参数。不能为 null。 + /// The pipeline options for configuring parameters such as parallelism. Must not be null. /// - /// 适用于需要自定义并行度或错误处理策略但使用默认差异算法的场景。 + /// Suitable for scenarios that require custom parallelism or error handling strategies while using + /// the default binary differ algorithm. /// public DiffPipeline(DiffPipelineOptions options) : this(options, new StreamingHdiffDiffer(), null, null, null) @@ -103,27 +106,28 @@ public DiffPipeline(DiffPipelineOptions options) } /// - /// 使用完整配置初始化管道实例。 + /// Initializes a new pipeline instance with full configuration. /// - /// 管道选项,包含并行度、错误处理等配置。不能为 null。 - /// 二进制差异比较器,负责生成和应用二进制补丁。不能为 null。 + /// The pipeline options containing parallelism, error handling, and other settings. Must not be null. + /// The binary differ responsible for generating and applying binary patches. Must not be null. /// - /// 清理阶段()使用的文件匹配器。用于比较新旧目录中的文件节点。 - /// 如果为 null,则使用 。 + /// The file matcher used during the Clean phase (). Compares file nodes between + /// the old and new directories. If null, is used. /// /// - /// 脏阶段()使用的文件匹配器。用于将补丁文件匹配到对应的旧版本文件。 - /// 如果为 null,则使用 。 + /// The file matcher used during the Dirty phase (). Matches patch files to their + /// corresponding old version files. If null, is used. /// /// - /// 可选的进度报告器,用于接收文件级处理进度更新。 + /// An optional progress reporter for receiving file-level processing progress updates. /// /// - /// 当 null 时引发。 + /// Thrown when or is null. /// /// - /// 此构造函数适用于需要完全控制差异比较器、匹配器和进度报告的进阶场景。 - /// 推荐使用 的流畅 API 进行配置。 + /// This constructor is suitable for advanced scenarios requiring full control over the binary differ, + /// matchers, and progress reporting. The fluent API provided by + /// is recommended for configuration. /// public DiffPipeline( DiffPipelineOptions options, @@ -140,14 +144,15 @@ public DiffPipeline( } /// - /// 使用指定的选项、差异比较器和进度报告器初始化管道实例(向后兼容构造函数)。 + /// Initializes a new pipeline instance with the specified options, binary differ, and progress reporter + /// (backward-compatible constructor). /// - /// 管道选项。不能为 null。 - /// 二进制差异比较器。不能为 null。 - /// 可选的进度报告器。 + /// The pipeline options. Must not be null. + /// The binary differ. Must not be null. + /// An optional progress reporter. /// - /// 此构造函数仅用于保持二进制兼容性。新代码应使用接受 ICleanMatcher 和 - /// IDirtyMatcher 参数的重载构造函数。 + /// This constructor is provided only for binary compatibility. New code should use the overload that + /// accepts ICleanMatcher and IDirtyMatcher parameters. /// public DiffPipeline(DiffPipelineOptions options, IBinaryDiffer binaryDiffer, IProgress? progress = null) : this(options, binaryDiffer, null, null, progress) @@ -155,38 +160,44 @@ public DiffPipeline(DiffPipelineOptions options, IBinaryDiffer binaryDiffer, IPr } /// - /// 比较源目录(旧版本)和目标目录(新版本),为发生变化的文件并行生成差异补丁。 + /// Compares the source directory (old version) and target directory (new version), generating differential + /// patches in parallel for files that have changed. /// - /// 旧版本应用程序目录路径。该目录必须存在。 - /// 新版本应用程序目录路径。该目录必须存在。 - /// 补丁文件输出目录路径。如果不存在则自动创建。 - /// 可选的进度报告器,覆盖构造函数中设置的进度报告器。用于接收文件级处理进度更新。 - /// 取消令牌,用于取消正在进行的补丁生成操作。 - /// 表示异步操作的任务。 + /// The old version application directory path. This directory must exist. + /// The new version application directory path. This directory must exist. + /// The patch file output directory path. Created automatically if it does not exist. + /// An optional progress reporter that overrides the one set in the constructor. Receives file-level processing progress updates. + /// A cancellation token to cancel the ongoing patch generation operation. + /// A task that represents the asynchronous operation. /// - /// 当 null 或空白时引发。 + /// Thrown when , , or is null or whitespace. /// /// - /// 当 指定的目录不存在时引发。 + /// Thrown when the directory specified by or does not exist. /// /// - /// 当通过 取消操作时引发。 + /// Thrown when the operation is canceled via . /// /// /// - /// 详细工作流程: + /// Detailed workflow: /// - /// 验证输入目录是否存在。 - /// 使用 比较新旧目录,识别出变化的文件(DifferentNodes)和新增的文件(LeftNodes)。 - /// 对每个变化的文件:计算相对路径、创建临时目录、使用 生成 .patch 文件。 - /// 对每个新增的文件:直接复制到补丁输出目录的相应位置。 - /// 生成 generalupdate_delete_files.json 清单,记录旧版本中已删除(不再存在于新版本中)的文件。 + /// Validates that the input directories exist. + /// Uses to compare the old and new directories, + /// identifying changed files (DifferentNodes) and new files (LeftNodes). + /// For each changed file: computes the relative path, creates a temporary directory, + /// and uses to generate a .patch file. + /// For each new file: copies it directly to the corresponding location in the patch output directory. + /// Generates a generalupdate_delete_files.json manifest recording files that have been + /// deleted from the new version (no longer present in the old version). /// /// /// - /// 文件处理通过 控制并发度,最大并发数由 决定。 - /// 如果 false(默认值),单个文件的失败不会影响其他文件的处理, - /// 错误信息通过进度报告机制传递。如果为 true,任何文件的失败都会立即终止所有处理。 + /// File processing is controlled by a with the maximum concurrency determined + /// by . If + /// is false (default), failure of an individual file does not affect processing of other files, + /// and error details are passed through the progress reporting mechanism. If true, any file failure + /// immediately terminates all processing. /// /// public async Task CleanAsync( @@ -267,37 +278,43 @@ public async Task CleanAsync( } /// - /// 将补丁文件从 并行应用到 中的旧版本文件上。 + /// Applies patch files from to the old version files in in parallel. /// - /// 应用程序安装目录(包含旧版本文件)。 - /// 补丁文件所在目录。 - /// 可选的进度报告器,覆盖构造函数中设置的进度报告器。 - /// 取消令牌,用于取消正在进行的补丁应用操作。 - /// 表示异步操作的任务。 + /// The application installation directory (containing old version files). + /// The directory containing patch files. + /// An optional progress reporter that overrides the one set in the constructor. + /// A cancellation token to cancel the ongoing patch application operation. + /// A task that represents the asynchronous operation. /// /// - /// 详细工作流程: + /// Detailed workflow: /// - /// 如果 不存在,直接返回。 - /// 扫描补丁目录中的所有文件(跳过黑名单目录),查找 generalupdate_delete_files.json 并执行文件删除。 - /// 使用 将补丁文件与旧版本文件进行匹配配对。 - /// 对每个匹配的文件对,使用临时文件策略安全地应用补丁:先将补丁结果写入临时文件, - /// 成功后删除原文件再移动临时文件到原位置,确保应用过程中的故障不会损坏原始文件。 - /// 复制所有不在补丁清单中的未知/新增文件到应用程序目录。 + /// If or does not exist, returns immediately. + /// Scans all files in the patch directory (skipping blacklisted directories), finds the + /// generalupdate_delete_files.json file, and performs file deletion. + /// Uses to pair patch files with their corresponding old version files. + /// For each matched file pair, safely applies the patch using a temporary file strategy: + /// first writes the patch result to a temporary file, then on success deletes the original file and + /// moves the temporary file to the original location, ensuring failures during application do not + /// corrupt the original file. + /// Copies all unknown/new files not present in the patch manifest to the application directory. /// /// /// - /// 删除清单处理细节: - /// 如果补丁目录中包含 generalupdate_delete_files.json 文件,该文件记录了在新版本中已被删除的文件。 - /// 系统通过比较文件中记录的文件哈希值与当前文件的 SHA256 哈希值来识别并删除这些文件。 + /// Deletion manifest handling details: + /// If the patch directory contains a generalupdate_delete_files.json file, this file records the + /// SHA256 hash values of files that have been deleted in the new version. The system identifies and removes + /// these files by comparing the recorded hash values with the SHA256 hash of each current file. /// /// - /// 临时文件策略: - /// 方法使用 {随机文件名}_{原文件名} 的临时文件名, - /// 在确保补丁成功应用后才替换原文件。这种策略最大限度地降低了应用失败时数据丢失的风险。 + /// Temporary file strategy: + /// The method uses a temporary file name of the format {randomFileName}_{originalFileName}. + /// The original file is only replaced after the patch has been successfully applied. This strategy minimizes + /// the risk of data loss in the event of an application failure. /// /// - /// 最后, 会清理补丁目录并将所有新增文件复制到应用程序目录中。 + /// Finally, cleans up the patch directory and copies all new files to the + /// application directory. /// /// public async Task DirtyAsync( @@ -367,24 +384,27 @@ public async Task DirtyAsync( } /// - /// 使用临时文件策略安全地将单个补丁文件应用到对应的应用程序文件。 + /// Safely applies a single patch file to the corresponding application file using a temporary file strategy. /// - /// 要更新的应用程序文件完整路径。 - /// 补丁文件完整路径。 - /// 取消令牌。 + /// The full path to the application file to update. + /// The full path to the patch file. + /// The cancellation token. /// /// - /// 此方法执行以下步骤: + /// This method executes the following steps: /// - /// 检查应用程序文件和补丁文件是否存在,如果任一不存在则跳过。 - /// 在与应用程序文件相同的目录中创建一个临时文件(名称格式:{随机文件名}_{原文件名})。 - /// 调用 将补丁应用到原文件,输出写入临时文件。 - /// 如果补丁应用成功,删除原文件并将临时文件移动至原文件位置。 + /// Checks whether the application file and patch file both exist; skips if either does not exist. + /// Creates a temporary file in the same directory as the application file + /// (file name format: {randomFileName}_{originalFileName}). + /// Calls to apply the patch to the original file, + /// writing output to the temporary file. + /// If the patch application succeeds, deletes the original file and moves the temporary + /// file to the original file location. /// /// /// - /// 这种"写入临时文件→替换原文件"的策略确保了如果补丁应用过程中发生故障, - /// 原始文件不会被损坏或丢失。 + /// This "write to temp file then replace original" strategy ensures that if a failure occurs during + /// patch application, the original file is not corrupted or lost. /// /// private async Task ApplyPatch(string appFilePath, string patchFilePath, CancellationToken ct) @@ -409,20 +429,20 @@ private async Task ApplyPatch(string appFilePath, string patchFilePath, Cancella } /// - /// 处理删除清单(generalupdate_delete_files.json),从应用程序目录中删除已废弃的文件。 + /// Processes the deletion manifest (generalupdate_delete_files.json) and removes obsolete files from the application directory. /// - /// 补丁目录中的文件列表。 - /// 应用程序目录中的文件列表。 + /// The list of files in the patch directory. + /// The list of files in the application directory. /// /// - /// 此方法查找补丁目录中的 generalupdate_delete_files.json 文件, - /// 该文件包含在新版本中已被删除的文件的 SHA256 哈希值列表。 - /// 然后扫描应用程序目录中的每个文件,计算其 SHA256 哈希值并与清单中的值比对, - /// 匹配的文件将被删除。 + /// This method locates the generalupdate_delete_files.json file in the patch directory, + /// which contains a list of SHA256 hash values for files that have been deleted in the new version. + /// It then scans each file in the application directory, computes its SHA256 hash, and compares it + /// against the values in the manifest. Matching files are deleted. /// /// - /// 注意:删除前会将文件属性重置为 , - /// 以防止因只读属性导致删除失败。 + /// Note: Before deletion, file attributes are reset to to prevent + /// deletion failures caused by read-only attributes. /// /// private static void HandleDeleteList(IEnumerable patchFiles, IEnumerable oldFiles) @@ -447,23 +467,27 @@ private static void HandleDeleteList(IEnumerable patchFiles, IEnumerab } /// - /// 将补丁目录中的新增文件(不在旧版本中的文件)复制到应用程序目录,然后清理补丁目录。 + /// Copies new files from the patch directory (files not present in the old version) to the application + /// directory, then cleans up the patch directory. /// - /// 应用程序目录路径。 - /// 补丁目录路径。 - /// 表示异步操作的任务。 + /// The application directory path. + /// The patch directory path. + /// A task that represents the asynchronous operation. /// /// - /// 此方法执行以下操作: + /// This method performs the following operations: /// - /// 比较应用程序目录和补丁目录,找出补丁目录中新增的文件。 - /// 过滤掉黑名单格式(如可执行文件扩展名)的文件。 - /// 将新增文件复制到应用程序目录的相应位置,自动创建缺失的子目录。 - /// 最后删除整个补丁目录,完成清理。 + /// Compares the application directory and patch directory to identify files that are new + /// in the patch directory. + /// Filters out files with blacklisted formats (e.g., executable file extensions). + /// Copies the new files to the corresponding locations in the application directory, + /// automatically creating any missing subdirectories. + /// Finally, deletes the entire patch directory to complete cleanup. /// /// /// - /// 此步骤在脏模式的最后阶段执行,确保所有新增文件都被正确地合并到应用程序目录中。 + /// This step is executed in the final phase of the dirty mode to ensure all new files are correctly + /// merged into the application directory. /// /// private static Task CopyUnknownFiles(string appPath, string patchPath) @@ -492,15 +516,16 @@ private static Task CopyUnknownFiles(string appPath, string patchPath) } /// - /// 根据目标文件信息计算其在补丁输出目录中的临时子目录路径。 + /// Computes the temporary subdirectory path in the patch output directory for a given target file. /// - /// 当前正在处理的文件节点。 - /// 目标(新版本)目录路径。 - /// 补丁输出目录路径。 - /// 文件的临时子目录完整路径。如果文件在目标目录的根目录下,则返回补丁目录路径。 + /// The file node currently being processed. + /// The target (new version) directory path. + /// The patch output directory path. + /// The full path to the file's temporary subdirectory. If the file is at the root of the target directory, returns the patch directory path. /// - /// 此方法通过将文件的完整路径中的目标目录部分替换为补丁目录部分来计算相对路径。 - /// 如果目录不存在,则自动创建。这样可以保持补丁输出目录中的目录结构与目标目录一致。 + /// This method computes the relative path by replacing the target directory portion of the file's full path + /// with the patch directory portion. If the directory does not exist, it is created automatically. + /// This preserves the directory structure in the patch output directory to match the target directory. /// private static string GetTempDirectory(FileNode file, string targetPath, string patchPath) { @@ -514,16 +539,16 @@ private static string GetTempDirectory(FileNode file, string targetPath, string } /// - /// 验证输入目录是否存在且不为空。 + /// Validates that the input directories exist and are not null or empty. /// - /// 源(旧版本)目录路径。 - /// 目标(新版本)目录路径。 - /// 补丁输出目录路径。 - /// 当任意路径为 null 或空白时引发。 - /// 当源目录或目标目录不存在时引发。 + /// The source (old version) directory path. + /// The target (new version) directory path. + /// The patch output directory path. + /// Thrown when any path is null or whitespace. + /// Thrown when the source directory or target directory does not exist. /// - /// 此验证仅在 开始时调用。它确保所有必需的输入目录都已就绪, - /// 避免在执行过程中因路径无效而失败。 + /// This validation is called only at the start of . It ensures that all required + /// input directories are ready, preventing failures due to invalid paths during execution. /// private static void ValidateDirectories(string sourcePath, string targetPath, string patchPath) { diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineBuilder.cs b/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineBuilder.cs index 3a1a0b6e..ae1b0a8e 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineBuilder.cs @@ -7,25 +7,26 @@ namespace GeneralUpdate.Core.Pipeline; /// -/// 的流畅构建器,提供链式调用的配置方式。 +/// Fluent builder for , providing a chained configuration API. /// /// /// -/// 提供了一种声明式的方式来配置和创建 -/// 实例。所有配置方法都返回构建器实例本身,支持链式调用。 +/// offers a declarative way to configure and create +/// instances. All configuration methods return the builder instance +/// itself, supporting method chaining. /// /// -/// 默认配置: +/// Default configuration: /// -/// 差异比较器: -/// 清理匹配器: -/// 脏匹配器: -/// 最大并行度:2 -/// 首次错误停止:false(继续处理其他文件) +/// Binary differ: +/// Clean matcher: +/// Dirty matcher: +/// Maximum parallelism: 2 +/// Stop on first error: false (continue processing other files) /// /// /// -/// 使用示例: +/// Usage example: /// /// var pipeline = new DiffPipelineBuilder() /// .UseDiffer(new StreamingHdiffDiffer()) @@ -36,10 +37,10 @@ namespace GeneralUpdate.Core.Pipeline; /// .WithProgress(new Progress<DiffProgress>(p => Console.WriteLine($"{p.Completed}/{p.Total}"))) /// .Build(); /// -/// // 生成补丁 +/// // Generate patches /// await pipeline.CleanAsync(oldVersionDir, newVersionDir, patchOutputDir); /// -/// // 应用补丁 +/// // Apply patches /// await pipeline.DirtyAsync(appDir, patchDir); /// /// @@ -54,15 +55,16 @@ public class DiffPipelineBuilder private IProgress? _progress; /// - /// 设置用于生成和应用二进制差异补丁的差异比较器。 + /// Sets the binary differ used to generate and apply binary differential patches. /// - /// 实现了 接口的差异比较器实例。不能为 null。 - /// 当前 实例,支持链式调用。 - /// null 时引发。 + /// The binary differ instance implementing . Must not be null. + /// The current instance, enabling chained calls. + /// Thrown when is null. /// - /// 如果未调用此方法,默认使用 。 - /// 基于 HDiffPatch 算法实现,具有较好的压缩率和性能。 - /// 可以通过自定义实现 接口来使用其他差异算法。 + /// If this method is not called, is used by default. + /// is based on the HDiffPatch algorithm and offers good compression + /// ratios and performance. Custom binary differ algorithms can be used by implementing the + /// interface. /// public DiffPipelineBuilder UseDiffer(IBinaryDiffer differ) { @@ -71,23 +73,23 @@ public DiffPipelineBuilder UseDiffer(IBinaryDiffer differ) } /// - /// 设置清理阶段()使用的文件匹配器, - /// 用于目录比较和在补丁生成过程中进行文件匹配。 + /// Sets the file matcher used during the Clean phase (), + /// for directory comparison and file matching during patch generation. /// - /// 实现了 接口的匹配器实例。不能为 null。 - /// 当前 实例,支持链式调用。 - /// null 时引发。 + /// The matcher instance implementing . Must not be null. + /// The current instance, enabling chained calls. + /// Thrown when is null. /// /// - /// 负责两个关键操作: + /// is responsible for two key operations: /// - /// Compare — 比较新旧两个目录,识别出变化的、新增的和删除的文件。 - /// Match — 在补丁生成过程中,将新版本的文件与旧版本中对应的文件进行匹配。 - /// Except — 找出旧版本中有但新版本中已删除的文件。 + /// Compare — Compares the old and new directories, identifying changed, new, and deleted files. + /// Match — During patch generation, matches files from the new version with corresponding files in the old version. + /// Except — Identifies files present in the old version but deleted from the new version. /// /// /// - /// 如果未调用此方法,默认使用 。 + /// If this method is not called, is used by default. /// /// public DiffPipelineBuilder UseCleanMatcher(ICleanMatcher matcher) @@ -97,21 +99,22 @@ public DiffPipelineBuilder UseCleanMatcher(ICleanMatcher matcher) } /// - /// 设置脏阶段()使用的文件匹配器, - /// 用于将补丁文件匹配到应用程序中对应的旧版本文件。 + /// Sets the file matcher used during the Dirty phase (), + /// for matching patch files to their corresponding old version files in the application directory. /// - /// 实现了 接口的匹配器实例。不能为 null。 - /// 当前 实例,支持链式调用。 - /// null 时引发。 + /// The matcher instance implementing . Must not be null. + /// The current instance, enabling chained calls. + /// Thrown when is null. /// /// - /// 在脏阶段,补丁目录中的每个补丁文件需要与应用程序目录中对应的原始文件配对。 - /// 方法接收一个旧文件对象和补丁文件列表, - /// 返回与之匹配的补丁文件。 + /// During the Dirty phase, each patch file in the patch directory needs to be paired with its corresponding + /// original file in the application directory. The method receives an old + /// file object and the list of patch files, and returns the matching patch file. /// /// - /// 默认匹配器 通过文件路径的相对路径进行匹配。 - /// 自定义匹配器可以实现基于文件名、哈希值或其他策略的匹配逻辑。 + /// The default matcher performs matching based on the relative path + /// of file paths. Custom matchers can implement matching logic based on file names, hash values, or + /// other strategies. /// /// public DiffPipelineBuilder UseDirtyMatcher(IDirtyMatcher matcher) @@ -121,24 +124,26 @@ public DiffPipelineBuilder UseDirtyMatcher(IDirtyMatcher matcher) } /// - /// 设置文件处理的最大并行度。 + /// Sets the maximum degree of parallelism for file processing. /// - /// 最大并行文件处理数。必须大于 0。 - /// 当前 实例,支持链式调用。 + /// The maximum number of files to process concurrently. Must be greater than 0. + /// The current instance, enabling chained calls. /// - /// 当 小于 1 时引发。 + /// Thrown when is less than 1. /// /// /// - /// 此值控制 - /// 中同时处理的文件数量。较高的值可以提高多核系统上的处理速度,但也会增加内存和 I/O 资源消耗。 + /// This value controls the number of files processed simultaneously in + /// and . + /// Higher values can improve processing speed on multi-core systems but also increase memory and I/O + /// resource consumption. /// /// - /// 建议: + /// Recommendations: /// - /// 2(默认值):适用于大多数场景,平衡速度和资源消耗。 - /// 1:完全串行处理,适用于 I/O 受限或资源敏感的环境。 - /// 4-8:适用于多核 CPU 和快速 SSD 的高性能环境。 + /// 2 (default): Suitable for most scenarios, balancing speed and resource consumption. + /// 1: Fully serial processing, suitable for I/O-bound or resource-sensitive environments. + /// 4-8: Suitable for high-performance environments with multi-core CPUs and fast SSDs. /// /// /// @@ -151,24 +156,26 @@ public DiffPipelineBuilder WithParallelism(int maxDegreeOfParallelism) } /// - /// 设置是否在首次文件处理错误时立即停止整个管道。 + /// Sets whether the entire pipeline should stop immediately when the first file processing error occurs. /// /// - /// 如果为 true,任何文件的处理失败都会导致整个操作立即终止并抛出异常。 - /// 如果为 false(默认值),失败的文件会被跳过,处理继续,错误信息通过进度报告传递。 + /// If true, any file processing failure causes the entire operation to terminate immediately + /// and throw an exception. If false (default), failed files are skipped, processing continues, + /// and error details are passed through progress reporting. /// - /// 当前 实例,支持链式调用。 + /// The current instance, enabling chained calls. /// /// - /// 当 false 时(默认值): - /// 单个文件的失败不会影响其他文件的处理。失败的详细信息通过 - /// 传递,调用方可以通过进度报告机制检查每个文件的处理状态。 - /// 这在批量处理大量文件时特别有用,可以最大限度地减少失败的影響。 + /// When is false (default): + /// Failure of an individual file does not affect the processing of other files. Failure details are + /// communicated through , and callers can inspect the processing + /// status of each file through the progress reporting mechanism. This is particularly useful when + /// processing large batches of files, minimizing the impact of failures. /// /// - /// 当为 true 时: - /// 任何文件的失败都会立即取消所有正在进行的处理任务并抛出异常。 - /// 适用于对数据完整性要求极高的场景。 + /// When true: + /// Any file failure immediately cancels all ongoing processing tasks and throws an exception. + /// Suitable for scenarios with high data integrity requirements. /// /// public DiffPipelineBuilder WithStopOnFirstError(bool stopOnFirstError = true) @@ -178,21 +185,21 @@ public DiffPipelineBuilder WithStopOnFirstError(bool stopOnFirstError = true) } /// - /// 附加一个进度报告器,用于接收实时的文件级进度更新。 + /// Attaches a progress reporter to receive real-time file-level progress updates. /// /// - /// 实现了 接口的进度报告器实例。不能为 null。 + /// The progress reporter instance implementing . Must not be null. /// - /// 当前 实例,支持链式调用。 - /// null 时引发。 + /// The current instance, enabling chained calls. + /// Thrown when is null. /// /// - /// 将在每个文件处理完成时收到通知, - /// 包含已完成数、总数、当前文件名和可选的错误消息。 - /// 可以使用 或自定义实现。 + /// The will be notified when each file finishes processing, + /// providing the number of completed files, total files, current file name, and optional error message. + /// Use or a custom implementation. /// /// - /// 示例: + /// Example: /// /// var progress = new Progress<DiffProgress>(p => /// { @@ -210,18 +217,19 @@ public DiffPipelineBuilder WithProgress(IProgress progress) } /// - /// 使用当前配置构建 实例。 + /// Builds a instance using the current configuration. /// - /// 配置完成的 实例。 + /// A fully configured instance. /// /// - /// 此方法将所有配置的参数封装到 中, - /// 并使用默认值补充任何未显式设置的参数(如差异比较器默认使用 )。 - /// 然后创建一个新的 实例并返回。 + /// This method packages all configured parameters into a instance, + /// fills in default values for any parameters not explicitly set (e.g., the binary differ defaults to + /// ), creates a new instance, and returns it. /// /// - /// 构建后的管道可以用于生成补丁()或应用补丁 - /// ()。管道实例是线程安全的,可重复使用。 + /// The built pipeline can be used for generating patches () or + /// applying patches (). Pipeline instances are thread-safe and + /// can be reused. /// /// public DiffPipeline Build() diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineOptions.cs b/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineOptions.cs index c6b0fe02..42b12187 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineOptions.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DiffPipelineOptions.cs @@ -3,63 +3,65 @@ namespace GeneralUpdate.Core.Pipeline; /// -/// 配置 运行时的选项。 +/// Configures runtime options for . /// /// /// -/// 定义了差异管道的运行时行为参数。 -/// 这些选项通过 的流畅 API 进行配置, -/// 或直接传递给 的构造函数。 +/// defines the runtime behavior parameters of the differential pipeline. +/// These options can be configured via the fluent API of or passed directly +/// to the constructor. /// /// -/// 默认值: +/// Default values: /// -/// = 2 — 同时处理 2 个文件。 -/// = false — 出现错误时继续处理其他文件。 +/// = 2 — Processes 2 files concurrently. +/// = false — Continues processing other files on error. /// /// /// public sealed class DiffPipelineOptions { /// - /// 获取或设置可以同时处理的文件最大数量。 + /// Gets or sets the maximum number of files that can be processed concurrently. /// /// - /// 最大并行文件处理数。默认值为 2。设置为 1 可强制串行处理。 + /// The maximum number of files to process in parallel. The default value is 2. + /// Set to 1 to force serial processing. /// /// /// - /// 此属性控制 内部使用的 - /// 的初始计数,用于限制并发文件处理的任务数。 + /// This property controls the initial count of the + /// used internally by to limit the number of concurrent file processing tasks. /// /// - /// 调优建议: + /// Tuning recommendations: /// - /// 1:完全串行执行,内存占用最低,适用于资源受限环境。 - /// 2(默认值):最小并行度,在大多数环境下提供良好的性能提升。 - /// 4:适用于现代多核 CPU 和 SSD 存储。 - /// 8 以上:适用于高性能服务器,需注意 I/O 饱和。 + /// 1: Fully serial execution, minimum memory usage, suitable for resource-constrained environments. + /// 2 (default): Minimum parallelism, provides good performance gains in most environments. + /// 4: Suitable for modern multi-core CPUs and SSD storage. + /// 8+: Suitable for high-performance servers; be mindful of I/O saturation. /// /// /// public int MaxDegreeOfParallelism { get; set; } = 2; /// - /// 获取或设置一个值,指示在文件处理出错时是否立即停止整个管道。 + /// Gets or sets a value indicating whether the pipeline should stop immediately when a file processing error occurs. /// /// - /// 如果为 true,第一个文件错误会导致管道立即终止并抛出异常; - /// 如果为 false(默认值),错误被记录并通过进度报告传递,管道继续处理剩余文件。 + /// If true, the first file error causes the pipeline to terminate immediately and throw an exception; + /// if false (default), errors are logged and passed through progress reporting, and the pipeline + /// continues processing remaining files. /// /// /// - /// 当此属性为 false 时,单个文件的处理失败不会影响其他文件。 - /// 失败信息通过 属性传递给进度报告器。 - /// 处理完成后,调用方可以检查每个文件的处理结果。 + /// When this property is false, a processing failure for an individual file does not affect other files. + /// Failure information is passed to the progress reporter via the property. + /// After processing completes, callers can inspect the results for each file. /// /// - /// 当此属性为 true 时,任何文件的失败都会取消所有正在进行的任务, - /// 并向调用方抛出异常。适用于对数据一致性要求极高的更新场景。 + /// When this property is true, any file failure cancels all ongoing tasks and throws an exception + /// to the caller. Suitable for update scenarios with high data consistency requirements. /// /// public bool StopOnFirstError { get; set; } = false; diff --git a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs index cf804bdc..13278645 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs @@ -8,45 +8,48 @@ namespace GeneralUpdate.Core.Pipeline; /// -/// 哈希验证中间件,用于验证下载的压缩包的 SHA256 完整性。 +/// Hash verification middleware for validating the SHA256 integrity of a downloaded archive. /// /// /// -/// 此中间件从 中读取以下键: +/// This middleware reads the following keys from : /// -/// "ZipFilePath" — 已下载的压缩包文件路径。 -/// "Hash" — 预期的 SHA256 哈希值(十六进制字符串)。 +/// "ZipFilePath" — The path to the downloaded archive file. +/// "Hash" — The expected SHA256 hash value (hexadecimal string). /// /// /// -/// 工作流程: +/// Workflow: /// -/// 从上下文获取压缩包路径和期望的哈希值。 -/// 使用 计算文件的实际 SHA256 哈希值。 -/// 将实际哈希值与期望哈希值进行不区分大小写的比较。 -/// 如果匹配,则记录成功日志并继续;如果不匹配,则抛出 终止管道。 +/// Retrieves the archive path and expected hash from the context. +/// Computes the actual SHA256 hash of the file using . +/// Performs a case-insensitive comparison between the actual and expected hash values. +/// If they match, logs success and continues; if they do not match, throws a to terminate the pipeline. /// /// /// -/// 此中间件应在 之前注册,确保在解压缩之前验证包的完整性。 +/// This middleware should be registered before to ensure the integrity +/// of the archive is verified before decompression. /// /// public class HashMiddleware : IMiddleware { /// - /// 异步执行哈希验证逻辑。 + /// Asynchronously executes the hash verification logic. /// - /// 管道上下文,包含压缩包路径和期望的哈希值。 - /// 表示异步操作的任务。 - /// 当实际文件的 SHA256 哈希值与期望哈希值不匹配时引发。 - /// 文件读取或哈希计算过程中的其他异常。 + /// The pipeline context containing the archive path and expected hash value. + /// A task that represents the asynchronous operation. + /// Thrown when the actual SHA256 hash of the file does not match the expected hash. + /// Other exceptions that may occur during file reading or hash computation. /// /// - /// 此方法是管道的第一个安全检查阶段。它确保下载的压缩包在传输过程中未被篡改或损坏。 + /// This method represents the first security-check stage of the pipeline. It ensures that the downloaded + /// archive has not been tampered with or corrupted during transit. /// /// - /// 哈希计算在后台线程上执行(通过 ),避免阻塞调用线程。 - /// 验证失败时, 会向上传播,中断整个管道执行。 + /// Hash computation is performed on a background thread (via ) to avoid blocking + /// the calling thread. On verification failure, the propagates upward + /// to halt the entire pipeline execution. /// /// public async Task InvokeAsync(PipelineContext context) @@ -76,17 +79,19 @@ public async Task InvokeAsync(PipelineContext context) } /// - /// 使用 SHA256 算法计算文件的哈希值并与期望值进行比较。 + /// Computes the SHA256 hash of a file and compares it against the expected value. /// - /// 要验证的文件完整路径。 - /// 期望的 SHA256 哈希值(十六进制字符串)。 + /// The full path to the file to verify. + /// The expected SHA256 hash value (hexadecimal string). /// - /// 如果文件的实际 SHA256 哈希值与 匹配(不区分大小写),则为 true;否则为 false。 + /// true if the actual SHA256 hash of the file matches + /// (case-insensitive comparison); otherwise, false. /// /// - /// 哈希计算通过 在后台线程池线程上执行,以避免阻塞。 - /// 内部使用 计算文件哈希,该算法实现了标准的 SHA256 哈希计算。 - /// 比较操作用 进行不区分大小写的十六进制字符串比较。 + /// Hash computation is performed on a background thread-pool thread via + /// to avoid blocking. Internally uses to compute the file hash, + /// which implements the standard SHA256 hash algorithm. The comparison uses + /// for case-insensitive hexadecimal string comparison. /// private Task VerifyFileHash(string path, string hash) { diff --git a/src/c#/GeneralUpdate.Core/Pipeline/IMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/IMiddleware.cs index f6c6c622..ed90f8c5 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/IMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/IMiddleware.cs @@ -3,31 +3,32 @@ namespace GeneralUpdate.Core.Pipeline { /// - /// 定义管道中间件的契约接口。 + /// Defines the contract interface for pipeline middleware. /// /// /// - /// 所有管道中间件必须实现此接口。 方法在管道构建 - /// ()的 执行期间按注册顺序 - /// (FIFO)依次调用。每个中间件负责一个独立的处理阶段,例如哈希验证、解压缩或应用差异补丁。 + /// All pipeline middleware must implement this interface. The method is + /// called sequentially in registration order (FIFO) during execution of the pipeline built by + /// . Each middleware is responsible for an independent processing stage, + /// such as hash verification, decompression, or applying differential patches. /// /// - /// 中间件之间通过 共享数据。上游中间件将计算结果写入上下文, - /// 下游中间件从中读取所需的输入。 + /// Middleware share data through . Upstream middleware write computed results + /// into the context, and downstream middleware read the required inputs from it. /// /// /// - /// 以下示例演示如何实现一个自定义中间件: + /// The following example demonstrates how to implement a custom middleware: /// /// public class MyMiddleware : IMiddleware /// { /// public async Task InvokeAsync(PipelineContext context) /// { - /// // 从上下文中读取数据 + /// // Read data from the context /// var data = context.Get<string>("MyKey"); - /// // 执行处理逻辑 + /// // Execute processing logic /// await Task.Run(() => { /* ... */ }); - /// // 将结果写回上下文 + /// // Write results back to the context /// context.Add("Result", "processed"); /// } /// } @@ -36,17 +37,18 @@ namespace GeneralUpdate.Core.Pipeline public interface IMiddleware { /// - /// 异步执行中间件的处理逻辑。 + /// Asynchronously executes the processing logic of the middleware. /// /// - /// 管道上下文,包含当前执行环境的状态和中间件之间共享的数据。 - /// 实现应从该上下文读取输入参数,并将处理结果写入其中。 + /// The pipeline context, which contains the state of the current execution environment and + /// shared data between middleware. Implementations should read input parameters from this context + /// and write processing results into it. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// - /// 此方法由 在遍历中间件队列时依次调用。 - /// 实现应避免长时间阻塞,始终使用异步模式(await)。 - /// 如果处理失败,应抛出异常以中断管道执行。 + /// This method is called sequentially by when traversing the + /// middleware queue. Implementations should avoid long blocking operations and always use asynchronous + /// patterns (await). If processing fails, an exception should be thrown to halt pipeline execution. /// Task InvokeAsync(PipelineContext context); } diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs index a7ee47a0..1e93b263 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -5,54 +5,58 @@ namespace GeneralUpdate.Core.Pipeline; /// -/// 差异补丁中间件,使用 将二进制差异补丁并行应用到应用程序文件。 +/// Differential patch middleware that uses to apply binary differential patches +/// to application files in parallel. /// /// /// -/// 此中间件从 中读取以下键: +/// This middleware reads the following keys from : /// -/// "SourcePath" — 应用程序安装目标路径(旧版本文件所在目录)。 -/// "PatchPath" — 差异补丁文件的存放路径(包含解压后的补丁数据)。 -/// "DiffPipeline" 实例,由 构建并注入。 +/// "SourcePath" — The application installation target path (directory containing old version files). +/// "PatchPath" — The storage path for differential patch files (contains decompressed patch data). +/// "DiffPipeline" — The instance, built and injected by . /// /// /// -/// 工作流程: +/// Workflow: /// -/// 从上下文获取源路径、补丁路径和 实例。 -/// 调用 -/// 将补丁并行应用到所有旧版文件上。 -/// 处理过程中的进度通过 内部的进度报告机制反馈。 +/// Retrieves the source path, patch path, and instance from the context. +/// Calls +/// to apply patches to all old version files in parallel. +/// Progress during processing is reported through the progress reporting mechanism inside . /// /// /// -/// 此中间件应在 之后注册, -/// 以确保压缩包已验证完整性且已正确解压到 "PatchPath" 目录。 -/// 如果未启用差异补丁("PatchEnabled"false),此中间件不应被注册到管道中。 +/// This middleware should be registered after and , +/// to ensure the archive has been verified for integrity and correctly extracted to the "PatchPath" directory. +/// If differential patching is not enabled ("PatchEnabled" is false), this middleware should not be +/// registered in the pipeline. /// /// public class PatchMiddleware : IMiddleware { /// - /// 异步执行差异补丁应用逻辑。 + /// Asynchronously executes the differential patch application logic. /// - /// 管道上下文,包含源路径、补丁路径和 实例。 - /// 表示异步操作的任务。 + /// The pipeline context containing the source path, patch path, and instance. + /// A task that represents the asynchronous operation. /// - /// 当 中未包含 "DiffPipeline" 键时引发。 - /// 调用方需确保 已构建并注入了 实例。 + /// Thrown when the does not contain the "DiffPipeline" key. + /// Callers must ensure that has built and injected the instance. /// - /// 差异补丁应用过程中发生的其他异常。 + /// Other exceptions that may occur during differential patch application. /// /// - /// 此方法是更新管道的最终处理阶段。它将之前解压到 "PatchPath" 的补丁文件 - /// 并行应用到 "SourcePath" 中的旧版本文件上,生成更新后的文件。 - /// 补丁应用使用 - /// 方法,该方法内部通过信号量控制并发度,并在所有文件处理完成后复制未知的新文件。 + /// This method is the final processing stage of the update pipeline. It applies the patch files + /// previously extracted to "PatchPath" onto the old version files in "SourcePath" in parallel, + /// producing the updated files. Patch application uses + /// , + /// which internally controls concurrency via a semaphore and copies unknown new files after all files are processed. /// /// - /// 如果 未在上下文中配置,此中间件会抛出 - /// 并提供明确的错误消息,指导用户检查引导程序配置。 + /// If is not configured in the context, this middleware throws an + /// with a clear error message guiding the user to check the + /// bootstrapper configuration. /// /// public async Task InvokeAsync(PipelineContext context) diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PipelineBuilder.cs b/src/c#/GeneralUpdate.Core/Pipeline/PipelineBuilder.cs index 3fa41e09..4dced78b 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PipelineBuilder.cs @@ -5,31 +5,31 @@ namespace GeneralUpdate.Core.Pipeline { /// - /// 管道构建器,采用 FIFO(先进先出)顺序注册和执行中间件。 + /// Pipeline builder that registers and executes middleware in FIFO (first-in, first-out) order. /// /// /// - /// 是 GeneralUpdate 管道模式的核心编排组件。 - /// 它管理一个 实例的不可变队列,中间件按注册顺序排队, - /// 在调用 时按 FIFO 顺序依次异步执行。 + /// is the core orchestration component of the GeneralUpdate pipeline pattern. + /// It manages an immutable queue of instances. Middleware are enqueued in + /// registration order, and during they execute sequentially in FIFO order asynchronously. /// /// - /// 典型的更新管道流程如下: + /// A typical update pipeline flow is as follows: /// - /// — 验证下载压缩包的 SHA256 哈希值。 - /// — 将压缩包解压到目标目录。 - /// — 应用二进制差异补丁(如果启用了补丁功能)。 + /// — Verifies the SHA256 hash of the downloaded archive. + /// — Decompresses the archive to the target directory. + /// — Applies binary differential patches (if patching is enabled). /// /// /// - /// 使用 无条件注册中间件, - /// 或使用 根据条件决定是否注册。 + /// Use to unconditionally register middleware, + /// or to register conditionally. /// /// /// /// /// var context = new PipelineContext(); - /// // 设置上下文数据... + /// // Set context data... /// var builder = new PipelineBuilder(context) /// .UseMiddleware<HashMiddleware>() /// .UseMiddlewareIf<CompressMiddleware>(true) @@ -40,25 +40,26 @@ namespace GeneralUpdate.Core.Pipeline public sealed class PipelineBuilder(PipelineContext context) { /// - /// 中间件不可变队列,FIFO(先进先出)顺序。 + /// Immutable queue of middleware, maintained in FIFO (first-in, first-out) order. /// /// - /// 使用 保证注册后队列内容不被意外修改。 - /// 每次 操作都返回一个新的队列实例,原始实例保持不变。 + /// Uses to guarantee that the queue contents cannot be accidentally + /// modified after registration. Each operation returns a new + /// queue instance, leaving the original instance unchanged. /// private ImmutableQueue _middlewareQueue = ImmutableQueue.Empty; /// - /// 向管道注册一个中间件。每次调用都会将中间件实例排队到队列尾部。 + /// Registers a middleware type into the pipeline. Each call enqueues a middleware instance at the tail of the queue. /// /// - /// 要注册的中间件类型。必须实现 接口且具有无参数构造函数。 + /// The type of middleware to register. Must implement the interface and have a parameterless constructor. /// - /// 当前 实例,支持链式调用。 + /// The current instance, enabling chained calls. /// - /// 此方法始终注册中间件,无论任何条件。如果需要条件注册,请使用 - /// 。 - /// 中间件实例通过 new TMiddleware() 创建,因此类型必须具有公开的无参数构造函数。 + /// This method always registers the middleware regardless of any condition. For conditional registration, + /// use . + /// The middleware instance is created via new TMiddleware(), so the type must have a public parameterless constructor. /// public PipelineBuilder UseMiddleware() where TMiddleware : IMiddleware, new() { @@ -68,19 +69,20 @@ public sealed class PipelineBuilder(PipelineContext context) } /// - /// 根据条件向管道注册中间件。仅当条件为 true 时才注册。 + /// Conditionally registers a middleware type into the pipeline. The middleware is only registered when the condition is true. /// /// - /// 要注册的中间件类型。必须实现 接口且具有无参数构造函数。 + /// The type of middleware to register. Must implement the interface and have a parameterless constructor. /// /// - /// 注册条件。如果为 nullfalse,则跳过注册; - /// 如果为 true,则创建并注册中间件实例。 + /// The registration condition. If null or false, registration is skipped; + /// if true, a middleware instance is created and registered. /// - /// 当前 实例,支持链式调用。 + /// The current instance, enabling chained calls. /// - /// 此方法适用于需要根据运行时配置或功能开关决定是否启用某个处理阶段的场景。 - /// 例如,仅在启用差异补丁时才注册 : + /// This method is suitable for scenarios where certain processing stages should be enabled or disabled + /// based on runtime configuration or feature flags. For example, register + /// only when differential patching is enabled: /// /// builder.UseMiddlewareIf<PatchMiddleware>(isPatchEnabled); /// @@ -97,18 +99,20 @@ public PipelineBuilder UseMiddlewareIf(bool? condition) } /// - /// 按 FIFO 顺序异步执行所有已注册的中间件。 + /// Asynchronously executes all registered middleware in FIFO order. /// - /// 表示异步操作的任务。当所有中间件执行完毕时,该任务完成。 + /// A task that represents the asynchronous operation. The task completes when all middleware have finished executing. /// /// - /// 此方法遍历 ,对每个中间件依次调用 - /// 。 - /// 中间件按注册顺序(先进先出)串行执行——前一个中间件完成后才会执行下一个。 + /// This method traverses the and calls + /// on each middleware in sequence. + /// Middleware execute serially in registration order (first-in, first-out) — the next middleware + /// starts only after the previous one has completed. /// /// - /// 如果任何中间件抛出异常,管道将立即终止,后续中间件不会执行。 - /// 调用者应捕获并处理异常以执行适当的错误恢复逻辑。 + /// If any middleware throws an exception, the pipeline terminates immediately and subsequent + /// middleware will not execute. Callers should catch and handle exceptions to perform appropriate + /// error recovery logic. /// /// public async Task Build() diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PipelineContext.cs b/src/c#/GeneralUpdate.Core/Pipeline/PipelineContext.cs index fef30986..1a1f4803 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PipelineContext.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PipelineContext.cs @@ -4,65 +4,67 @@ namespace GeneralUpdate.Core.Pipeline; /// -/// 管道上下文,为中间件提供线程安全的键值对存储,用于在管道的各个阶段之间共享数据。 +/// Pipeline context that provides thread-safe key-value storage for sharing data across pipeline stages. /// /// /// -/// 是中间件之间通信的核心载体。每个中间件可以从上下文中读取上游中间件产生的结果, -/// 并将自身的处理结果写入上下文,供下游中间件使用。 -/// 在构造时接收一个上下文实例,该实例在整个管道生命周期内保持不变。 +/// is the core communication medium between middleware. Each middleware can read +/// results produced by upstream middleware from the context and write its own processing results into it +/// for downstream middleware to consume. receives a context instance at construction +/// time, and that instance remains consistent throughout the entire pipeline lifecycle. /// /// -/// 内部使用 保证线程安全,支持在多线程环境中安全地读写。 +/// Internally uses to guarantee thread safety, +/// supporting safe reads and writes in multi-threaded environments. /// /// -/// 以下是更新管道中预定义的上下文键及其说明: +/// The following are predefined context keys used in the update pipeline and their descriptions: /// /// /// -/// 键名 -/// 类型 -/// 说明 +/// Key +/// Type +/// Description /// /// /// "Hash" /// -/// 期望的 SHA256 哈希值,用于验证下载的压缩包完整性。 +/// The expected SHA256 hash value, used to verify the integrity of the downloaded archive. /// /// /// "Format" /// -/// 压缩包格式(如 ZIP、GZip 等),用于解压缩操作。 +/// The archive format (e.g., ZIP, GZip) used for decompression operations. /// /// /// "Encoding" /// -/// 解压缩时使用的字符编码。 +/// The character encoding used during decompression. /// /// /// "ZipFilePath" /// -/// 已下载的压缩包文件完整路径。 +/// The full path to the downloaded archive file. /// /// /// "SourcePath" /// -/// 应用程序安装目标路径。 +/// The application installation target path. /// /// /// "PatchPath" /// -/// 差异补丁文件的临时存放路径。 +/// The temporary storage path for differential patch files. /// /// /// "PatchEnabled" /// -/// 指示是否启用了差异补丁功能。若为 false,解压缩结果将直接写入 "SourcePath" +/// Indicates whether the differential patch feature is enabled. If false, decompression results are written directly to "SourcePath". /// /// /// "DiffPipeline" /// -/// 差异补丁管道的实例,由 构建并注入。 +/// An instance of the differential patch pipeline, built and injected by . /// /// /// @@ -71,17 +73,18 @@ public class PipelineContext private ConcurrentDictionary _context = new(); /// - /// 从上下文中获取指定键的强类型值。 + /// Retrieves a strongly-typed value associated with the specified key from the context. /// - /// 值的期望类型。如果存储的值不是此类型,则返回 default - /// 要查找的键。区分大小写。 + /// The expected type of the value. Returns default if the stored value is not of this type. + /// The key to look up. Case-sensitive. /// - /// 与指定键关联的强类型值;如果键不存在或类型不匹配,则返回 default(TValue)。 - /// 注意,对于引用类型,defaultnull;对于值类型,default 为零值。 + /// The strongly-typed value associated with the specified key; or default(TValue) if the key + /// does not exist or the type does not match. Note that default is null for reference types + /// and the zero value for value types. /// /// - /// 使用 - /// 实现,是线程安全的读取操作。典型用法: + /// Implemented using , + /// this is a thread-safe read operation. Typical usage: /// /// var path = context.Get<string>("ZipFilePath"); /// var format = context.Get<Configuration.Format>("Format"); @@ -97,17 +100,17 @@ public class PipelineContext } /// - /// 向上下文添加或更新指定键的值。 + /// Adds or updates the value for the specified key in the context. /// - /// 要存储的值的类型。 - /// 键名,不能为 null 或空白字符串。 - /// 要存储的值。可以为 null。 + /// The type of the value to store. + /// The key name. Must not be null or whitespace. + /// The value to store. Can be null. /// - /// 当 null 或仅包含空白字符时引发。 + /// Thrown when is null or consists only of whitespace characters. /// /// - /// 如果指定键已存在,则使用新值覆盖旧值(即实现 Upsert 语义)。 - /// 此操作是线程安全的。典型用法: + /// If the specified key already exists, the existing value is overwritten with the new value + /// (i.e., implements Upsert semantics). This operation is thread-safe. Typical usage: /// /// context.Add("Hash", "A1B2C3D4E5F6..."); /// context.Add("PatchEnabled", true); @@ -124,24 +127,24 @@ public void Add(string key, TValue? value) } /// - /// 从上下文中移除指定键及其关联值。 + /// Removes the specified key and its associated value from the context. /// - /// 要移除的键名。 - /// 如果键存在并被成功移除,则为 true;否则为 false + /// The key to remove. + /// true if the key existed and was successfully removed; otherwise, false. /// - /// 此操作使用 - /// 实现,是线程安全的。如果指定的键不存在,则返回 false 且不引发异常。 + /// This operation uses + /// and is thread-safe. If the specified key does not exist, it returns false without throwing an exception. /// public bool Remove(string key) => _context.TryRemove(key, out _); /// - /// 检查上下文中是否包含指定的键。 + /// Checks whether the specified key exists in the context. /// - /// 要检查的键名。 - /// 如果键存在,则为 true;否则为 false + /// The key to check. + /// true if the key exists; otherwise, false. /// - /// 此操作使用 - /// 实现,是线程安全的只读检查。不会修改上下文中的任何数据。 + /// This operation uses + /// and is a thread-safe read-only check. It does not modify any data in the context. /// public bool ContainsKey(string key) => _context.ContainsKey(key); } diff --git a/src/c#/GeneralUpdate.Core/Security/IHttpAuthProvider.cs b/src/c#/GeneralUpdate.Core/Security/IHttpAuthProvider.cs index f5df56b4..ebcdd30e 100644 --- a/src/c#/GeneralUpdate.Core/Security/IHttpAuthProvider.cs +++ b/src/c#/GeneralUpdate.Core/Security/IHttpAuthProvider.cs @@ -8,22 +8,94 @@ namespace GeneralUpdate.Core.Security; +/// +/// Defines a strategy for applying HTTP authentication to outgoing requests. +/// +/// +/// +/// Implementations of this interface inject authentication credentials into +/// instances before they are sent to the server. +/// The authentication mechanism is abstracted so that different schemes (Bearer token, +/// API key, HMAC signature, etc.) can be used interchangeably. +/// +/// +/// Built-in implementations: +/// +/// — performs no authentication. +/// — sets the Authorization: Bearer header. +/// — sets a custom header with the API key value. +/// — computes an HMAC-SHA256 signature over the request body and timestamp. +/// +/// +/// +/// Use to instantiate the appropriate provider +/// based on a scheme string and token, or set a global default via +/// . +/// +/// public interface IHttpAuthProvider { + /// + /// Applies authentication credentials to the specified HTTP request. + /// + /// The HTTP request message to authenticate. Headers and properties may be modified. + /// A for cancelling the authentication operation. + /// A task representing the asynchronous authentication operation. + /// + /// Implementations should modify in place by adding or modifying + /// headers. The method is asynchronous to support schemes that require computation or + /// external lookups (e.g., HMAC signature generation over request content). + /// Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = default); } +/// +/// An authentication provider that performs no authentication. +/// Used as the default when no authentication scheme is configured. +/// +/// +/// This provider simply returns a completed task without modifying the request. +/// It is suitable for public APIs or when authentication is handled by other means +/// (e.g., network-level authentication, reverse proxy). +/// public sealed class NoOpAuthProvider : IHttpAuthProvider { + /// + /// Applies no authentication. Returns immediately without modifying the request. + /// + /// The HTTP request message (unmodified). + /// A (ignored by this implementation). + /// A completed task. public Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = default) => Task.CompletedTask; } +/// +/// An authentication provider that uses the Bearer Token scheme +/// (Authorization: Bearer <token>). +/// +/// +/// This provider sets the Authorization header to Bearer <token> on the +/// outgoing request. This is one of the most common authentication schemes for REST APIs. +/// public sealed class BearerTokenAuthProvider : IHttpAuthProvider { private readonly string _token; + + /// + /// Initializes a new instance of the class. + /// + /// The Bearer token value. Must not be null. + /// Thrown when is null. public BearerTokenAuthProvider(string token) => _token = token ?? throw new ArgumentNullException(nameof(token)); + + /// + /// Applies the Bearer token authentication by setting the Authorization header. + /// + /// The HTTP request message to authenticate. + /// A (ignored by this implementation). + /// A completed task after setting the header. public Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = default) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); @@ -31,15 +103,36 @@ public Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = } } +/// +/// An authentication provider that sends an API key via a custom HTTP header. +/// +/// +/// The default header name is X-Api-Key, but a custom header name can be specified +/// in the constructor. This scheme is commonly used by API gateways and managed API services. +/// public sealed class ApiKeyAuthProvider : IHttpAuthProvider { private readonly string _h; private readonly string _k; + + /// + /// Initializes a new instance of the class. + /// + /// The API key value. Must not be null. + /// The HTTP header name to use. Defaults to X-Api-Key. Must not be null. + /// Thrown when or is null. public ApiKeyAuthProvider(string apiKey, string headerName = "X-Api-Key") { _k = apiKey ?? throw new ArgumentNullException(nameof(apiKey)); _h = headerName ?? throw new ArgumentNullException(nameof(headerName)); } + + /// + /// Applies the API key authentication by adding the configured header to the request. + /// + /// The HTTP request message to authenticate. + /// A (ignored by this implementation). + /// A completed task after adding the header. public Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = default) { request.Headers.Add(_h, _k); @@ -47,11 +140,55 @@ public Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = } } +/// +/// An authentication provider that computes an HMAC-SHA256 signature over the +/// request body and current timestamp. +/// +/// +/// +/// This provider implements a request-signing authentication scheme. It adds two headers: +/// +/// X-Update-Timestamp: the current Unix timestamp in seconds. +/// X-Update-Signature: an HMAC-SHA256 hash of the concatenation +/// body|timestamp using the configured secret key. +/// +/// +/// +/// The receiving server recomputes the signature using the same secret to verify the +/// request's authenticity and integrity, and can optionally reject requests with stale +/// timestamps to prevent replay attacks. +/// +/// public sealed class HmacAuthProvider : IHttpAuthProvider { private readonly string _secret; + + /// + /// Initializes a new instance of the class. + /// + /// The shared secret key used for HMAC signing. Must not be null. + /// Thrown when is null. public HmacAuthProvider(string secretKey) => _secret = secretKey ?? throw new ArgumentNullException(nameof(secretKey)); + + /// + /// Applies HMAC authentication by computing a signature over the request body and timestamp. + /// + /// The HTTP request message to authenticate. + /// A for cancelling the operation. + /// A task representing the asynchronous operation. + /// + /// + /// Execution flow: + /// + /// Reads the request body content as a string (or empty string if no content). + /// Gets the current UTC Unix timestamp as a string. + /// Computes an HMAC-SHA256 hash of the concatenated string body|timestamp + /// using the configured secret key. + /// Sets the X-Update-Timestamp and X-Update-Signature headers on the request. + /// + /// + /// public async Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = default) { var body = request.Content != null @@ -61,6 +198,13 @@ public async Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken t request.Headers.Add("X-Update-Timestamp", ts); request.Headers.Add("X-Update-Signature", sig); } + + /// + /// Computes an HMAC-SHA256 hash of the input data using the specified key. + /// + /// The input data string to hash. + /// The HMAC secret key. + /// The HMAC-SHA256 hash as a lowercase hexadecimal string. private static string HmacSha256(string data, string key) { var h = new HMACSHA256(Encoding.UTF8.GetBytes(key)) @@ -69,8 +213,35 @@ private static string HmacSha256(string data, string key) } } +/// +/// Factory class for creating instances based on a scheme string and token. +/// +/// +/// +/// This factory encapsulates the logic for selecting the appropriate authentication provider +/// based on the authentication scheme name: +/// +/// If is provided, creates an . +/// If is "apikey", creates an . +/// If is provided (any other scheme), creates a . +/// Otherwise, returns a . +/// +/// +/// +/// The factory is used internally by when no global +/// authentication provider has been set via . +/// +/// public static class HttpAuthProviderFactory { + /// + /// Creates an based on the provided scheme, token, and secret key. + /// + /// The authentication scheme name (e.g., "bearer", "apikey", "hmac"). Case-insensitive. + /// The authentication token or API key value. + /// The HMAC secret key. If provided, HMAC authentication takes precedence over other schemes. + /// An instance matching the specified parameters, + /// or if no matching scheme is found. public static IHttpAuthProvider Create(string? scheme, string? token, string? secretKey) { if (!string.IsNullOrEmpty(secretKey)) return new HmacAuthProvider(secretKey); diff --git a/src/c#/GeneralUpdate.Core/Security/ISslValidationPolicy.cs b/src/c#/GeneralUpdate.Core/Security/ISslValidationPolicy.cs index 8b5afa6b..40dc0de6 100644 --- a/src/c#/GeneralUpdate.Core/Security/ISslValidationPolicy.cs +++ b/src/c#/GeneralUpdate.Core/Security/ISslValidationPolicy.cs @@ -3,16 +3,71 @@ namespace GeneralUpdate.Core.Security; +/// +/// Defines a strategy for validating SSL/TLS server certificates during HTTPS communication. +/// +/// +/// +/// Implementations of this interface encapsulate the logic for determining whether a +/// server certificate is trusted. The validation result controls whether the HTTPS +/// connection proceeds or is rejected. +/// +/// +/// The default implementation is , which rejects +/// any certificate with SSL policy errors. Custom implementations can be registered globally +/// via +/// to relax or extend validation rules (e.g., for self-signed certificates in development +/// environments). +/// +/// public interface ISslValidationPolicy { + /// + /// Validates the server's SSL/TLS certificate. + /// + /// The server's X509 certificate, or null if not provided. + /// The certificate chain, or null if not available. + /// The SSL policy errors detected during the validation handshake. + /// true if the certificate is considered valid; otherwise false. + /// + /// Implementations should examine the value along with + /// the certificate and chain to make a trust decision. Returning false will cause + /// the HTTPS request to be aborted with an authentication error. + /// bool ValidateCertificate( X509Certificate2? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors); } +/// +/// A strict SSL validation policy that only accepts certificates with no SSL policy errors. +/// +/// +/// +/// This is the default validation policy used by . +/// It rejects any certificate that has detected policy errors, such as: +/// +/// Name mismatch between the certificate and the hostname. +/// Certificate signed by an untrusted root authority. +/// Expired or not-yet-valid certificate. +/// +/// +/// +/// For development or testing scenarios with self-signed certificates, replace this policy +/// with a custom implementation by calling +/// . +/// +/// public sealed class StrictSslValidationPolicy : ISslValidationPolicy { + /// + /// Validates the certificate by checking whether SSL policy errors are present. + /// + /// The server's X509 certificate. Ignored by this implementation. + /// The certificate chain. Ignored by this implementation. + /// The SSL policy errors detected during validation. + /// true only when is . public bool ValidateCertificate( X509Certificate2? certificate, X509Chain? chain, diff --git a/src/c#/GeneralUpdate.Core/Silent/SilentPollOrchestrator.cs b/src/c#/GeneralUpdate.Core/Silent/SilentPollOrchestrator.cs index 40f14bba..20b3f3a3 100644 --- a/src/c#/GeneralUpdate.Core/Silent/SilentPollOrchestrator.cs +++ b/src/c#/GeneralUpdate.Core/Silent/SilentPollOrchestrator.cs @@ -21,26 +21,33 @@ namespace GeneralUpdate.Core.Silent; /// -/// 静默更新轮询协调器 —— 定期检查更新,在后台静默下载更新包, -/// 并在进程退出时延迟执行应用程序更新。 +/// Silent update polling orchestrator -- periodically checks for updates, +/// silently downloads update packages in the background, and defers application +/// update execution until the process exits. /// /// /// -/// 遵循与 相同的 AppType 分流模式: +/// Follows the same AppType dispatch pattern as : /// /// -/// Upgrade(AppType=2):更新包在轮询周期内就地应用, -/// 它们作用于 UpdatePath 而非正在运行的应用的 InstallPath -/// Client(AppType=1):更新包被延迟 —— 存储在 ProcessInfo 中, -/// 在进程退出时通过 IPC 交给 Upgrade 进程执行实际更新。 +/// Upgrade (AppType=2): Update packages are applied in-place +/// during the polling cycle. They operate on UpdatePath rather than +/// the running application's InstallPath. +/// Client (AppType=1): Update packages are deferred -- stored in +/// ProcessInfo and handed off to the Upgrade process via IPC when the +/// current process exits. /// /// -/// 核心工作流程: +/// Core workflow: /// -/// 启动后台轮询循环,按 间隔检查服务器是否有新版本。 -/// 发现新版本后,在后台静默下载所有更新包。 -/// Upgrade 包立即应用,Client 包暂存到 ProcessInfo 中。 -/// 监听 事件,在进程退出时启动升级程序。 +/// Starts a background polling loop that checks the server for new versions +/// at intervals specified by . +/// When a new version is found, silently downloads all update packages +/// in the background. +/// Upgrade packages are applied immediately; Client packages are staged +/// into ProcessInfo. +/// Listens to the event and launches the +/// upgrade program when the process exits. /// /// /// @@ -59,11 +66,14 @@ public class SilentPollOrchestrator : IDisposable private List _clientVersions = new(); /// - /// 初始化 的新实例。 + /// Initializes a new instance of the class. /// - /// 全局配置信息,包含更新 URL、版本号、产品信息等。 - /// 静默更新选项,包含轮询间隔等配置。 - /// null 时抛出。 + /// The global configuration information, including update URL, + /// version numbers, product information, etc. + /// The silent update options, including polling interval and + /// client launch behavior. + /// Thrown when + /// or is null. public SilentPollOrchestrator(GlobalConfigInfo configInfo, SilentOptions options) { _configInfo = configInfo ?? throw new ArgumentNullException(nameof(configInfo)); @@ -71,38 +81,42 @@ public SilentPollOrchestrator(GlobalConfigInfo configInfo, SilentOptions options } /// - /// 设置更新生命周期钩子(链式调用方法)。 + /// Sets the update lifecycle hooks (fluent method). /// - /// 实现 接口的钩子实例,用于在更新各阶段插入自定义逻辑。 - /// 当前 实例,支持链式调用。 + /// An implementation for injecting + /// custom logic at various update stages. + /// The current instance, enabling fluent chaining. public SilentPollOrchestrator WithHooks(IUpdateHooks? hooks) { _hooks = hooks; return this; } /// - /// 设置更新进度报告器(链式调用方法)。 + /// Sets the update progress reporter (fluent method). /// - /// 实现 接口的报告器实例。 - /// 当前 实例,支持链式调用。 + /// An implementation for reporting + /// update status. + /// The current instance, enabling fluent chaining. public SilentPollOrchestrator WithReporter(IUpdateReporter? reporter) { _reporter = reporter; return this; } /// - /// 设置自定义操作系统策略(链式调用方法),用于覆盖默认的平台特定更新策略(Windows/Linux/macOS)。 + /// Sets a custom operating system strategy (fluent method), overriding the default + /// platform-specific update strategy (Windows/Linux/macOS). /// - /// 自定义的操作系统策略实例。 - /// 当前 实例,支持链式调用。 + /// A custom implementation. + /// The current instance, enabling fluent chaining. public SilentPollOrchestrator WithOsStrategy(IStrategy? strategy) { _customOsStrategy = strategy; return this; } /// - /// 启动静默轮询协调器,开始后台检查更新。 + /// Starts the silent polling orchestrator, beginning background update checks. /// - /// 表示启动操作的任务。注意:此任务在轮询循环启动后立即完成,不代表轮询循环已结束。 + /// A task representing the start operation. Note: this task completes as soon as + /// the polling loop is launched, not when the polling loop ends. /// /// - /// 启动流程: + /// Start flow: /// - /// 注册 事件处理器。 - /// 创建 - /// 在后台任务中启动轮询循环 - /// 为轮询任务附加异常处理器以记录未处理的异常。 + /// Registers the event handler. + /// Creates a . + /// Launches the polling loop in a background task. + /// Attaches an exception handler to the polling task to log unhandled exceptions. /// /// /// @@ -124,15 +138,16 @@ public Task StartAsync() } /// - /// 停止静默轮询协调器,取消正在进行的轮询和下载操作。 + /// Stops the silent polling orchestrator, cancelling in-progress polling and download operations. /// /// - /// 调用此方法会: + /// Calling this method will: /// - /// 取消后台轮询循环。 - /// 注销事件处理器。 + /// Cancel the background polling loop. + /// Unregister the event handler. /// - /// 注意:此方法不会取消已经在进行的下载任务,但会阻止新的轮询周期开始。 + /// Note: This method does not cancel downloads that are already in progress, + /// but it prevents new polling cycles from starting. /// public void Stop() { @@ -141,18 +156,23 @@ public void Stop() } /// - /// 后台轮询循环,按照配置的间隔定期检查更新并执行更新准备。 + /// Background polling loop that periodically checks for updates and performs + /// update preparation at the configured interval. /// - /// 取消令牌,用于停止轮询循环。 - /// 表示轮询循环的任务。当准备完成或取消时结束。 + /// A cancellation token for stopping the polling loop. + /// A task representing the polling loop. Ends when preparation is complete + /// or cancellation is requested. /// - /// 循环逻辑: + /// + /// Loop logic: /// - /// 调用 检查并准备更新。 - /// 如果准备完成(_prepared == 1),退出循环。 - /// 否则等待 后再次检查。 + /// Calls to check for and prepare updates. + /// If preparation is complete (_prepared == 1), exits the loop. + /// Otherwise, waits for before checking again. /// - /// 循环中的单个异常不会终止整个循环,而是记录错误后继续下一次轮询。 + /// Individual exceptions within the loop do not terminate the entire loop; + /// errors are logged and polling continues on the next cycle. + /// /// private async Task PollLoopAsync(CancellationToken token) { @@ -176,23 +196,26 @@ private async Task PollLoopAsync(CancellationToken token) } /// - /// 检查是否有可用更新,若有则执行完整的更新准备工作(下载、备份、应用 Upgrade 包、准备 ProcessInfo)。 + /// Checks for available updates and, if found, performs the full update preparation + /// workflow including download, backup, Upgrade package application, and ProcessInfo staging. /// - /// 取消令牌。 - /// 表示异步操作的任务。 + /// A cancellation token. + /// A task representing the asynchronous operation. /// /// - /// 这是静默更新的核心方法,执行完整的工作流: + /// This is the core method of the silent update flow, executing the complete workflow: /// /// - /// 通过 向服务器查询可用更新。 - /// 使用 构建下载计划。 - /// 检查版本失败记录,跳过已知失败的版本。 - /// 调用钩子(hooks)的 OnBeforeUpdateAsync 允许外部取消更新。 - /// 初始化黑名单,创建临时目录,执行备份。 - /// 使用 下载所有更新包。 - /// 按 AppType 分流:Upgrade 包立即通过策略执行,Client 包暂存到 ProcessInfo。 - /// 调用钩子的 OnAfterUpdateAsync 通知更新完成。 + /// Queries the server for available updates via . + /// Builds a download plan using . + /// Checks version failure records to skip known-failed versions. + /// Calls hooks' OnBeforeUpdateAsync to allow external cancellation of the update. + /// Initializes the blacklist, creates a temp directory, and performs backup. + /// Downloads all update packages using + /// . + /// Dispatches by AppType: Upgrade packages are executed immediately via the + /// platform strategy; Client packages are staged in ProcessInfo. + /// Calls hooks' OnAfterUpdateAsync to notify that the update is complete. /// /// private async Task PrepareUpdateIfNeededAsync(CancellationToken token) @@ -307,7 +330,7 @@ private async Task PrepareUpdateIfNeededAsync(CancellationToken token) return; } - // Split packages by AppType — mirrors ClientUpdateStrategy + // Split packages by AppType -- mirrors ClientUpdateStrategy var downloadVersions = plan.Assets.Select(a => new VersionInfo { Name = a.Name, @@ -322,7 +345,7 @@ private async Task PrepareUpdateIfNeededAsync(CancellationToken token) _clientVersions = downloadVersions.Where(v => v.AppType == (int)AppType.Client).ToList(); GeneralTracer.Info($"SilentPollOrchestrator: Upgrade packages={upgradeVersions.Count}, Client packages={_clientVersions.Count}"); - // Apply Upgrade packages in place — safe because they target UpdatePath + // Apply Upgrade packages in place -- safe because they target UpdatePath if (upgradeVersions.Count > 0) { GeneralTracer.Info("SilentPollOrchestrator: applying Upgrade packages."); @@ -376,22 +399,26 @@ private async Task PrepareUpdateIfNeededAsync(CancellationToken token) } /// - /// 进程退出事件处理器 —— 当进程即将退出时,将准备好的 Client 包信息通过 IPC 发送给升级程序并启动升级。 + /// Process exit event handler -- when the process is about to exit, sends the prepared + /// Client package information to the upgrade program via IPC and launches the upgrade. /// - /// 事件发送者。 - /// 事件参数。 + /// The event sender. + /// The event arguments. /// /// - /// 此方法在 事件触发时被调用。 - /// 仅在更新已准备好(_prepared == 1)且升级程序尚未启动时执行。 + /// This method is invoked when the + /// event fires. It only executes when an update has been prepared (_prepared == 1) + /// and the upgrade program has not yet been started. /// /// - /// 执行流程: + /// Execution flow: /// - /// 通过 Interlocked.Exchange 确保升级程序只启动一次。 - /// 解析升级程序的路径(优先使用 UpdatePath,回退到 InstallPath)。 - /// 如果存在 Client 包,通过 发送加密的 ProcessInfo。 - /// 启动升级进程(使用 ShellExecute 以提升权限)。 + /// Uses Interlocked.Exchange to ensure the upgrade program is only + /// started once. + /// Resolves the upgrade program path (prefers UpdatePath, falls back to InstallPath). + /// If Client packages exist, sends the encrypted ProcessInfo via + /// . + /// Starts the upgrade process (uses ShellExecute for privilege elevation). /// /// /// @@ -401,7 +428,7 @@ private void OnProcessExit(object? sender, EventArgs e) try { - // Resolve updater location — prefers UpdatePath, falls back to InstallPath + // Resolve updater location -- prefers UpdatePath, falls back to InstallPath var updaterDir = !string.IsNullOrWhiteSpace(_configInfo.UpdatePath) ? (Path.IsPathRooted(_configInfo.UpdatePath) ? _configInfo.UpdatePath @@ -432,13 +459,15 @@ private void OnProcessExit(object? sender, EventArgs e) } /// - /// 尝试通过钩子和报告器报告更新错误。 + /// Attempts to report an update error through hooks and the reporter. /// - /// 更新上下文,包含应用信息。 - /// 发生的异常。 + /// The update context containing application information. + /// The exception that occurred. /// - /// 此方法会尽力调用 _hooks.OnUpdateErrorAsync_reporter.ReportAsync, - /// 即使这些调用本身抛出异常也不会传播(仅记录警告日志),确保不会因错误报告失败而掩盖原始错误。 + /// This method makes a best-effort attempt to call _hooks.OnUpdateErrorAsync and + /// _reporter.ReportAsync. Even if these calls themselves throw exceptions, + /// the exceptions are not propagated (only a warning is logged), ensuring that a + /// failure in error reporting does not mask the original error. /// private void TryReportError(UpdateContext ctx, Exception ex) { @@ -455,8 +484,9 @@ private void TryReportError(UpdateContext ctx, Exception ex) } /// - /// 初始化 ,使用配置中的黑名单列表。 - /// 如果配置中的列表为空,则使用 中的默认值。 + /// Initializes the using the configured + /// blacklist information. Falls back to if the + /// configured lists are empty. /// private void InitBlackList() { @@ -469,11 +499,12 @@ private void InitBlackList() } /// - /// 根据当前操作系统创建对应的更新策略实例。 - /// 如果设置了自定义策略(_customOsStrategy),则优先使用。 + /// Creates the appropriate update strategy instance based on the current operating system. + /// Uses the custom strategy (_customOsStrategy) if one has been set. /// - /// 与当前平台匹配的 实现。 - /// 当操作系统不是 Windows、Linux 或 macOS 时抛出。 + /// An implementation matching the current platform. + /// Thrown when the operating system is + /// not Windows, Linux, or macOS. private IStrategy CreateStrategy() { if (_customOsStrategy != null) return _customOsStrategy; @@ -484,9 +515,9 @@ private IStrategy CreateStrategy() } /// - /// 检测当前运行的操作系统平台。 + /// Detects the current operating system platform. /// - /// 枚举值,表示当前操作系统。 + /// A enum value representing the current operating system. private static PlatformType GetPlatform() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return PlatformType.Windows; @@ -496,13 +527,16 @@ private static PlatformType GetPlatform() } /// - /// 检查指定版本是否为已知的失败版本(已记录在环境变量 UpgradeFail 中)。 + /// Checks whether the specified version is a known failed version (recorded in the + /// UpgradeFail environment variable). /// - /// 要检查的版本号字符串。 - /// 如果版本号不为空且小于等于环境变量中记录的失败版本号则返回 true + /// The version string to check. + /// true if the version is not null and is less than or equal to the + /// failed version recorded in the environment variable. /// - /// 此功能用于避免反复尝试已知失败的版本更新。 - /// 通过环境变量 UpgradeFail 记录曾经失败的版本号,如果待更新的版本号小于等于该值则跳过。 + /// This feature prevents repeatedly attempting known-failed version updates. + /// The UpgradeFail environment variable stores a previously failed version number; + /// if the candidate version is less than or equal to that value, it is skipped. /// private static bool CheckFail(string? version) { @@ -513,10 +547,11 @@ private static bool CheckFail(string? version) } /// - /// 释放由 占用的资源。 + /// Releases all resources used by the . /// /// - /// 调用 停止轮询,然后释放 。 + /// Calls to halt polling, then disposes the + /// . /// public void Dispose() { @@ -526,25 +561,29 @@ public void Dispose() } /// -/// 静默更新的配置选项。 +/// Configuration options for silent updates. /// /// -/// 通过 可以控制静默更新的轮询行为和客户端重启策略。 +/// controls the polling behavior and client restart policy +/// for silent updates. /// public sealed class SilentOptions { /// - /// 获取或设置轮询间隔时间。默认为 1 小时。 + /// Gets or sets the polling interval. The default is 1 hour. /// - /// 两次更新检查之间的时间间隔。最小建议值不应低于 5 分钟以避免频繁的网络请求。 + /// The time interval between update checks. The recommended minimum is + /// 5 minutes to avoid excessive network requests. public TimeSpan PollInterval { get; set; } = TimeSpan.FromHours(1); /// - /// 获取或设置一个值,指示更新完成后是否自动启动客户端应用程序。 + /// Gets or sets a value indicating whether the client application should be + /// automatically launched after an update completes. /// /// - /// true(默认值):升级完成后自动启动客户端; - /// false:由调用方手动控制重启时机(适用于维护窗口、编排式发布等场景)。 + /// true (default): Automatically start the client after the upgrade completes; + /// false: The caller controls restart timing manually (suitable for maintenance + /// windows, orchestrated deployments, etc.). /// public bool LaunchClientAfterUpdate { get; set; } = true; } diff --git a/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs index 01706d65..23a996f7 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs @@ -16,30 +16,31 @@ namespace GeneralUpdate.Core.Strategy { /// - /// 抽象基类,定义平台特定的更新策略。提供管道执行循环、上下文构建和错误处理等通用逻辑。 + /// Abstract base class that defines platform-specific update strategies. + /// Provides common logic for pipeline execution loops, context construction, and error handling. /// /// /// - /// 本类是模板方法模式的典型应用,子类(、 - /// )通过重写 方法提供各自平台的中间件链。 + /// This class is a typical application of the Template Method pattern. Subclasses (, , + /// ) override to provide their own platform-specific middleware chains. /// /// - /// 管道执行循环(): + /// Pipeline Execution Loop (): /// - /// 遍历 _configinfo.UpdateVersions 集合,逐一处理每个更新版本。 - /// 调用 构建管道上下文,包含压缩包路径、 - /// 哈希值、格式编码、源路径和补丁配置等关键参数。 - /// 调用 (抽象方法,由子类实现)获取中间件构建器。 - /// 执行 PipelineBuilder.Build(),以先进先出(FIFO)顺序执行注册的中间件: - /// Hash(完整性校验)→ Decompress(解压更新包)→ Patch(应用增量补丁)。 - /// 通过 向服务器报告当前版本的更新结果。 - /// 删除已处理的压缩包文件。 + /// Iterates through the _configinfo.UpdateVersions collection, processing each update version one by one. + /// Calls to build the pipeline context, which contains key parameters such as + /// the archive path, hash value, format encoding, source path, and patch configuration. + /// Calls (abstract method, implemented by subclasses) to obtain the middleware builder. + /// Executes PipelineBuilder.Build() to run the registered middleware in FIFO order: + /// Hash (integrity verification) → Decompress (extract update package) → Patch (apply incremental patches). + /// Reports the update result for the current version to the server via . + /// Deletes the processed archive file. /// /// /// - /// 错误处理:每个版本的更新失败时,调用 记录异常并分发事件, - /// 然后调用 TryRollback 尝试从备份目录恢复。错误不会中断后续版本的处理。 - /// 所有版本处理完毕后清理临时目录并调用 。 + /// Error Handling: When an individual version update fails, records the exception and dispatches events, + /// then TryRollback attempts to restore from the backup directory. Errors do not interrupt processing of subsequent versions. + /// After all versions have been processed, the temporary directory is cleaned up and is called. /// /// public abstract class AbstractStrategy : IStrategy @@ -47,87 +48,87 @@ public abstract class AbstractStrategy : IStrategy private const string Patchs = "patchs"; /// - /// 全局配置信息,包含更新包路径、临时目录、报告地址、版本列表等参数。 - /// 由 方法初始化,供管道执行循环使用。 + /// Global configuration information containing parameters such as update package path, temporary directory, report URL, and version list. + /// Initialized by the method and used by the pipeline execution loop. /// protected GlobalConfigInfo _configinfo = new(); /// - /// 获取或设置生命周期钩子。由引导程序注入,用于在更新前后执行自定义逻辑。 + /// Gets or sets the lifecycle hooks. Injected by the bootstrap to execute custom logic before and after updates. /// public IUpdateHooks Hooks { get; set; } = new Hooks.NoOpUpdateHooks(); /// - /// 获取或设置更新状态报告器。负责向服务器报告每个版本的处理进度和最终结果。 + /// Gets or sets the update status reporter. Responsible for reporting the processing progress and final result of each version to the server. /// public IUpdateReporter Reporter { get; set; } = new Download.Reporting.NoOpUpdateReporter(); /// - /// 获取或设置差异补丁管道。支持并行应用增量补丁并报告进度。 + /// Gets or sets the differential patch pipeline. Supports parallel application of incremental patches and progress reporting. /// public DiffPipeline? DiffPipeline { get; set; } /// - /// 获取或设置要启动的应用程序名称。由上层策略(如 ) - /// 在调用 前设置。 + /// Gets or sets the name of the application to launch. Set by the upper-level strategy (such as ) + /// before calling . /// public string? LaunchAppName { get; set; } /// - /// 获取或设置是否同时启动 Bowl 辅助进程。仅 Windows 平台有效,由上层策略在调用 - /// 前设置。 + /// Gets or sets whether to also launch the Bowl helper process. Only valid on the Windows platform. + /// Set by the upper-level strategy before calling . /// public bool LaunchBowl { get; set; } /// - /// 获取或设置是否优先使用更新路径。当为 true 时, - /// 会优先从 解析应用程序, - /// 失败后再回退到 。 - /// 由 在启动升级进程时设置。 + /// Gets or sets whether to prefer using the update path. When true, + /// will first attempt to resolve the application from , + /// and fall back to on failure. + /// Set by when launching the upgrade process. /// public bool UseUpdatePath { get; set; } /// - /// 启动主应用程序。虚方法,由子类提供平台特定的应用启动实现。 + /// Starts the main application. Virtual method, overridden by subclasses to provide platform-specific application launch implementations. /// /// - /// 默认实现抛出 。子类应重写此方法以执行 - /// 平台相关的进程启动逻辑(如设置工作目录、环境变量等)。 + /// The default implementation throws . Subclasses should override this method to execute + /// platform-specific process launch logic (such as setting the working directory, environment variables, etc.). /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. public virtual Task StartAppAsync() => throw new NotImplementedException(); /// - /// 执行更新管道的核心循环。遍历所有待更新版本,依次构建管道上下文、执行中间件链、报告状态并清理资源。 + /// Executes the core pipeline loop for updates. Iterates through all pending update versions, sequentially building pipeline contexts, + /// executing the middleware chain, reporting status, and cleaning up resources. /// /// /// - /// 管道执行循环详解: + /// Pipeline Execution Loop Details: /// - /// 遍历版本:_configinfo.UpdateVersions 中逐个取出 - /// 对象。 - /// 构建上下文:调用 创建 - /// ,包含压缩包路径(由 TempPath 和版本名称拼接)、 - /// 哈希值、压缩格式、源路径和补丁配置等关键参数。 - /// 构建管道:调用 (抽象方法,由子类实现 - /// 平台特定逻辑),注册 Hash(完整性校验)、Decompress(解压缩)、 - /// Patch(增量补丁)等中间件。 - /// 执行管道:调用 PipelineBuilder.Build(),按照注册顺序以 - /// 先进先出方式依次执行所有中间件。 - /// 报告状态:通过 向服务器报告 - /// 当前版本的更新结果(成功或失败)。 - /// 清理资源:调用 DeleteVersionZip 删除当前版本已处理的压缩包文件。 + /// Iterate Versions: Retrieves objects one by one from _configinfo.UpdateVersions. + /// Build Context: Calls to create a + /// containing key parameters such as the archive path (composed of TempPath and the version name), + /// hash value, compression format, source path, and patch configuration. + /// Build Pipeline: Calls (abstract method implemented by subclasses + /// with platform-specific logic) to register middleware such as Hash (integrity verification), Decompress (extraction), + /// and Patch (incremental patches). + /// Execute Pipeline: Calls PipelineBuilder.Build() to execute all registered middleware + /// in FIFO order. + /// Report Status: Reports the current version's update result (success or failure) to the server + /// via . + /// Clean Up Resources: Calls DeleteVersionZip to remove the processed archive file for the current version. /// /// /// - /// 错误处理策略:单个版本更新失败时,捕获异常后依次调用 - /// 记录错误、TryRollback 尝试从备份恢复, - /// 然后继续处理下一个版本。所有版本处理完毕后,无论是否有版本失败,都会执行清理操作 - /// 并调用 。 + /// Error Handling Strategy: When an individual version update fails, the exception is caught and + /// logs the error, TryRollback attempts to restore from the backup, + /// and processing continues with the next version. After all versions have been processed, regardless of whether any versions failed, + /// cleanup operations are performed and is called. /// /// - /// 资源清理:循环结束后删除补丁临时目录,并尝试清理 TempPath - /// (仅当目录为空时删除,避免误删其他 AppType 的包文件)。 + /// Resource Cleanup: After the loop ends, the patch temporary directory is deleted, and TempPath is cleaned up + /// (only deleted when empty to avoid accidentally removing package files for other AppType values). /// /// public virtual async Task ExecuteAsync() @@ -177,37 +178,38 @@ await VersionService.Report(_configinfo.ReportUrl } /// - /// 初始化策略实例。接收全局配置信息并存储以供后续使用。 + /// Initializes the strategy instance. Receives global configuration information and stores it for subsequent use. /// - /// 全局配置信息,包含更新包路径、临时目录、报告地址、版本列表等参数。 + /// Global configuration information containing parameters such as update package path, temporary directory, report URL, and version list. public virtual void Create(GlobalConfigInfo parameter) => _configinfo = parameter; /// - /// 创建管道上下文,填充公共参数和平台特定参数。子类可重写此方法以添加平台特定的上下文参数。 + /// Creates the pipeline context, populating common parameters and platform-specific parameters. + /// Subclasses can override this method to add platform-specific context parameters. /// /// /// - /// 管道上下文()包含以下键值对: + /// The pipeline context () contains the following key-value pairs: /// - /// 说明 - /// ZipFilePath压缩包完整路径,由 TempPath - /// 和版本名称拼接而成,用于 Decompress 中间件定位更新包。 - /// Hash更新包的哈希值,用于 Hash 中间件 - /// 校验文件完整性,防止数据损坏或篡改。 - /// Format压缩格式(如 ZIP、GZip),用于 - /// Decompress 中间件选择合适的解压算法。 - /// Encoding文件编码格式,用于解压时正确处理文件名编码。 - /// SourcePath目标安装路径,由 - /// 根据版本的应用类型和配置决定。 - /// PatchPath补丁文件的临时存储路径,用于 Patch 中间件。 - /// PatchEnabled是否启用增量补丁功能,由 _configinfo.PatchEnabled 控制。 - /// DiffPipeline差异补丁管道实例,用于并行应用补丁并报告进度。 + /// KeyDescription + /// ZipFilePathFull path to the archive, composed of TempPath + /// and the version name, used by the Decompress middleware to locate the update package. + /// HashHash value of the update package, used by the Hash middleware + /// to verify file integrity and prevent data corruption or tampering. + /// FormatCompression format (e.g., ZIP, GZip), used by the + /// Decompress middleware to select the appropriate decompression algorithm. + /// EncodingFile encoding format, used to correctly handle file name encoding during decompression. + /// SourcePathTarget installation path, determined by + /// based on the version's application type and configuration. + /// PatchPathTemporary storage path for patch files, used by the Patch middleware. + /// PatchEnabledWhether incremental patching is enabled, controlled by _configinfo.PatchEnabled. + /// DiffPipelineDifferential patch pipeline instance, used for parallel patch application and progress reporting. /// /// /// - /// 当前待处理的版本信息,包含名称、哈希值、应用类型等。 - /// 补丁文件的临时存储目录路径。 - /// 填充完毕的管道上下文实例。 + /// The current version information to be processed, containing name, hash, application type, etc. + /// The temporary storage directory path for patch files. + /// The populated pipeline context instance. protected virtual PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) { var context = new PipelineContext(); @@ -231,54 +233,54 @@ protected virtual PipelineContext CreatePipelineContext(VersionInfo version, str } /// - /// 构建更新管道中间件链。抽象方法,由各平台子类提供具体的中间件注册逻辑。 + /// Builds the update pipeline middleware chain. Abstract method; each platform subclass provides its specific middleware registration logic. /// /// /// - /// 子类应在此方法中创建 实例,并依次注册以下中间件 - /// (顺序不可颠倒): + /// Subclasses should create a instance in this method and register the following middleware + /// in order (the sequence must not be changed): /// - /// Hash 中间件:校验更新包的文件完整性,防止数据损坏或篡改。 - /// Decompress 中间件:解压更新包到目标安装目录。 - /// Patch 中间件:应用增量补丁,仅更新变更的文件以节省带宽和磁盘空间。 + /// Hash Middleware: Verifies the file integrity of the update package to prevent data corruption or tampering. + /// Decompress Middleware: Extracts the update package to the target installation directory. + /// Patch Middleware: Applies incremental patches, updating only changed files to save bandwidth and disk space. /// - /// 各平台可根据自身特性添加额外的中间件(如权限设置、符号链接处理等)。 + /// Each platform can add additional middleware as needed (such as permission settings, symbolic link handling, etc.). /// /// - /// 管道上下文,包含中间件执行所需的所有参数。 - /// 配置完中间件的管道构建器实例。 + /// The pipeline context containing all parameters required for middleware execution. + /// The pipeline builder instance with middleware configured. protected abstract PipelineBuilder BuildPipeline(PipelineContext context); /// - /// 在 成功完成后调用。子类可重写此方法以添加平台特定的执行后逻辑。 + /// Called after completes successfully. Subclasses can override this method to add platform-specific post-execution logic. /// /// - /// 此方法在所有版本处理完毕、临时目录清理完成后调用。可用于执行平台特定的收尾工作, - /// 如清理临时文件、记录完成日志等。 + /// This method is called after all versions have been processed and the temporary directories have been cleaned up. + /// It can be used to execute platform-specific finishing work, such as cleaning temporary files or logging completion. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. protected virtual Task OnExecuteCompleteAsync() { return Task.CompletedTask; } /// - /// 处理管道执行过程中发生的异常。记录错误日志并通过事件系统分发异常信息。 + /// Handles exceptions that occur during pipeline execution. Logs the error and dispatches exception information through the event system. /// /// /// - /// 此方法在 捕获到异常时调用,执行以下操作: + /// This method is called when catches an exception. It performs the following operations: /// - /// 通过 记录异常堆栈和消息。 - /// 通过 分发 , - /// 供上层监听者处理。 + /// Logs the exception stack trace and message via . + /// Dispatches via + /// for upper-layer listeners to handle. /// /// /// - /// 子类可重写此方法以添加自定义错误处理逻辑,如发送告警通知、记录审计日志等。 + /// Subclasses can override this method to add custom error handling logic, such as sending alert notifications or recording audit logs. /// /// - /// 管道执行过程中捕获的异常。 + /// The exception caught during pipeline execution. protected virtual void HandleExecuteException(Exception e) { GeneralTracer.Error($"Strategy execution exception.", e); @@ -286,7 +288,7 @@ protected virtual void HandleExecuteException(Exception e) } /// - /// 检查指定路径下是否存在目标文件。 + /// Checks whether the target file exists under the specified path. /// protected static string CheckPath(string path, string name) { @@ -297,12 +299,12 @@ protected static string CheckPath(string path, string name) } /// - /// 解析可执行文件的完整路径。可选地优先检查 , - /// 失败后再回退到 InstallPath。 + /// Resolves the full path of the executable. Optionally checks first, + /// then falls back to InstallPath on failure. /// - /// 可执行文件的名称。 - /// 当为 true 时,优先检查 UpdatePath。 - /// 找到则返回完整路径,否则返回空字符串。 + /// The name of the executable file. + /// When true, checks UpdatePath first. + /// The full path if found; otherwise, an empty string. protected string ResolveAppPath(string name, bool preferUpdatePath = false) { if (preferUpdatePath && !string.IsNullOrWhiteSpace(_configinfo.UpdatePath)) @@ -317,24 +319,25 @@ protected string ResolveAppPath(string name, bool preferUpdatePath = false) } /// - /// 解析更新包的目标应用路径。根据版本的应用类型决定使用更新路径还是安装路径。 + /// Resolves the target application path for the update package. Determines whether to use the update path or install path + /// based on the version's application type. /// /// /// - /// 路径解析逻辑: + /// Path Resolution Logic: /// - /// 如果当前版本的应用类型为升级端(AppType == 2)且 - /// UpdatePath 已配置,则优先使用 UpdatePath 作为目标路径。 - /// 否则使用 InstallPath 作为目标路径。 + /// If the current version has application type Upgrade (AppType == 2) and + /// UpdatePath is configured, UpdatePath is used as the target path first. + /// Otherwise, InstallPath is used as the target path. /// /// /// - /// UpdatePath 可以是绝对路径或相对路径。相对路径会与 InstallPath 拼接为完整路径。 - /// 此设计允许将升级端的更新包应用到与客户端不同的目标目录。 + /// UpdatePath can be an absolute or relative path. Relative paths are combined with InstallPath to form the full path. + /// This design allows upgrade-end update packages to be applied to a different target directory than the client. /// /// - /// 当前待处理的版本信息,用于判断应用类型。 - /// 目标安装目录的完整路径。 + /// The current version information to be processed, used to determine the application type. + /// The full path of the target installation directory. protected string ResolveTargetPath(VersionInfo version) { if (version.AppType == 2 && !string.IsNullOrWhiteSpace(_configinfo.UpdatePath)) diff --git a/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs index 2015ae86..9dd63923 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs @@ -22,47 +22,48 @@ namespace GeneralUpdate.Core.Strategy; /// -/// 客户端更新策略。负责与服务器校验版本、下载更新包、构造升级端所需的进程信息并启动升级进程。 +/// Client update strategy. Responsible for version verification with the server, downloading update packages, +/// constructing process information required by the upgrade process, and launching the upgrade process. /// /// -/// 本策略是 AppType.Client 角色策略,完整更新流程如下: +/// This strategy serves the AppType.Client role. The complete update workflow is as follows: /// -/// 1. 版本校验:通过 向服务器发送版本信息, -/// 根据返回值设置 IsMainUpdateIsUpgradeUpdate 标志,确定更新场景。 +/// 1. Version Verification: Sends version information to the server via , +/// and sets the IsMainUpdate and IsUpgradeUpdate flags based on the response to determine the update scenario. /// /// -/// 2. 事件分发:构造 并通过 EventManager 分发到订阅者。 +/// 2. Event Dispatch: Constructs an and dispatches it to subscribers via EventManager. /// /// -/// 3. 跳过检查:如果非强制更新且预检查回调返回 true,则跳过本次更新。 +/// 3. Skip Check: If the update is not forced and the pre-check callback returns true, the update is skipped. /// /// -/// 4. 更新前钩子:调用 Hooks.OnBeforeUpdateAsync,允许外部逻辑取消本次更新。 +/// 4. Pre-Update Hook: Calls Hooks.OnBeforeUpdateAsync, allowing external logic to cancel the update. /// /// -/// 5. 备份:将当前安装目录备份到临时目录(可通过 BackupEnabled 禁用)。 +/// 5. Backup: Backs up the current installation directory to a temporary directory (can be disabled via BackupEnabled). /// /// -/// 6. 下载:通过下载编排器下载所有更新包,支持自定义策略、执行器和处理管道。 +/// 6. Download: Downloads all update packages through the download orchestrator, supporting custom policies, executors, and processing pipelines. /// /// -/// 7. 场景分发:根据服务器校验结果执行不同流程: -/// - UpgradeOnly:仅原地应用升级程序更新包; -/// - MainOnly:序列化主程序更新包为 ProcessInfo 并通过 IPC 发送,启动升级进程; -/// - Both:先应用升级程序更新包,再发送主程序信息并启动升级进程。 +/// 7. Scenario Dispatch: Executes different workflows based on the server validation result: +/// - UpgradeOnly: Applies upgrade program update packages in place only; +/// - MainOnly: Serializes main program update packages as ProcessInfo and sends via IPC, then starts the upgrade process; +/// - Both: Applies upgrade program update packages first, then sends main program info and starts the upgrade process. /// /// -/// 8. 升级应用:通过 ApplyUpgradePackagesAsync 调用 OS 策略管道原地应用增量/全量更新包。 +/// 8. Upgrade Application: Calls ApplyUpgradePackagesAsync to apply incremental/full update packages in place through the OS strategy pipeline. /// /// -/// 9. IPC 通信:通过 SendProcessIpc 将主程序更新信息序列化为 JSON 并通过加密 IPC 发送给升级端。 +/// 9. IPC Communication: Serializes main program update information as JSON via SendProcessIpc and sends it to the upgrade process through encrypted IPC. /// /// -/// 10. 启动升级进程:通过 LaunchUpgradeProcessAsync 委托 OS 策略启动升级端可执行文件。 +/// 10. Launch Upgrade Process: Delegates to the OS strategy via LaunchUpgradeProcessAsync to start the upgrade executable. /// -/// 本类采用双层策略设计ClientUpdateStrategy 作为"角色"策略,负责编排更新流程; -/// 内部组合一个 OS 特定的平台策略() -/// 处理平台相关操作(文件操作、进程管理、安装路径判断等)。 +/// This class uses a two-layer strategy design: ClientUpdateStrategy acts as the "role" strategy responsible for orchestrating the update flow; +/// it internally composes an OS-specific platform strategy (, , ) +/// to handle platform-related operations (file operations, process management, installation path determination, etc.). /// /// public class ClientUpdateStrategy : IStrategy @@ -78,7 +79,7 @@ public class ClientUpdateStrategy : IStrategy private int _mainRecordId; /// - /// 由服务器验证结果确定的更新场景,表示需要更新的目标。 + /// Update scenario determined by the server validation result, indicating which update targets are needed. /// private enum UpdateScenario { @@ -89,122 +90,125 @@ private enum UpdateScenario } /// - /// 获取或设置更新生命周期钩子。由引导程序通过 .Hooks<T>() 注册注入。 + /// Gets or sets the update lifecycle hooks. Registered and injected by the bootstrap via .Hooks<T>(). /// - /// 实现了 IUpdateHooks 的钩子实例。默认为 NoOpUpdateHooks(无操作实现)。 + /// An IUpdateHooks hook instance. Defaults to NoOpUpdateHooks (no-operation implementation). /// - /// 钩子回调在更新流程的关键节点被安全调用(包裹在 try-catch 中),单个钩子失败不会阻断流程。 - /// 参见 等方法。 + /// Hook callbacks are safely invoked at key points in the update flow (wrapped in try-catch), so a single hook failure does not block the flow. + /// See , , and related methods. /// public Hooks.IUpdateHooks Hooks { get; set; } = new Hooks.NoOpUpdateHooks(); /// - /// 获取或设置更新状态报告器。由引导程序通过 .UpdateReporter<T>() 注册注入。 + /// Gets or sets the update status reporter. Registered and injected by the bootstrap via .UpdateReporter<T>(). /// - /// 实现了 IUpdateReporter 的报告器实例。默认为 NoOpUpdateReporter(无操作实现)。 + /// An IUpdateReporter reporter instance. Defaults to NoOpUpdateReporter (no-operation implementation). /// - /// 报告器在更新开始、下载完成、更新应用成功或失败时向服务器上报状态。 - /// 所有上报调用都包裹在 try-catch 中,上报失败不会阻断流程。 - /// 参见 等方法。 + /// The reporter reports status to the server when the update starts, download completes, or the update is applied or fails. + /// All reporting calls are wrapped in try-catch, so a reporting failure does not block the flow. + /// See , , and related methods. /// public Download.Reporting.IUpdateReporter Reporter { get; set; } = new Download.Reporting.NoOpUpdateReporter(); /// - /// 获取或设置下载数据源。由引导程序通过 .DownloadSource<T>() 注册注入, - /// 或在引导程序中通过 HubConfig 配置。 + /// Gets or sets the download data source. Registered and injected by the bootstrap via .DownloadSource<T>(), + /// or configured via HubConfig in the bootstrap. /// - /// 实现了 IDownloadSource 的数据源实例。为 null 时默认使用 HttpDownloadSource + /// An IDownloadSource data source instance. When null, HttpDownloadSource is used by default. /// - /// 在 中,如果此属性为 null, - /// 则会根据 GlobalConfigInfo 中的 UpdateUrl、版本号等信息自动创建 HttpDownloadSource 实例。 + /// In , if this property is null, + /// an HttpDownloadSource instance is automatically created based on the UpdateUrl, version number, + /// and other information in GlobalConfigInfo. /// public Download.Abstractions.IDownloadSource? DownloadSource { get; set; } /// - /// 初始化 的新实例。 + /// Initializes a new instance of the class. /// /// - /// 默认构造函数。所有属性使用默认值(无操作钩子、无操作报告器)。 - /// 下载编排器默认为 null,将在 中创建默认的 DefaultDownloadOrchestrator。 - /// 策略实例需要通过 方法传入 完成初始化。 + /// Default constructor. All properties use default values (no-op hooks, no-op reporter). + /// The download orchestrator defaults to null and will be set to a default DefaultDownloadOrchestrator + /// in . + /// The strategy instance must be initialized via with a . /// public ClientUpdateStrategy() { } /// - /// 使用自定义下载编排器初始化 的新实例。 + /// Initializes a new instance of the class with a custom download orchestrator. /// - /// 自定义下载编排器实例,用于接管批量下载流程。如果为 null,则使用默认编排器。 + /// Custom download orchestrator instance to take over the batch download flow. If null, the default orchestrator is used. /// - /// 通过此构造函数传入的编排器优先级高于通过 方法设置的值。 + /// The orchestrator passed via this constructor takes priority over the value set via . /// public ClientUpdateStrategy(Download.Abstractions.IDownloadOrchestrator? orchestrator) => _orchestrator = orchestrator; /// - /// 设置自定义 OS 级别策略。由引导程序通过 .Strategy<T>() 注册注入。 + /// Sets a custom OS-level strategy. Registered and injected by the bootstrap via .Strategy<T>(). /// - /// 自定义 OS 策略实例。设置后将替换 中的自动平台探测。 + /// Custom OS strategy instance. When set, replaces the automatic platform detection in . /// - /// 当设置此策略后, 将跳过 RuntimeInformation.IsOSPlatform 的检查, - /// 直接使用此处注入的策略。适用于需要完全自定义平台行为的场景。 + /// When this strategy is set, will skip the RuntimeInformation.IsOSPlatform check + /// and use this injected strategy directly. Suitable for scenarios requiring completely custom platform behavior. /// public void SetOsStrategy(IStrategy? strategy) => _customOsStrategy = strategy; /// - /// 设置自定义下载编排器。由引导程序通过 .DownloadOrchestrator<T>() 注册注入。 + /// Sets a custom download orchestrator. Registered and injected by the bootstrap via .DownloadOrchestrator<T>(). /// - /// 自定义下载编排器实例。为 null 时使用默认的 DefaultDownloadOrchestrator。 + /// Custom download orchestrator instance. When null, the default DefaultDownloadOrchestrator is used. /// - /// 设置后将在 的下载阶段使用此编排器。 - /// 如果已通过构造函数注入编排器,则构造函数中的值优先。 - /// 自定义编排器完全接管下载流程, 和 - /// 的设置将被忽略。 + /// When set, this orchestrator is used during the download phase of . + /// If an orchestrator was already injected via the constructor, the constructor value takes precedence. + /// A custom orchestrator fully takes over the download flow; settings from , , + /// and are ignored. /// public void SetOrchestrator(Download.Abstractions.IDownloadOrchestrator? orchestrator) => _orchestrator = orchestrator; /// - /// 设置自定义下载重试策略。由引导程序通过 .DownloadPolicy<T>() 注册注入。 + /// Sets a custom download retry policy. Registered and injected by the bootstrap via .DownloadPolicy<T>(). /// - /// 自定义下载策略实例。为 null 时使用默认重试行为。 + /// Custom download policy instance. When null, the default retry behavior is used. /// - /// 仅在使用默认下载编排器(DefaultDownloadOrchestrator)时生效。 - /// 如果同时设置了自定义编排器(),则此策略被忽略。 - /// 下载策略控制每次失败后的等待时间和最大重试次数。 + /// Only effective when using the default download orchestrator (DefaultDownloadOrchestrator). + /// If a custom orchestrator is set via , this policy is ignored. + /// The download policy controls the wait time between retries and the maximum number of retries. /// public void SetDownloadPolicy(Download.Abstractions.IDownloadPolicy? policy) => _customDownloadPolicy = policy; /// - /// 设置自定义单文件下载执行器。由引导程序通过 .DownloadExecutor<T>() 注册注入。 + /// Sets a custom single-file download executor. Registered and injected by the bootstrap via .DownloadExecutor<T>(). /// - /// 自定义下载执行器实例。为 null 时使用默认的 HTTP 下载执行器。 + /// Custom download executor instance. When null, the default HTTP download executor is used. /// - /// 仅在使用默认下载编排器(DefaultDownloadOrchestrator)时生效。 - /// 如果同时设置了自定义编排器(),则此执行器被忽略。 - /// 可用于实现 FTP、SFTP 或私有协议的文件下载。 + /// Only effective when using the default download orchestrator (DefaultDownloadOrchestrator). + /// If a custom orchestrator is set via , this executor is ignored. + /// Can be used to implement file download via FTP, SFTP, or custom protocols. /// public void SetDownloadExecutor(Download.Abstractions.IDownloadExecutor? executor) => _customDownloadExecutor = executor; /// - /// 设置自定义下载后处理管道工厂。由引导程序通过 .DownloadPipeline<T>() 注册注入。 + /// Sets a custom download post-processing pipeline factory. Registered and injected by the bootstrap via .DownloadPipeline<T>(). /// - /// 管道工厂委托,接收文件路径参数并返回 IDownloadPipeline 实例。为 null 时跳过管道处理。 + /// Pipeline factory delegate that receives a file path and returns an IDownloadPipeline instance. When null, pipeline processing is skipped. /// - /// 仅在使用默认下载编排器(DefaultDownloadOrchestrator)时生效。 - /// 如果同时设置了自定义编排器(),则此工厂被忽略。 - /// 管道工厂在每个文件下载完成后被调用,可用于执行哈希校验、解密、病毒扫描等后处理操作。 + /// Only effective when using the default download orchestrator (DefaultDownloadOrchestrator). + /// If a custom orchestrator is set via , this factory is ignored. + /// The pipeline factory is called after each file download completes and can be used for post-processing operations + /// such as hash verification, decryption, or virus scanning. /// public void SetDownloadPipelineFactory(Func? factory) => _customDownloadPipelineFactory = factory; /// - /// 使用全局配置信息初始化策略实例。解析 OS 特定策略并传递差异更新管道。 + /// Initializes the strategy instance with global configuration information. Resolves the OS-specific strategy and passes the differential update pipeline. /// - /// 全局配置信息,包含版本号、安装路径、更新 URL、应用密钥等。 - /// null 时抛出。 + /// Global configuration information containing version number, install path, update URL, app secret key, etc. + /// Thrown when is null. /// - /// 此方法在 执行前调用,完成以下初始化: - /// - 保存配置信息到 _configInfo 字段; - /// - 通过 解析当前操作系统对应的平台策略; - /// - 如果 OS 策略继承自 AbstractStrategy,传递待设置的差异更新管道(DiffPipeline)。 + /// This method is called before and completes the following initialization: + /// - Stores the configuration in the _configInfo field; + /// - Resolves the platform strategy for the current OS via ; + /// - If the OS strategy inherits from AbstractStrategy, passes the pending differential pipeline (DiffPipeline). /// public void Create(GlobalConfigInfo parameter) { @@ -217,16 +221,17 @@ public void Create(GlobalConfigInfo parameter) } /// - /// 执行客户端更新流程的入口方法。清理冲突进程、执行标准工作流,并在异常时触发错误事件和报告。 + /// Entry method for executing the client update process. Cleans up conflicting processes, executes the standard workflow, + /// and triggers error events and reports on exceptions. /// - /// 表示异步操作的任务。 - /// 当策略尚未通过 方法配置时抛出。 + /// A task that represents the asynchronous operation. + /// Thrown when the strategy has not been configured via . /// - /// 执行流程: - /// 1. 调用 关闭可能冲突的升级进程(Bowl)。 - /// 2. 调用 执行核心更新工作流。 - /// 3. 如果上述步骤抛出异常,依次安全调用:错误钩子 → 失败报告 → 日志记录 → 事件分发。 - /// 所有异常都被捕获并通过 分发为 ,不会向上传播。 + /// Execution flow: + /// 1. Calls to shut down potentially conflicting upgrade processes (Bowl). + /// 2. Calls to execute the core update workflow. + /// 3. If the above steps throw an exception, safely invokes in order: error hook, failure report, log, and event dispatch. + /// All exceptions are caught and dispatched as via without propagating upward. /// public async Task ExecuteAsync() { @@ -251,13 +256,13 @@ public async Task ExecuteAsync() private DiffPipeline? _pendingDiffPipeline; /// - /// 设置差异更新管道,用于在 OS 策略上并行应用增量补丁。 + /// Sets the differential update pipeline for parallel application of incremental patches on the OS strategy. /// - /// 差异更新管道实例。如果为 null,则清除待设置管道。 + /// Differential update pipeline instance. If null, clears the pending pipeline. /// - /// 如果 OS 策略(通过 解析)尚未初始化,则将管道暂存到 _pendingDiffPipeline 字段, - /// 待 方法被调用时传递到 OS 策略的 DiffPipeline 属性。 - /// 差异管道支持并行应用增量补丁(differential patches),显著加快升级包应用速度。 + /// If the OS strategy (resolved via ) is not yet initialized, the pipeline is stored in the + /// _pendingDiffPipeline field and passed to the OS strategy's DiffPipeline property when is called. + /// The differential pipeline supports parallel application of incremental patches, significantly speeding up upgrade package application. /// public void SetDiffPipeline(DiffPipeline? diffPipeline) { @@ -268,13 +273,13 @@ public void SetDiffPipeline(DiffPipeline? diffPipeline) } /// - /// 启动已更新的应用程序。委托给已解析的 OS 特定策略执行。 + /// Starts the updated application. Delegates to the resolved OS-specific strategy. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// - /// 此方法将启动调用委托给底层 OS 策略()。 - /// 在 中被调用以启动升级端进程。 - /// 如果 _osStrategynull(尚未调用 ),调用将被安全忽略。 + /// This method delegates the launch call to the underlying OS strategy (, , or ). + /// It is called within to start the upgrade process. + /// If _osStrategy is null ( has not been called yet), the call is safely ignored. /// public async Task StartAppAsync() { @@ -283,15 +288,17 @@ public async Task StartAppAsync() } /// - /// 注册更新预检查回调。在版本校验完成后、实际下载和备份前调用,允许根据业务逻辑决定是否跳过本次更新。 + /// Registers an update pre-check callback. Called after version validation completes but before actual download and backup, + /// allowing the update to be skipped based on business logic. /// - /// 预检查回调函数,接收 参数,返回 true 表示跳过更新,false 表示继续更新。 - /// 返回当前 实例,支持链式调用。 - /// null 时抛出。 + /// Pre-check callback function that receives an parameter and returns true to skip the update, false to continue. + /// Returns the current instance, supporting fluent chaining. + /// Thrown when is null. /// - /// 预检查回调在 方法中被调用,仅在非强制更新时生效。 - /// 回调接收完整的 ,可根据版本号、发布日期、更新内容等业务逻辑决定是否跳过。 - /// 例如:当更新版本仅包含非关键修复且当前版本低于某个阈值时跳过更新。 + /// The pre-check callback is invoked in the method and is only effective for non-forced updates. + /// The callback receives the full and can decide whether to skip based on business logic + /// such as version number, release date, or update content. + /// For example: skip an update when the new version only contains non-critical fixes and the current version is below a certain threshold. /// public ClientUpdateStrategy UseUpdatePrecheck(Func func) { @@ -302,12 +309,12 @@ public ClientUpdateStrategy UseUpdatePrecheck(Func fu #region Workflow /// - /// 执行核心更新工作流。根据运行时配置决定执行标准模式或静默模式。 + /// Executes the core update workflow. Determines whether to run standard mode or silent mode based on runtime configuration. /// /// - /// 当前实现始终调用 。 - /// 运行时选项(EncodingFormatDownloadTimeOut 等) - /// 已由 Bootstrap.ApplyRuntimeOptions() 设置到 _configInfo 上。 + /// The current implementation always calls . + /// Runtime options (Encoding, Format, DownloadTimeOut, etc.) + /// are already set on _configInfo by Bootstrap.ApplyRuntimeOptions(). /// private async Task ExecuteWorkflowAsync() { @@ -318,46 +325,48 @@ private async Task ExecuteWorkflowAsync() } /// - /// 执行标准客户端更新工作流。包含版本校验、事件分发、跳过检查、钩子调用、备份、下载、场景分发和启动升级进程。 + /// Executes the standard client update workflow. Includes version validation, event dispatch, skip check, hook invocation, + /// backup, download, scenario dispatch, and upgrade process launch. /// /// - /// 完整执行流程如下: + /// Complete execution flow: /// - /// 步骤 1 — 版本校验:使用 DownloadSource 或默认的 HttpDownloadSource 调用服务器验证版本。 - /// 返回结果包含更新资源清单以及主程序/升级程序的更新标志位。 + /// Step 1 - Version Validation: Uses DownloadSource or the default HttpDownloadSource to call the server for version validation. + /// The response contains the update asset manifest and update flags for the main program/upgrade program. /// /// - /// 步骤 2 — 场景判定:根据服务器的 HasMainUpdateHasUpgradeUpdate 标志确定更新场景: - /// None(无需更新)、UpgradeOnly(仅升级程序)、MainOnly(仅主程序)、Both(两者都需要更新)。 + /// Step 2 - Scenario Determination: Determines the update scenario based on the server's HasMainUpdate and HasUpgradeUpdate flags: + /// None (no update needed), UpgradeOnly (upgrade program only), MainOnly (main program only), Both (both need updating). /// /// - /// 步骤 3 — 事件分发:构造 并通过 EventManager.Instance.Dispatch 分发。 + /// Step 3 - Event Dispatch: Constructs an and dispatches it via EventManager.Instance.Dispatch. /// /// - /// 步骤 4 — 跳过检查:若非强制更新且预检查回调返回 true,则跳过本次更新。 + /// Step 4 - Skip Check: If the update is not forced and the pre-check callback returns true, the update is skipped. /// /// - /// 步骤 5 — 更新前钩子:调用 Hooks.OnBeforeUpdateAsync,返回 false 时取消更新。 + /// Step 5 - Pre-Update Hook: Calls Hooks.OnBeforeUpdateAsync; cancels the update if it returns false. /// /// - /// 步骤 6 — 备份:将当前安装目录备份到临时目录(可通过 BackupEnabled 禁用)。 - /// 同时初始化黑名单配置以排除不需要备份的文件。 + /// Step 6 - Backup: Backs up the current installation directory to a temporary directory (can be disabled via BackupEnabled). + /// Also initializes the blacklist configuration to exclude files that do not need to be backed up. /// /// - /// 步骤 7 — 失败版本检查:通过环境变量 UpgradeFail 检查当前版本是否已知失败,避免重复失败的升级。 + /// Step 7 - Failed Version Check: Checks whether the current version is a known failed upgrade via the UpgradeFail environment variable, + /// avoiding repeated failed upgrades. /// /// - /// 步骤 8 — 下载:通过下载编排器下载所有更新包。 - /// 优先使用自定义编排器(_orchestrator),否则使用 DefaultDownloadOrchestrator, - /// 后者支持自定义重试策略、下载执行器和后处理管道。 + /// Step 8 - Download: Downloads all update packages through the download orchestrator. + /// Prefers the custom orchestrator (_orchestrator), otherwise uses DefaultDownloadOrchestrator, + /// which supports custom retry policies, download executors, and post-processing pipelines. /// /// - /// 步骤 9 — 场景分发:根据判定结果执行不同操作: + /// Step 9 - Scenario Dispatch: Executes different operations based on the determined scenario: /// - /// UpgradeOnly:调用 原地应用升级更新包。 - /// MainOnly:调用 发送主程序更新信息, - /// 然后调用 启动升级进程。 - /// Both:先应用升级更新包,再发送主程序信息并启动升级进程。 + /// UpgradeOnly: Calls to apply upgrade packages in place. + /// MainOnly: Calls to send main program update information, + /// then calls to start the upgrade process. + /// Both: Applies upgrade packages first, then sends main program information and starts the upgrade process. /// /// /// @@ -545,13 +554,13 @@ private async Task ExecuteStandardWorkflowAsync() #region Scenario actions /// - /// 原地应用升级程序(Upgrade)的更新包。委托 OS 策略执行实际的补丁应用操作。 + /// Applies upgrade (Upgrade) update packages in place. Delegates the actual patch application to the OS strategy. /// - /// 升级程序的版本信息列表。 + /// The list of version information for the upgrade program. /// - /// 此方法仅处理 AppType.Upgrade 类型的更新包。 - /// 将更新包路径设置到 _configInfo.UpdateVersions,然后委托 OS 策略(如 ) - /// 通过其管道(增量/全量)逐个应用补丁。 + /// This method only processes update packages of type AppType.Upgrade. + /// It sets the update package paths in _configInfo.UpdateVersions and then delegates to the OS strategy + /// (such as ) to apply patches one by one through its pipeline (incremental/full). /// private async Task ApplyUpgradePackagesAsync(List upgradeVersions) { @@ -563,15 +572,16 @@ private async Task ApplyUpgradePackagesAsync(List upgradeVersions) } /// - /// 将主程序(Client)的更新信息序列化为 ProcessInfo 并通过加密 IPC 发送给升级端进程。 + /// Serializes the main program (Client) update information as ProcessInfo and sends it to the upgrade process via encrypted IPC. /// - /// 主程序的版本信息列表。 + /// The list of version information for the main program. /// - /// 此方法完成以下操作: - /// 1. 使用 ConfigurationMapper.MapToProcessInfo 将配置信息和版本列表映射为 ProcessInfo 对象; - /// 2. 将 ProcessInfo 序列化为 JSON 字符串,存储在 _configInfo.ProcessInfo 中; - /// 3. 通过 EncryptedFileProcessInfoProvider 将加密后的进程信息发送给升级端。 - /// 升级端(Bowl)收到此信息后,将根据 ProcessInfo 执行实际的安装替换操作。 + /// This method performs the following operations: + /// 1. Uses ConfigurationMapper.MapToProcessInfo to map configuration information and version list into a ProcessInfo object; + /// 2. Serializes the ProcessInfo as a JSON string and stores it in _configInfo.ProcessInfo; + /// 3. Sends the encrypted process information to the upgrade process via EncryptedFileProcessInfoProvider. + /// After the upgrade process (Bowl) receives this information, it will perform the actual installation and replacement operations + /// based on the ProcessInfo. /// private void SendProcessIpc(List clientVersions) { @@ -588,14 +598,14 @@ private void SendProcessIpc(List clientVersions) } /// - /// 启动升级进程(Bowl/Upgrade App),委托 OS 策略执行平台特定的进程启动操作。 + /// Launches the upgrade process (Bowl/Upgrade App), delegating to the OS strategy for platform-specific process startup. /// /// - /// 启动前配置 OS 策略的启动参数: - /// - LaunchAppName:设置为 _configInfo.UpdateAppName,指定要启动的升级程序文件名; - /// - LaunchBowl:设置为 false,避免递归启动 Bowl 进程; - /// - UseUpdatePath:根据 _configInfo.UpdatePath 是否为空字符串决定。 - /// 调用 后,升级进程将接管后续的安装替换操作。 + /// Configures the OS strategy launch parameters before starting: + /// - LaunchAppName: Set to _configInfo.UpdateAppName, specifying the upgrade program file name to launch; + /// - LaunchBowl: Set to false to avoid recursively launching the Bowl process; + /// - UseUpdatePath: Determined by whether _configInfo.UpdatePath is empty. + /// After calling , the upgrade process takes over the subsequent installation and replacement operations. /// private async Task LaunchUpgradeProcessAsync() { @@ -616,15 +626,15 @@ private async Task LaunchUpgradeProcessAsync() #region Helpers /// - /// 解析当前操作系统对应的更新策略。优先使用自定义策略,否则根据操作系统自动选择。 + /// Resolves the update strategy for the current operating system. Prefers a custom strategy, otherwise auto-selects based on the OS. /// - /// OS 特定策略实例()。 - /// 当前操作系统不受支持(非 Windows、Linux 或 macOS)时抛出。 + /// An OS-specific strategy instance (, , or ). + /// Thrown when the current OS is not supported (not Windows, Linux, or macOS). /// - /// 解析优先级: - /// 1. 通过 设置的自定义策略(由引导程序通过 .Strategy<T>() 注入); - /// 2. 根据 RuntimeInformation.IsOSPlatform 自动探测当前操作系统并实例化对应策略。 - /// 如果以上均不匹配,则抛出 + /// Resolution priority: + /// 1. Custom strategy set via (injected by the bootstrap via .Strategy<T>()); + /// 2. Automatic OS detection via RuntimeInformation.IsOSPlatform to instantiate the corresponding strategy. + /// If neither matches, a is thrown. /// private IStrategy ResolveOsStrategy() { @@ -640,12 +650,13 @@ private IStrategy ResolveOsStrategy() } /// - /// 初始化文件黑名单配置。用于备份和文件操作时排除不需要处理的文件、格式和目录。 + /// Initializes the file blacklist configuration. Used to exclude files, formats, and directories that do not need processing + /// during backup and file operations. /// /// - /// 优先使用 _configInfo 中配置的黑名单;如果未配置则使用 提供的默认值。 - /// 黑名单包括:排除的文件名列表(如配置文件)、排除的文件扩展名列表(如 .log)、 - /// 跳过的目录列表(如临时目录)。 + /// Prefers the blacklist configured in _configInfo; if not configured, uses the defaults from . + /// The blacklist includes: excluded file names (e.g., config files), excluded file extensions (e.g., .log), + /// and skipped directories (e.g., temporary directories). /// private void InitBlackList() { @@ -660,12 +671,12 @@ private void InitBlackList() } /// - /// 备份当前安装目录到指定的备份目录,用于更新失败时回滚。 + /// Backs up the current installation directory to the specified backup directory for rollback on update failure. /// /// - /// 备份操作通过 StorageManager.Backup 执行,排除黑名单中配置的目录。 - /// 备份目录的路径格式为:{InstallPath}/backup_{ClientVersion}。 - /// 此步骤可通过 GlobalConfigInfo.BackupEnabled 设置为 false 跳过。 + /// The backup operation is performed via StorageManager.Backup, excluding directories configured in the blacklist. + /// The backup directory path format is: {InstallPath}/backup_{ClientVersion}. + /// This step can be skipped by setting GlobalConfigInfo.BackupEnabled to false. /// private void Backup() { @@ -676,17 +687,17 @@ private void Backup() } /// - /// 判断本次更新是否可以被跳过。 + /// Determines whether the current update can be skipped. /// - /// 是否强制更新。强制更新时不可跳过。 - /// 更新信息事件参数,包含版本列表和响应状态。 - /// 如果可以跳过则返回 true,否则返回 false + /// Whether the update is forced. A forced update cannot be skipped. + /// Update information event arguments containing the version list and response status. + /// Returns true if the update can be skipped; otherwise false. /// - /// 跳过条件: - /// 1. false(非强制更新); - /// 2. 通过 注册的预检查回调返回 true - /// 如果任意条件不满足(强制更新或无预检查回调),则不可跳过。 - /// 此方法在 的事件分发之后、备份之前被调用。 + /// Skip conditions: + /// 1. is false (non-forced update); + /// 2. The pre-check callback registered via returns true. + /// If either condition is not met (forced update or no pre-check callback), the update cannot be skipped. + /// This method is called after event dispatch and before backup in . /// private bool CanSkip(bool isForcibly, UpdateInfoEventArgs updateInfo) { @@ -695,15 +706,16 @@ private bool CanSkip(bool isForcibly, UpdateInfoEventArgs updateInfo) } /// - /// 检查指定版本是否已被记录为失败的升级版本。 + /// Checks whether the specified version has been recorded as a known failed upgrade version. /// - /// 要检查的版本号字符串。 - /// 如果该版本曾被标记为失败升级且当前环境变量中的失败版本大于或等于指定版本,则返回 true + /// The version string to check. + /// Returns true if the version was previously marked as a failed upgrade and the failed version in the environment variable + /// is greater than or equal to the specified version. /// - /// 通过读取环境变量 UpgradeFail 获取已知失败的版本号。 - /// 如果 UpgradeFail 环境变量为空或 为空,则返回 false。 - /// 版本比较使用 类的语义化版本比较。 - /// 此机制用于避免反复尝试已知失败的升级。 + /// Reads the known failed version number from the UpgradeFail environment variable. + /// If the UpgradeFail environment variable is empty or is empty, returns false. + /// Version comparison uses the semantic version comparison of the class. + /// This mechanism avoids repeatedly attempting known failed upgrades. /// private bool CheckFail(string version) { @@ -714,13 +726,13 @@ private bool CheckFail(string version) } /// - /// 获取当前运行平台的枚举值。 + /// Gets the platform type for the current running OS. /// - /// 当前平台类型(、 - /// )。 + /// The current platform type (, , + /// , or ). /// - /// 使用 RuntimeInformation.IsOSPlatform 进行运行时检测。 - /// 此返回值用于构造 HttpDownloadSource 时向服务器告知客户端平台。 + /// Uses RuntimeInformation.IsOSPlatform for runtime detection. + /// The return value is used to inform the server of the client platform when constructing HttpDownloadSource. /// private static PlatformType GetPlatform() { @@ -731,14 +743,15 @@ private static PlatformType GetPlatform() } /// - /// 关闭指定名称的冲突进程(Bowl 升级进程),释放文件锁定。 + /// Shuts down conflicting processes (Bowl upgrade process) by name to release file locks. /// - /// 要关闭的进程名称(不含扩展名)。为空或空白时跳过。 + /// The name of the process to shut down (without extension). Skipped if null or whitespace. /// - /// 此方法在更新流程的入口处被调用,用于确保升级进程(Bowl)不在运行状态, - /// 避免文件锁定导致后续备份或替换操作失败。 - /// 关闭操作通过 GracefulExit.ShutdownAsync 实现优雅退出。 - /// 如果指定进程不存在或关闭过程中发生异常,此方法会记录警告但不阻断流程。 + /// This method is called at the entry point of the update flow to ensure the upgrade process (Bowl) is not running, + /// preventing file locks from causing subsequent backup or replacement operations to fail. + /// The shutdown is performed gracefully via GracefulExit.ShutdownAsync. + /// If the specified process does not exist or an exception occurs during shutdown, this method logs a warning + /// but does not block the flow. /// private async Task CallSmallBowlHomeAsync(string processName) { @@ -764,9 +777,9 @@ private async Task CallSmallBowlHomeAsync(string processName) // ════════════════════════════════════════════════════════════════ /// - /// 构建更新上下文对象,用于传递给钩子和报告器方法。 + /// Builds an update context object for passing to hook and reporter methods. /// - /// 包含当前更新信息的 实例。 + /// An instance containing the current update information. private Hooks.UpdateContext BuildUpdateContext() { return new Hooks.UpdateContext( @@ -779,10 +792,10 @@ private Hooks.UpdateContext BuildUpdateContext() } /// - /// 安全调用更新前钩子。如果钩子抛出异常,记录警告并返回 true(允许继续更新)。 + /// Safely invokes the pre-update hook. If the hook throws an exception, logs a warning and returns true (allows the update to continue). /// - /// 更新上下文。 - /// 钩子返回的值;如果钩子抛出异常则返回 true + /// The update context. + /// The value returned by the hook; returns true if the hook throws an exception. private async Task SafeOnBeforeUpdateAsync(Hooks.UpdateContext ctx) { try @@ -797,9 +810,9 @@ private async Task SafeOnBeforeUpdateAsync(Hooks.UpdateContext ctx) } /// - /// 安全调用启动应用前钩子。如果钩子抛出异常,记录警告并继续流程。 + /// Safely invokes the pre-start-app hook. If the hook throws an exception, logs a warning and continues the flow. /// - /// 更新上下文。 + /// The update context. private async Task SafeOnBeforeStartAppAsync(Hooks.UpdateContext ctx) { try @@ -813,10 +826,10 @@ private async Task SafeOnBeforeStartAppAsync(Hooks.UpdateContext ctx) } /// - /// 安全调用更新错误钩子。如果钩子抛出异常,记录警告。 + /// Safely invokes the update error hook. If the hook throws an exception, logs a warning. /// - /// 更新上下文。 - /// 更新过程中发生的异常。 + /// The update context. + /// The exception that occurred during the update. private async Task SafeOnUpdateErrorAsync(Hooks.UpdateContext ctx, Exception error) { try @@ -830,9 +843,9 @@ private async Task SafeOnUpdateErrorAsync(Hooks.UpdateContext ctx, Exception err } /// - /// 安全调用更新完成后钩子(升级包应用后)。如果钩子抛出异常,记录警告。 + /// Safely invokes the post-update hook (after upgrade packages are applied). If the hook throws an exception, logs a warning. /// - /// 更新上下文。 + /// The update context. private async Task SafeOnAfterUpdateAsync(Hooks.UpdateContext ctx) { try @@ -846,9 +859,9 @@ private async Task SafeOnAfterUpdateAsync(Hooks.UpdateContext ctx) } /// - /// 安全调用下载完成钩子。如果钩子抛出异常,记录警告。 + /// Safely invokes the download completed hook. If the hook throws an exception, logs a warning. /// - /// 更新上下文。 + /// The update context. private async Task SafeOnDownloadCompletedAsync(Hooks.UpdateContext ctx) { try @@ -866,9 +879,9 @@ private async Task SafeOnDownloadCompletedAsync(Hooks.UpdateContext ctx) } /// - /// 安全上报更新已开始状态。如果上报失败,记录警告。 + /// Safely reports the update started status. If the report fails, logs a warning. /// - /// 更新上下文。 + /// The update context. private async Task SafeReportUpdateStartedAsync(Hooks.UpdateContext ctx) { try @@ -884,9 +897,9 @@ await Reporter } /// - /// 安全上报下载已完成状态。如果上报失败,记录警告。 + /// Safely reports the download completed status. If the report fails, logs a warning. /// - /// 更新上下文。 + /// The update context. private async Task SafeReportDownloadCompletedAsync(Hooks.UpdateContext ctx) { try @@ -902,10 +915,10 @@ await Reporter } /// - /// 安全上报更新失败状态。如果上报失败,记录警告。 + /// Safely reports the update failed status. If the report fails, logs a warning. /// - /// 更新上下文。 - /// 导致更新失败的异常。 + /// The update context. + /// The exception that caused the update to fail. private async Task SafeReportUpdateFailedAsync(Hooks.UpdateContext ctx, Exception error) { try @@ -921,9 +934,9 @@ await Reporter } /// - /// 安全上报更新应用成功状态。如果上报失败,记录警告。 + /// Safely reports the update applied success status. If the report fails, logs a warning. /// - /// 更新上下文。 + /// The update context. private async Task SafeReportUpdateAppliedAsync(Hooks.UpdateContext ctx) { try diff --git a/src/c#/GeneralUpdate.Core/Strategy/IStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/IStrategy.cs index 9be4649f..a5503312 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/IStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/IStrategy.cs @@ -6,88 +6,89 @@ namespace GeneralUpdate.Core.Strategy { /// - /// 定义更新策略的协定接口。 - /// 所有平台特定的更新策略(Windows、Linux、macOS 以及 OSS)都必须实现此接口, - /// 以提供完整的更新生命周期管理。 + /// Defines the contract interface for update strategies. + /// All platform-specific update strategies (Windows, Linux, macOS, and OSS) must implement this interface + /// to provide complete update lifecycle management. /// /// - /// 更新生命周期按以下顺序执行: + /// The update lifecycle executes in the following order: /// - /// — 使用全局配置初始化策略。 - /// — 执行更新流程(哈希校验、解压缩、应用补丁等)。 - /// — 启动已更新的主应用程序并退出更新程序进程。 + /// — Initializes the strategy with global configuration. + /// — Executes the update process (hash verification, decompression, patch application, etc.). + /// — Starts the updated main application and exits the updater process. /// /// - /// 如果需要扩展自定义策略,请继承此接口。 - /// 平台相关策略(如 WindowsStrategyLinuxStrategyMacStrategy) - /// 通常继承自 AbstractStrategy 基类,该基类提供了管道执行的标准实现。 + /// To extend with a custom strategy, implement this interface. + /// Platform-specific strategies (such as WindowsStrategy, LinuxStrategy, MacStrategy) + /// typically inherit from the AbstractStrategy base class, which provides a standard pipeline execution implementation. /// /// public interface IStrategy { /// - /// 获取或设置更新生命周期钩子,用于在更新前后执行自定义回调。 + /// Gets or sets the update lifecycle hooks, used to execute custom callbacks before and after the update. /// /// - /// 提供了多个可重写的方法,包括: - /// 、 - /// 以及错误处理回调。 - /// 默认实现使用 NoOpUpdateHooks(空操作)。 + /// provides multiple overridable methods, including: + /// , , + /// , and error-handling callbacks. + /// The default implementation uses NoOpUpdateHooks (no operation). /// IUpdateHooks Hooks { get; set; } /// - /// 获取或设置更新状态报告器,用于向服务器或事件系统报告更新进度和结果。 + /// Gets or sets the update status reporter, used to report update progress and results to the server or event system. /// /// - /// 报告器可用于将更新状态(正在更新、成功、失败)上报给远程服务(如 GeneralSpacestation), - /// 或通过 EventManager 触发本地事件。默认实现使用 NoOpUpdateReporter(空操作)。 + /// The reporter can be used to report update status (updating, success, failure) to a remote service + /// (such as GeneralSpacestation) or trigger local events via EventManager. + /// The default implementation uses NoOpUpdateReporter (no operation). /// IUpdateReporter Reporter { get; set; } /// - /// 异步执行更新策略的主要流程。 + /// Asynchronously executes the main update strategy workflow. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// - /// 此方法是更新流程的核心。典型的执行流程包括: + /// This method is the core of the update process. A typical execution flow includes: /// - /// 下载更新包或从配置源获取版本信息。 - /// 通过中间件管道执行哈希校验、解压缩和应用补丁。 - /// 触发更新前后的生命周期钩子。 - /// 报告更新状态(开始、进度、完成或失败)。 + /// Downloading update packages or retrieving version information from a configuration source. + /// Executing hash verification, decompression, and patch application through the middleware pipeline. + /// Triggering pre- and post-update lifecycle hooks. + /// Reporting update status (start, progress, completion, or failure). /// /// Task ExecuteAsync(); /// - /// 异步启动已更新的主应用程序,然后退出当前的更新程序进程。 + /// Asynchronously starts the updated main application, then exits the current updater process. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// /// - /// 此方法在更新流程完成后调用。它会: + /// This method is called after the update process completes. It will: /// /// - /// 解析主应用程序的可执行文件路径。 - /// 使用 Process.Start 启动主应用程序。 - /// 调用 GracefulExit.CurrentProcessAsync() 优雅地终止更新程序进程。 + /// Resolve the executable path of the main application. + /// Start the main application using Process.Start. + /// Call GracefulExit.CurrentProcessAsync() to gracefully terminate the updater process. /// /// - /// 对于 Windows 策略,如果配置了 Bowl 辅助进程,还会同时启动该进程。 + /// For the Windows strategy, if a Bowl helper process is configured, it will also be started. /// /// Task StartAppAsync(); /// - /// 使用全局配置信息创建并初始化策略实例。 + /// Creates and initializes the strategy instance using global configuration information. /// - /// 全局配置信息,包含安装路径、应用名称、版本号等设置。 - /// 为 null 时抛出。 + /// Global configuration information containing settings such as install path, application name, version number, etc. + /// Thrown when is null. /// - /// 此方法必须在调用 之前调用,以提供策略执行所需的全部配置参数。 - /// 配置信息包括 InstallPathMainAppNameUpdateAppNameClientVersion、 - /// PatchEnabled 等关键设置。 + /// This method must be called before to provide all configuration parameters required for strategy execution. + /// Configuration information includes key settings such as InstallPath, MainAppName, UpdateAppName, ClientVersion, + /// and PatchEnabled. /// void Create(GlobalConfigInfo parameter); } diff --git a/src/c#/GeneralUpdate.Core/Strategy/LinuxStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/LinuxStrategy.cs index 987b5d44..a626419e 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/LinuxStrategy.cs @@ -9,38 +9,40 @@ namespace GeneralUpdate.Core.Strategy; /// -/// Linux 平台专用的更新策略。 -/// 实现了针对 Linux 操作系统的更新流程,包括管道构建、哈希校验、解压缩和应用补丁。 +/// Linux platform-specific update strategy. +/// Implements the update flow for the Linux operating system, including pipeline building, +/// hash verification, decompression, and patch application. /// /// /// -/// 此类继承自 AbstractStrategy,提供了 Linux 环境下的完整更新生命周期管理。 +/// This class inherits from AbstractStrategy and provides complete update lifecycle management for the Linux environment. /// /// -/// 核心流程: +/// Core flow: /// -/// BuildPipeline — 构建中间件管道,按顺序执行哈希校验→解压缩→(可选)应用补丁。 -/// CreatePipelineContext — 创建包含版本信息和补丁路径的管道上下文。 -/// StartAppAsync — 使用 Process.Start 启动已更新的主应用程序, -/// 然后释放跟踪器并优雅退出当前更新程序进程。 +/// BuildPipeline — Builds the middleware pipeline, executing hash verification, decompression, +/// and (optionally) patch application in order. +/// CreatePipelineContext — Creates the pipeline context containing version information and patch path. +/// StartAppAsync — Starts the updated main application using Process.Start, +/// then releases the tracer and gracefully exits the current updater process. /// /// /// -/// 与 Windows 策略不同,Linux 策略不包含 Bowl 辅助进程的启动逻辑。 -/// 补丁功能由 PatchEnabled 配置控制。 +/// Unlike the Windows strategy, the Linux strategy does not include Bowl helper process launch logic. +/// The patching feature is controlled by the PatchEnabled configuration. /// /// public class LinuxStrategy : AbstractStrategy { /// - /// 创建管道上下文,包含目标版本信息和补丁路径。 + /// Creates the pipeline context containing target version information and patch path. /// - /// 目标版本信息。 - /// 补丁文件的路径。 - /// 包含版本信息和补丁路径的 PipelineContext 实例。 + /// The target version information. + /// The path to the patch files. + /// A PipelineContext instance containing version information and patch path. /// - /// 此方法被 AbstractStrategy 中的管道执行流程调用。 - /// 它会记录版本号和补丁路径,然后调用基类的 CreatePipelineContext 方法创建上下文对象。 + /// This method is called by the pipeline execution flow in AbstractStrategy. + /// It logs the version number and patch path, then calls the base class's CreatePipelineContext method to create the context object. /// protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) { @@ -50,18 +52,19 @@ protected override PipelineContext CreatePipelineContext(VersionInfo version, st } /// - /// 构建 Linux 平台的更新中间件管道。 + /// Builds the Linux platform update middleware pipeline. /// - /// 管道上下文,包含版本和补丁信息。 - /// 配置好的 PipelineBuilder 实例,包含哈希校验、解压缩和(可选)补丁中间件。 + /// The pipeline context containing version and patch information. + /// A configured PipelineBuilder instance containing hash verification, decompression, + /// and (optionally) patch middleware. /// /// - /// 管道按以下顺序组装中间件: + /// The pipeline assembles middleware in the following order: /// /// - /// HashMiddleware — 计算并验证文件哈希,确保数据完整性。 - /// CompressMiddleware — 解压缩下载的更新包。 - /// PatchMiddleware — (可选)应用二进制补丁。仅在 _configinfo.PatchEnabled 为 true 时启用。 + /// HashMiddleware — Computes and verifies file hashes to ensure data integrity. + /// CompressMiddleware — Decompresses the downloaded update package. + /// PatchMiddleware — (Optional) Applies binary patches. Only enabled when _configinfo.PatchEnabled is true. /// /// protected override PipelineBuilder BuildPipeline(PipelineContext context) @@ -75,23 +78,23 @@ protected override PipelineBuilder BuildPipeline(PipelineContext context) } /// - /// 异步启动已更新的主应用程序,然后退出当前更新程序进程。 + /// Asynchronously starts the updated main application, then exits the current updater process. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// /// - /// 此方法在更新流程完成后调用。执行步骤如下: + /// This method is called after the update process completes. The execution steps are as follows: /// /// - /// 使用 LaunchAppName 属性获取主应用程序名称,如果未设置则抛出异常。 - /// 调用 ResolveAppPath 解析应用程序的完整路径。 - /// 使用 Process.Start 启动主应用程序。 - /// 释放 GeneralTracer 资源。 - /// 调用 GracefulExit.CurrentProcessAsync() 优雅终止更新程序进程。 + /// Uses the LaunchAppName property to get the main application name; throws if not set. + /// Calls ResolveAppPath to resolve the full path of the application. + /// Starts the main application using Process.Start. + /// Disposes the GeneralTracer resources. + /// Calls GracefulExit.CurrentProcessAsync() to gracefully terminate the updater process. /// /// - /// 注意:Linux 策略在启动应用程序时不支持 Bowl 辅助进程。任何异常都会被 EventManager - /// 捕获并分发为 ExceptionEventArgs 事件。 + /// Note: The Linux strategy does not support the Bowl helper process when starting the application. + /// Any exception is caught and dispatched as an ExceptionEventArgs event via EventManager. /// /// public override async Task StartAppAsync() diff --git a/src/c#/GeneralUpdate.Core/Strategy/MacStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/MacStrategy.cs index bda00110..f63254ce 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/MacStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/MacStrategy.cs @@ -9,33 +9,36 @@ namespace GeneralUpdate.Core.Strategy; /// -/// macOS 平台专用的更新策略。 -/// 实现了针对 macOS 操作系统的更新流程,包括管道构建、哈希校验、解压缩和应用补丁。 +/// macOS platform-specific update strategy. +/// Implements the update flow for the macOS operating system, including pipeline building, +/// hash verification, decompression, and patch application. /// /// /// -/// 此类继承自 AbstractStrategy,提供了 macOS 环境下的完整更新生命周期管理。 -/// 其管道流程与 Linux 策略相似,但在 StartAppAsync 中会额外验证文件是否存在。 +/// This class inherits from AbstractStrategy and provides complete update lifecycle management for the macOS environment. +/// Its pipeline flow is similar to the Linux strategy, but additionally verifies file existence in StartAppAsync. /// /// -/// 核心流程: +/// Core flow: /// -/// BuildPipeline — 构建中间件管道,按顺序执行哈希校验→解压缩→(可选)应用补丁。 -/// ExecuteAsync — 执行基类的管道流程,并记录开始信息(使用 ConfigureAwait(false) 避免上下文切换死锁)。 -/// Create — 直接保存配置信息到内部字段。 -/// StartAppAsync — 启动已更新的主应用程序,然后退出当前更新程序进程。 +/// BuildPipeline — Builds the middleware pipeline, executing hash verification, decompression, +/// and (optionally) patch application in order. +/// ExecuteAsync — Executes the base class pipeline flow and logs start information +/// (uses ConfigureAwait(false) to avoid context-switch deadlocks). +/// Create — Directly stores configuration information in the internal field. +/// StartAppAsync — Starts the updated main application, then exits the current updater process. /// /// /// public class MacStrategy : AbstractStrategy { /// - /// 异步执行更新策略的主流程。 + /// Asynchronously executes the main update strategy flow. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// - /// 此方法首先记录执行开始信息,然后调用基类的 ExecuteAsync 方法执行实际的管道流程。 - /// 使用 ConfigureAwait(false) 配置以避免在 UI 上下文中发生死锁。 + /// This method first logs the execution start information, then calls the base class's ExecuteAsync method + /// to execute the actual pipeline flow. Uses ConfigureAwait(false) to avoid deadlocks in UI contexts. /// public override async Task ExecuteAsync() { @@ -44,22 +47,22 @@ public override async Task ExecuteAsync() } /// - /// 异步启动已更新的主应用程序,然后退出当前更新程序进程。 + /// Asynchronously starts the updated main application, then exits the current updater process. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// /// - /// 此方法在更新流程完成后调用。与 Windows/Linux 策略的不同之处在于: + /// This method is called after the update process completes. The differences from the Windows/Linux strategies are: /// /// - /// 使用 LaunchAppName 获取主应用程序名称,如果未设置则抛出异常。 - /// 调用 ResolveAppPath 解析应用程序路径。 - /// 在启动前使用 File.Exists 验证文件是否存在(macOS 特有)。 - /// 使用 Process.Start 启动主应用程序。 - /// 释放 GeneralTracer 资源并调用 GracefulExit.CurrentProcessAsync() 退出更新程序进程。 + /// Uses LaunchAppName to get the main application name; throws if not set. + /// Calls ResolveAppPath to resolve the application path. + /// Verifies file existence using File.Exists before launching (macOS-specific). + /// Starts the main application using Process.Start. + /// Disposes the GeneralTracer resources and calls GracefulExit.CurrentProcessAsync() to exit the updater process. /// /// - /// 任何异常都会被 EventManager 捕获并分发为 ExceptionEventArgs 事件。 + /// Any exception is caught and dispatched as an ExceptionEventArgs event via EventManager. /// /// public override async Task StartAppAsync() @@ -89,28 +92,29 @@ public override async Task StartAppAsync() } /// - /// 使用全局配置信息创建并初始化策略实例。 + /// Creates and initializes the strategy instance using global configuration information. /// - /// 全局配置信息,包含安装路径、应用名称、版本号等设置。 + /// Global configuration information containing settings such as install path, application name, and version number. /// - /// macOS 策略的 Create 实现直接保存配置信息到内部字段 _configinfo, - /// 而不调用基类的实现。这提供了更轻量级的初始化方式。 + /// The MacStrategy's Create implementation directly stores the configuration information in the internal field _configinfo, + /// without calling the base class implementation. This provides a more lightweight initialization approach. /// public override void Create(GlobalConfigInfo configInfo) => _configinfo = configInfo; /// - /// 构建 macOS 平台的更新中间件管道。 + /// Builds the macOS platform update middleware pipeline. /// - /// 管道上下文,包含版本和补丁信息。 - /// 配置好的 PipelineBuilder 实例,包含哈希校验、解压缩和(可选)补丁中间件。 + /// The pipeline context containing version and patch information. + /// A configured PipelineBuilder instance containing hash verification, decompression, + /// and (optionally) patch middleware. /// /// - /// 管道按以下顺序组装中间件: + /// The pipeline assembles middleware in the following order: /// /// - /// HashMiddleware — 计算并验证文件哈希,确保数据完整性。 - /// CompressMiddleware — 解压缩下载的更新包。 - /// PatchMiddleware — (可选)应用二进制补丁。仅在 _configinfo.PatchEnabled 为 true 时启用。 + /// HashMiddleware — Computes and verifies file hashes to ensure data integrity. + /// CompressMiddleware — Decompresses the downloaded update package. + /// PatchMiddleware — (Optional) Applies binary patches. Only enabled when _configinfo.PatchEnabled is true. /// /// protected override PipelineBuilder BuildPipeline(PipelineContext context) diff --git a/src/c#/GeneralUpdate.Core/Strategy/OSSUpdateStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/OSSUpdateStrategy.cs index ee293768..d166b0b5 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/OSSUpdateStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/OSSUpdateStrategy.cs @@ -16,34 +16,37 @@ namespace GeneralUpdate.Core.Strategy; /// -/// OSS(对象存储服务,Object Storage Service)更新策略。 -/// 通过 AppType 区分客户端(OSSClient)和升级端(OSSUpgrade)两种角色, -/// 分别执行版本检查、下载、解压缩和应用程序启动等操作。 +/// OSS (Object Storage Service) update strategy. +/// Uses AppType to distinguish between client (OSSClient) and upgrade (OSSUpgrade) roles, +/// performing version checking, downloading, decompression, and application launch accordingly. /// /// /// -/// 此类实现了 接口,提供完整的 OSS 更新生命周期管理。 -/// 根据 AppType 的不同,执行不同的流程: +/// This class implements the interface and provides complete OSS update lifecycle management. +/// Based on the AppType, different workflows are executed: /// /// /// -/// AppType.OSSClient(客户端) +/// AppType.OSSClient (Client) /// -/// 下载版本配置文件,与当前版本比较。如果有新版本,则启动升级进程(GeneralUpdate.Upgrade.exe), -/// 然后退出自身。升级进程负责执行实际的下载和安装操作。 +/// Downloads the version configuration file and compares it with the current version. +/// If a new version is available, launches the upgrade process (GeneralUpdate.Upgrade.exe), +/// then exits itself. The upgrade process is responsible for actual download and installation. /// /// /// -/// AppType.OSSUpgrade(升级端) +/// AppType.OSSUpgrade (Upgrade) /// -/// 读取版本配置文件,从 OSS 下载更新包,解压缩文件,启动主应用程序, -/// 然后退出自身。这是实际执行更新操作的进程。 +/// Reads the version configuration file, downloads update packages from OSS, decompresses files, +/// launches the main application, and then exits itself. This is the process that actually performs +/// the update operations. /// /// /// /// -/// 此策略还通过 IUpdateHooksIUpdateReporter 提供完整的生命周期回调 -/// 和状态上报功能,支持在更新各阶段执行自定义逻辑以及向服务器报告更新状态。 +/// This strategy also provides complete lifecycle callbacks and status reporting via IUpdateHooks +/// and IUpdateReporter, supporting custom logic at various update stages and reporting update status +/// to the server. /// /// public class OSSUpdateStrategy : IStrategy @@ -54,12 +57,13 @@ public class OSSUpdateStrategy : IStrategy private const int DefaultTimeOut = 60; /// - /// 使用指定的角色初始化 OSS 更新策略。 + /// Initializes the OSS update strategy with the specified role. /// - /// 指定当前实例的角色。AppType.OSSClient 表示客户端进程, - /// AppType.OSSUpgrade 表示升级进程。默认为 AppType.OSSClient。 + /// Specifies the role of the current instance. AppType.OSSClient indicates the client process, + /// AppType.OSSUpgrade indicates the upgrade process. Defaults to AppType.OSSClient. /// - /// 客户端角色仅负责检查版本并启动升级进程;升级角色负责实际的下载、解压缩和安装操作。 + /// The client role is only responsible for checking the version and launching the upgrade process; + /// the upgrade role handles the actual downloading, decompression, and installation operations. /// public OSSUpdateStrategy(AppType role = AppType.OSSClient) { @@ -67,50 +71,52 @@ public OSSUpdateStrategy(AppType role = AppType.OSSClient) } /// - /// 获取或设置更新生命周期钩子,用于在更新前后执行自定义回调。 + /// Gets or sets the update lifecycle hooks for executing custom callbacks before and after updates. /// /// - /// 默认实现为 NoOpUpdateHooks(空操作)。可通过设置此属性注入自定义钩子实现, - /// 以在更新开始、下载完成、更新完成、应用程序启动前以及错误处理等阶段执行自定义逻辑。 + /// The default implementation is NoOpUpdateHooks (no operation). Set this property to inject custom hook implementations + /// for executing custom logic at stages such as update start, download completion, update completion, + /// pre-application launch, and error handling. /// public Hooks.IUpdateHooks Hooks { get; set; } = new Hooks.NoOpUpdateHooks(); /// - /// 获取或设置更新状态报告器,用于向服务器或事件系统报告更新进度和结果。 + /// Gets or sets the update status reporter for reporting update progress and results to the server or event system. /// /// - /// 默认实现为 NoOpUpdateReporter(空操作)。可通过设置此属性注入自定义报告器实现, - /// 以将更新状态(正在更新、成功、失败)上报给远程服务(如 GeneralSpacestation)。 + /// The default implementation is NoOpUpdateReporter (no operation). Set this property to inject custom reporter implementations + /// to report update status (updating, success, failure) to a remote service (such as GeneralSpacestation). /// public Download.Reporting.IUpdateReporter Reporter { get; set; } = new Download.Reporting.NoOpUpdateReporter(); /// - /// 获取或设置下载源,用于从远程存储获取下载资产列表。 + /// Gets or sets the download source for retrieving the download asset list from remote storage. /// /// - /// 如果设置了此属性,将使用 IDownloadSource.ListAsync 获取下载资产列表, - /// 而不是从本地版本配置 JSON 文件中读取。 + /// If this property is set, IDownloadSource.ListAsync is used to obtain the download asset list, + /// instead of reading from the local version configuration JSON file. /// public IDownloadSource? DownloadSource { get; set; } /// - /// 获取或设置下载编排器,用于管理多个下载资产的有序下载。 + /// Gets or sets the download orchestrator for managing the orderly download of multiple assets. /// /// - /// 如果设置了此属性,将使用 IDownloadOrchestrator.ExecuteAsync 执行下载操作; - /// 否则将创建默认的 DefaultDownloadOrchestrator 实例进行下载。 - /// 下载编排器支持进度报告、并发控制和错误处理。 + /// If this property is set, IDownloadOrchestrator.ExecuteAsync is used to perform the download; + /// otherwise, a default DefaultDownloadOrchestrator instance is created for downloading. + /// The download orchestrator supports progress reporting, concurrency control, and error handling. /// public IDownloadOrchestrator? DownloadOrchestrator { get; set; } /// - /// 使用全局配置信息初始化 OSS 更新策略实例。 + /// Initializes the OSS update strategy instance with global configuration information. /// - /// 全局配置信息,包含安装路径、应用名称、版本号等设置。 - /// 为 null 时抛出。 + /// Global configuration information containing settings such as install path, application name, and version number. + /// Thrown when is null. /// - /// 此方法必须在调用 之前调用。 - /// 配置信息会被保存到内部字段以供后续使用,包括安装路径、版本号和下载超时等设置。 + /// This method must be called before . + /// The configuration information is stored in internal fields for subsequent use, including install path, + /// version number, and download timeout settings. /// public void Create(GlobalConfigInfo parameter) { @@ -118,24 +124,24 @@ public void Create(GlobalConfigInfo parameter) } /// - /// 根据角色(AppType)异步执行 OSS 更新策略的主要流程。 + /// Asynchronously executes the main OSS update strategy flow based on the role (AppType). /// - /// 表示异步操作的任务。 - /// 策略未通过 初始化时抛出。 + /// A task that represents the asynchronous operation. + /// Thrown when the strategy has not been initialized via . /// /// - /// 此方法根据构造时指定的 _role 分发执行流程: + /// This method dispatches the execution flow based on the _role specified at construction: /// /// /// /// AppType.OSSUpgrade - /// 调用 ExecuteUpgradeAsync 执行完整的更新流程: - /// 读取版本配置→下载更新包→解压缩→启动主应用。 + /// Calls ExecuteUpgradeAsync to execute the full update flow: + /// read version config, download update packages, decompress, and launch the main application. /// /// /// AppType.OSSClient - /// 调用 ExecuteClientAsync 执行客户端检查流程: - /// 下载版本配置→检查更新→(如有更新)启动升级进程。 + /// Calls ExecuteClientAsync to execute the client check flow: + /// download version config, check for updates, and (if available) launch the upgrade process. /// /// /// @@ -159,20 +165,20 @@ public async Task ExecuteAsync() // ════════════════════════════════════════════════════════════════ /// - /// 客户端更新检查流程。下载版本配置文件,检查是否有新版本, - /// 如有更新则启动升级进程并退出当前进程。 + /// Client-side update check flow. Downloads the version configuration file, checks for a new version, + /// and if an update is available, launches the upgrade process and exits the current process. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// /// - /// 执行步骤如下: + /// The execution steps are as follows: /// /// - /// _configInfo.UpdateUrl 下载版本配置文件到安装目录。 - /// 反序列化 JSON 文件,获取版本列表并按发布时间降序排序。 - /// 比较服务器最新版本与当前客户端版本。 - /// 如果有新版本,解析升级进程路径并启动升级程序。 - /// 调用 GracefulExit.CurrentProcessAsync 退出当前进程。 + /// Downloads the version configuration file from _configInfo.UpdateUrl to the install directory. + /// Deserializes the JSON file, obtains the version list, and sorts it in descending order by publish time. + /// Compares the latest server version with the current client version. + /// If a new version is available, resolves the upgrade process path and launches the upgrade program. + /// Calls GracefulExit.CurrentProcessAsync to exit the current process. /// /// private async Task ExecuteClientAsync() @@ -234,24 +240,24 @@ private async Task ExecuteClientAsync() // ════════════════════════════════════════════════════════════════ /// - /// 升级端更新流程。从 OSS 下载更新包、解压缩、启动主应用程序。 + /// Upgrade-side update flow. Downloads update packages from OSS, decompresses them, and launches the main application. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// /// - /// 这是实际执行更新操作的核心方法。执行步骤如下: + /// This is the core method that actually performs the update operations. The execution steps are as follows: /// /// - /// 读取版本配置文件或通过 DownloadSource 获取资产列表。 - /// 触发 OnBeforeUpdateAsync 钩子,允许取消更新。 - /// 下载所有更新资产到安装目录。 - /// 解压缩所有下载的 ZIP 文件并删除原始压缩包。 - /// 依次触发下载完成、更新完成、更新应用等生命周期钩子。 - /// 启动主应用程序。 + /// Reads the version configuration file or retrieves the asset list via DownloadSource. + /// Triggers the OnBeforeUpdateAsync hook, allowing the update to be cancelled. + /// Downloads all update assets to the install directory. + /// Decompresses all downloaded ZIP files and deletes the original archives. + /// Sequentially triggers lifecycle hooks for download completion, update completion, and update applied. + /// Launches the main application. /// /// - /// 任何异常都会被捕获,触发 OnUpdateErrorAsync 钩子并报告失败状态, - /// 然后退出当前进程。 + /// Any exception is caught, triggering the OnUpdateErrorAsync hook and reporting the failure status, + /// then exiting the current process. /// /// private async Task ExecuteUpgradeAsync() @@ -337,22 +343,22 @@ private async Task ExecuteUpgradeAsync() } /// - /// 异步启动已更新的主应用程序。 + /// Asynchronously starts the updated main application. /// - /// 表示异步操作的任务。如果应用程序名称未配置,则返回已完成的任务。 - /// 在主应用程序路径上找不到文件时抛出。 + /// A task that represents the asynchronous operation. Returns a completed task if the application name is not configured. + /// Thrown when the file is not found at the main application path. /// /// - /// 此方法在升级端更新流程完成后调用。它会: + /// This method is called after the upgrade-side update flow completes. It will: /// /// - /// 获取主应用程序名称(优先使用 MainAppName,其次使用 UpdateAppName)。 - /// 在安装目录中定位主应用程序的可执行文件。 - /// 使用 Process.Start 启动主应用程序。 + /// Retrieve the main application name (prefers MainAppName, falls back to UpdateAppName). + /// Locate the main application executable in the install directory. + /// Start the main application using Process.Start. /// /// - /// 与 Windows/Linux/Mac 策略不同,此方法不执行 GracefulExit.CurrentProcessAsync, - /// 退出操作由调用者 ExecuteUpgradeAsync 在 finally 块中处理。 + /// Unlike the Windows/Linux/Mac strategies, this method does not call GracefulExit.CurrentProcessAsync; + /// the exit operation is handled by the caller ExecuteUpgradeAsync in its finally block. /// /// public Task StartAppAsync() @@ -373,14 +379,14 @@ public Task StartAppAsync() #region Helpers /// - /// 从指定 URL 下载版本配置文件并保存到本地路径。 + /// Downloads the version configuration file from the specified URL and saves it to the local path. /// - /// 版本配置文件的远程 URL。 - /// 本地保存路径。 - /// 表示异步操作的任务。 + /// The remote URL of the version configuration file. + /// The local save path. + /// A task that represents the asynchronous operation. /// - /// 如果本地已存在同名文件,会先将其删除再下载新文件。 - /// 使用共享的 HttpClientProvider 实例发送 HTTP 请求。 + /// If the local file already exists, it is deleted before downloading the new file. + /// Uses the shared HttpClientProvider instance to send HTTP requests. /// private static async Task DownloadVersionConfig(string url, string path) { @@ -395,14 +401,14 @@ private static async Task DownloadVersionConfig(string url, string path) } /// - /// 判断客户端是否需要进行 OSS 升级。 + /// Determines whether the client needs an OSS upgrade. /// - /// 当前客户端版本字符串。 - /// 服务器最新版本字符串。 - /// 如果服务器版本高于客户端版本则返回 true,否则返回 false。 + /// The current client version string. + /// The latest server version string. + /// Returns true if the server version is higher than the client version; otherwise false. /// - /// 此方法会尝试将两个版本字符串解析为 Version 类型进行比较。 - /// 如果任一版本字符串为 null、空字符串或无法解析,则返回 false 表示不需要升级。 + /// This method attempts to parse both version strings as Version types for comparison. + /// If either version string is null, empty, or cannot be parsed, returns false indicating no upgrade is needed. /// private static bool IsOssUpgrade(string clientVersion, string serverVersion) { @@ -414,15 +420,15 @@ private static bool IsOssUpgrade(string clientVersion, string serverVersion) } /// - /// 下载所有更新资产到指定的目标路径。 + /// Downloads all update assets to the specified target path. /// - /// 要下载的资产列表。 - /// 目标安装路径。 - /// 表示异步操作的任务。 + /// The list of assets to download. + /// The target installation path. + /// A task that represents the asynchronous operation. /// - /// 如果设置了 DownloadOrchestrator,则使用该编排器执行下载; - /// 否则创建默认的 DefaultDownloadOrchestrator 实例进行下载。 - /// 默认编排器的超时时间从配置中读取,如果未配置则使用 60 秒。 + /// If DownloadOrchestrator is set, uses that orchestrator to perform the download; + /// otherwise creates a default DefaultDownloadOrchestrator instance for downloading. + /// The default orchestrator's timeout is read from the configuration; if not configured, 60 seconds is used. /// private async Task DownloadAssetsAsync(List assets, string targetPath) { @@ -443,14 +449,14 @@ private async Task DownloadAssetsAsync(List assets, string target } /// - /// 解压缩所有下载的资产文件(ZIP 格式)。 + /// Decompresses all downloaded asset files (ZIP format). /// - /// 已下载的资产列表。 - /// 解压缩目标路径。 - /// 解压缩时使用的字符编码。 + /// The list of downloaded assets. + /// The target path for decompression. + /// The character encoding to use during decompression. /// - /// 遍历资产列表,对每个资产执行 ZIP 解压缩操作。 - /// 解压缩完成后删除原始的 ZIP 文件。 + /// Iterates through the asset list and performs ZIP decompression for each asset. + /// Deletes the original ZIP files after decompression completes. /// private static void DecompressAssets(List assets, string targetPath, Encoding encoding) { @@ -466,9 +472,9 @@ private static void DecompressAssets(List assets, string targetPa } /// - /// 构建更新上下文,用于传递更新相关信息给生命周期钩子。 + /// Builds the update context for passing update-related information to lifecycle hooks. /// - /// 包含应用名称、安装路径、版本号等信息的 UpdateContext 实例。 + /// An UpdateContext instance containing application name, install path, version number, and other information. private Hooks.UpdateContext BuildUpdateContext() { return new Hooks.UpdateContext( @@ -481,47 +487,47 @@ private Hooks.UpdateContext BuildUpdateContext() } /// - /// 安全地调用更新前的钩子,捕获并记录异常以防止阻止更新流程。 + /// Safely invokes the pre-update hook, catching and logging exceptions to prevent blocking the update flow. /// - /// 更新上下文。 - /// 钩子调用的结果;如果钩子抛出异常则默认为 true(继续更新)。 + /// The update context. + /// The result of the hook invocation; defaults to true (continue updating) if the hook throws an exception. private async Task SafeOnBeforeUpdateAsync(Hooks.UpdateContext ctx) { try { return await Hooks.OnBeforeUpdateAsync(ctx).ConfigureAwait(false); } catch (Exception ex) { GeneralTracer.Warn($"OnBeforeUpdateAsync hook failed: {ex.Message}"); return true; } } /// - /// 安全地调用应用启动前的钩子,捕获并记录异常。 + /// Safely invokes the pre-start-app hook, catching and logging exceptions. /// - /// 更新上下文。 + /// The update context. private async Task SafeOnBeforeStartAppAsync(Hooks.UpdateContext ctx) { try { await Hooks.OnBeforeStartAppAsync(ctx).ConfigureAwait(false); } catch (Exception ex) { GeneralTracer.Warn($"OnBeforeStartAppAsync hook failed: {ex.Message}"); } } /// - /// 安全地调用更新错误钩子,捕获并记录异常。 + /// Safely invokes the update error hook, catching and logging exceptions. /// - /// 更新上下文。 - /// 更新过程中发生的异常。 + /// The update context. + /// The exception that occurred during the update. private async Task SafeOnUpdateErrorAsync(Hooks.UpdateContext ctx, Exception error) { try { await Hooks.OnUpdateErrorAsync(ctx, error).ConfigureAwait(false); } catch (Exception ex) { GeneralTracer.Warn($"OnUpdateErrorAsync hook failed: {ex.Message}"); } } /// - /// 安全地调用更新完成后的钩子,捕获并记录异常。 + /// Safely invokes the post-update hook, catching and logging exceptions. /// - /// 更新上下文。 + /// The update context. private async Task SafeOnAfterUpdateAsync(Hooks.UpdateContext ctx) { try { await Hooks.OnAfterUpdateAsync(ctx).ConfigureAwait(false); } catch (Exception ex) { GeneralTracer.Warn($"OnAfterUpdateAsync hook failed: {ex.Message}"); } } /// - /// 安全地调用下载完成钩子,捕获并记录异常。 + /// Safely invokes the download completed hook, catching and logging exceptions. /// - /// 更新上下文。 + /// The update context. private async Task SafeOnDownloadCompletedAsync(Hooks.UpdateContext ctx) { try @@ -534,9 +540,9 @@ private async Task SafeOnDownloadCompletedAsync(Hooks.UpdateContext ctx) catch (Exception ex) { GeneralTracer.Warn($"OnDownloadCompletedAsync hook failed: {ex.Message}"); } } /// - /// 安全地报告更新已开始的状态,捕获并记录异常。 + /// Safely reports the update started status, catching and logging exceptions. /// - /// 更新上下文。 + /// The update context. private async Task SafeReportUpdateStartedAsync(Hooks.UpdateContext ctx) { try @@ -546,9 +552,9 @@ private async Task SafeReportUpdateStartedAsync(Hooks.UpdateContext ctx) catch (Exception ex) { GeneralTracer.Warn($"Report UpdateStarted failed: {ex.Message}"); } } /// - /// 安全地报告更新已应用的状态,捕获并记录异常。 + /// Safely reports the update applied status, catching and logging exceptions. /// - /// 更新上下文。 + /// The update context. private async Task SafeReportUpdateAppliedAsync(Hooks.UpdateContext ctx) { try @@ -558,10 +564,10 @@ private async Task SafeReportUpdateAppliedAsync(Hooks.UpdateContext ctx) catch (Exception ex) { GeneralTracer.Warn($"Report UpdateApplied failed: {ex.Message}"); } } /// - /// 安全地报告更新失败的状态,捕获并记录异常。 + /// Safely reports the update failed status, catching and logging exceptions. /// - /// 更新上下文。 - /// 更新过程中发生的异常。 + /// The update context. + /// The exception that occurred during the update. private async Task SafeReportUpdateFailedAsync(Hooks.UpdateContext ctx, Exception error) { try diff --git a/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs index ad140687..72bf3d66 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs @@ -11,32 +11,36 @@ namespace GeneralUpdate.Core.Strategy; /// -/// 升级端更新策略。接收客户端通过加密 IPC 传递的进程信息,应用更新并启动主应用程序。 +/// Upgrade-side update strategy. Receives process information passed from the client via encrypted IPC, +/// applies updates, and launches the main application. /// /// /// -/// 本策略对应 AppType.Upgrade 角色,采用两层策略设计:上层角色策略(本类)负责编排更新流程, -/// 下层操作系统策略() -/// 负责执行具体的平台操作。 +/// This strategy serves the AppType.Upgrade role and uses a two-layer strategy design: +/// the upper role strategy (this class) handles workflow orchestration, +/// while the lower OS-level strategy (, , ) +/// handles platform-specific operations. /// /// -/// 执行流程: +/// Execution Flow: /// -/// 通过 方法接收客户端传递的 , -/// 其中包含已下载的更新包路径、哈希值等元数据。 -/// 调用 生命周期钩子, -/// 允许调用方在应用更新前执行自定义逻辑或取消操作。 -/// 委托操作系统策略执行更新管道:通过 Hash(哈希校验)→ -/// Decompress(解压缩)→ Patch(增量补丁)中间件链处理每个更新版本。 -/// 调用 钩子,通知调用方所有更新已应用完毕。 -/// 调用 钩子, -/// 允许调用方在启动主应用程序前执行额外操作(如设置可执行权限或准备资源文件)。 -/// 通过操作系统策略启动主应用程序(MainAppName)及 Bowl 辅助进程。 +/// Receives the passed from the client via the method, +/// which contains already-downloaded update package paths, hash values, and other metadata. +/// Calls the lifecycle hook, +/// allowing the caller to execute custom logic or cancel the operation before applying updates. +/// Delegates to the OS strategy to execute the update pipeline: processes each version through the +/// Hash (hash verification) → Decompress (extraction) → Patch (incremental patch) middleware chain. +/// Calls the hook to notify the caller that all updates have been applied. +/// Calls the hook, +/// allowing the caller to perform additional operations before launching the main application +/// (such as setting executable permissions or preparing resource files). +/// Launches the main application (MainAppName) and the Bowl helper process through the OS strategy. /// /// /// -/// 设计要点:升级端不执行版本验证或下载操作。客户端已完成所有网络请求和下载任务, -/// 并通过进程信息传递结果。升级端仅负责应用更新和启动应用程序——零网络开销。 +/// Design Note: The upgrade side does not perform version validation or download operations. +/// The client has already completed all network requests and downloads, passing results through process information. +/// The upgrade side is responsible only for applying updates and launching the application -- zero network overhead. /// /// public class UpgradeUpdateStrategy : IStrategy @@ -46,27 +50,28 @@ public class UpgradeUpdateStrategy : IStrategy private IStrategy? _customOsStrategy; /// - /// 获取或设置生命周期钩子。由引导程序注入,用于在更新流程的关键节点执行自定义逻辑。 + /// Gets or sets the lifecycle hooks. Injected by the bootstrap to execute custom logic at key points in the update flow. /// public Hooks.IUpdateHooks Hooks { get; set; } = new Hooks.NoOpUpdateHooks(); /// - /// 获取或设置更新状态报告器。由引导程序注入,负责向服务器或调用方报告更新进度和结果。 + /// Gets or sets the update status reporter. Injected by the bootstrap to report update progress and results to the server or caller. /// public Download.Reporting.IUpdateReporter Reporter { get; set; } = new Download.Reporting.NoOpUpdateReporter(); /// - /// 设置自定义操作系统级别策略(通过 .Strategy<T>() 注入)。 - /// 设置后将替换 中的自动平台检测逻辑。 + /// Sets a custom OS-level strategy (injected via .Strategy<T>()). + /// When set, replaces the automatic platform detection logic in . /// public void SetOsStrategy(IStrategy? strategy) => _customOsStrategy = strategy; /// - /// 初始化升级端策略。接收客户端传递的全局配置信息,并解析当前操作系统对应的策略实例。 + /// Initializes the upgrade-side strategy. Receives the global configuration information passed from the client + /// and resolves the strategy instance for the current operating system. /// - /// 全局配置信息,包含更新包路径、哈希值、版本信息等。 - /// 为 null 时抛出。 - /// 当前操作系统不受支持时由 抛出。 + /// Global configuration information containing update package paths, hash values, version information, etc. + /// Thrown when is null. + /// Thrown by when the current OS is not supported. public void Create(GlobalConfigInfo parameter) { _configInfo = parameter ?? throw new ArgumentNullException(nameof(parameter)); @@ -78,31 +83,33 @@ public void Create(GlobalConfigInfo parameter) } /// - /// 执行升级端更新流程。按照生命周期顺序依次执行:更新前钩子、操作系统更新管道、更新后钩子、启动前钩子、启动主应用。 + /// Executes the upgrade-side update flow. Follows the lifecycle order: pre-update hook, OS update pipeline, + /// post-update hook, pre-start-app hook, and main application launch. /// /// /// - /// 执行流程详解: + /// Detailed Execution Flow: /// - /// OnBeforeUpdate 钩子:调用 , - /// 如果返回 false 则取消本次更新。 - /// 操作系统更新管道:_configInfo.UpdateVersions 传递给 - /// ,由 OS 策略逐一处理每个版本的更新包 - /// (Hash 校验 → Decompress 解压 → Patch 补丁应用)。 - /// OnAfterUpdate 钩子:调用 , - /// 通知调用方所有更新已应用完成。 - /// 报告更新成功:通过 - /// 报告更新成功状态。 - /// OnBeforeStartApp 钩子:调用 , - /// 允许调用方在启动应用前执行额外操作(如设置可执行权限)。 - /// 启动应用:LaunchClientAfterUpdatetrue 时, - /// 通过 OS 策略启动主应用程序(MainAppName)及 Bowl 辅助进程。 + /// OnBeforeUpdate Hook: Calls . + /// If it returns false, the update is cancelled. + /// OS Update Pipeline: Passes _configInfo.UpdateVersions to + /// , where the OS strategy processes each version's update package + /// (Hash verification → Decompress extraction → Patch application). + /// OnAfterUpdate Hook: Calls + /// to notify the caller that all updates have been applied. + /// Report Success: Reports the update success status via + /// . + /// OnBeforeStartApp Hook: Calls , + /// allowing the caller to perform additional operations before launching the application + /// (such as setting executable permissions). + /// Launch Application: When LaunchClientAfterUpdate is true, + /// launches the main application (MainAppName) and the Bowl helper process via the OS strategy. /// /// /// - /// 异常处理:整个流程中任何异常均会被 try-catch 捕获,依次触发 - /// 钩子、报告更新失败状态、 - /// 记录错误日志并通过 分发异常事件。 + /// Exception Handling: Any exception in the entire flow is caught by the try-catch block, + /// which sequentially triggers , reports the update failure status, + /// logs the error, and dispatches the exception event via . /// /// public async Task ExecuteAsync() @@ -172,7 +179,14 @@ public async Task ExecuteAsync() private DiffPipeline? _pendingDiffPipeline; - /// Sets the DiffPipeline on the underlying OS-level strategy for parallel patch application. + /// + /// Sets the differential patch pipeline on the underlying OS-level strategy for parallel patch application. + /// + /// The differential pipeline instance. If null, clears the pending pipeline. + /// + /// If the OS strategy is not yet initialized, the pipeline is stored in a pending field + /// and passed to the OS strategy's DiffPipeline property when is called. + /// public void SetDiffPipeline(DiffPipeline? diffPipeline) { if (_osStrategy is AbstractStrategy abs) @@ -182,10 +196,10 @@ public void SetDiffPipeline(DiffPipeline? diffPipeline) } /// - /// 启动主应用程序。委托给底层操作系统策略执行平台相关的应用启动逻辑。 + /// Starts the main application. Delegates to the underlying OS strategy for platform-specific application launch logic. /// /// - /// 此方法由外部调用(如 Bowl 进程),用于在升级完成后启动主应用。 + /// This method is called externally (e.g., by the Bowl process) to start the main application after the upgrade completes. /// public async Task StartAppAsync() { diff --git a/src/c#/GeneralUpdate.Core/Strategy/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/WindowsStrategy.cs index a39cbf42..472f3424 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/WindowsStrategy.cs @@ -10,38 +10,40 @@ namespace GeneralUpdate.Core.Strategy { /// - /// Windows 平台专用的更新策略。 - /// 实现了针对 Windows 操作系统的更新流程,包括管道构建、哈希校验、解压缩和应用补丁。 + /// Windows platform-specific update strategy. + /// Implements the update flow for the Windows operating system, including pipeline building, + /// hash verification, decompression, and patch application. /// /// /// - /// 此类继承自 AbstractStrategy,提供了 Windows 环境下的完整更新生命周期管理。 + /// This class inherits from AbstractStrategy and provides complete update lifecycle management for the Windows environment. /// /// - /// 核心流程: + /// Core flow: /// - /// BuildPipeline — 构建中间件管道,按顺序执行哈希校验→解压缩→(可选)应用补丁。 - /// CreatePipelineContext — 创建包含版本信息和补丁路径的管道上下文。 - /// StartAppAsync — 启动已更新的主应用程序(如果配置了 Bowl 辅助进程,也会同时启动), - /// 然后释放跟踪器并优雅退出当前更新程序进程。 + /// BuildPipeline — Builds the middleware pipeline, executing hash verification, decompression, + /// and (optionally) patch application in order. + /// CreatePipelineContext — Creates the pipeline context containing version information and patch path. + /// StartAppAsync — Starts the updated main application (and the Bowl helper process if configured), + /// then releases the tracer and gracefully exits the current updater process. /// /// /// - /// 补丁功能由 PatchEnabled 配置控制。当启用时,管道会包含 PatchMiddleware; - /// 禁用时则跳过该步骤。 + /// The patching feature is controlled by the PatchEnabled configuration. When enabled, the pipeline + /// includes PatchMiddleware; when disabled, this step is skipped. /// /// public class WindowsStrategy : AbstractStrategy { /// - /// 创建管道上下文,包含目标版本信息和补丁路径。 + /// Creates the pipeline context containing target version information and patch path. /// - /// 目标版本信息。 - /// 补丁文件的路径。 - /// 包含版本信息和补丁路径的 PipelineContext 实例。 + /// The target version information. + /// The path to the patch files. + /// A PipelineContext instance containing version information and patch path. /// - /// 此方法被 AbstractStrategy 中的管道执行流程调用。 - /// 它会记录版本号和补丁路径,然后调用基类的 CreatePipelineContext 方法创建上下文对象。 + /// This method is called by the pipeline execution flow in AbstractStrategy. + /// It logs the version number and patch path, then calls the base class's CreatePipelineContext method to create the context object. /// protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) { @@ -50,18 +52,19 @@ protected override PipelineContext CreatePipelineContext(VersionInfo version, st } /// - /// 构建 Windows 平台的更新中间件管道。 + /// Builds the Windows platform update middleware pipeline. /// - /// 管道上下文,包含版本和补丁信息。 - /// 配置好的 PipelineBuilder 实例,包含哈希校验、解压缩和(可选)补丁中间件。 + /// The pipeline context containing version and patch information. + /// A configured PipelineBuilder instance containing hash verification, decompression, + /// and (optionally) patch middleware. /// /// - /// 管道按以下顺序组装中间件: + /// The pipeline assembles middleware in the following order: /// /// - /// HashMiddleware — 计算并验证文件哈希,确保数据完整性。 - /// CompressMiddleware — 解压缩下载的更新包。 - /// PatchMiddleware — (可选)应用二进制补丁。仅在 _configinfo.PatchEnabled 为 true 时启用。 + /// HashMiddleware — Computes and verifies file hashes to ensure data integrity. + /// CompressMiddleware — Decompresses the downloaded update package. + /// PatchMiddleware — (Optional) Applies binary patches. Only enabled when _configinfo.PatchEnabled is true. /// /// protected override PipelineBuilder BuildPipeline(PipelineContext context) @@ -75,23 +78,23 @@ protected override PipelineBuilder BuildPipeline(PipelineContext context) } /// - /// 异步启动已更新的主应用程序,然后退出当前更新程序进程。 + /// Asynchronously starts the updated main application, then exits the current updater process. /// - /// 表示异步操作的任务。 + /// A task that represents the asynchronous operation. /// /// - /// 此方法在更新流程完成后调用。执行步骤如下: + /// This method is called after the update process completes. The execution steps are as follows: /// /// - /// 使用 LaunchAppName 属性获取主应用程序名称,如果未设置则抛出异常。 - /// 调用 ResolveAppPath 解析应用程序的完整路径。 - /// 使用 Process.Start 启动主应用程序。 - /// 如果 LaunchBowl 为 true,还会启动 Bowl 辅助进程(用于界面交互或状态监控)。 - /// 释放 GeneralTracer 资源。 - /// 调用 GracefulExit.CurrentProcessAsync() 优雅终止更新程序进程。 + /// Uses the LaunchAppName property to get the main application name; throws if not set. + /// Calls ResolveAppPath to resolve the full path of the application. + /// Starts the main application using Process.Start. + /// If LaunchBowl is true, also starts the Bowl helper process (for UI interaction or status monitoring). + /// Disposes the GeneralTracer resources. + /// Calls GracefulExit.CurrentProcessAsync() to gracefully terminate the updater process. /// /// - /// 任何异常都会被 EventManager 捕获并分发为 ExceptionEventArgs 事件。 + /// Any exception is caught and dispatched as an ExceptionEventArgs event via EventManager. /// /// public override async Task StartAppAsync()