diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralClientBootstrap.cs
index 24aca154..efaa354b 100644
--- a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralClientBootstrap.cs
+++ b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralClientBootstrap.cs
@@ -21,7 +21,8 @@ namespace GeneralUpdate.Core;
///
/// This component is used only for client application bootstrapping classes.
///
-public class GeneralClientBootstrap : AbstractBootstrap
+[Obsolete("Use GeneralUpdateBootstrap with Option(UpdateOptions.AppType, AppType.ClientApp) instead. This class will be removed in v11. See migration guide at https://github.com/GeneralLibrary/GeneralUpdate/issues/344")]
+ public class GeneralClientBootstrap : AbstractBootstrap
{
///
/// All update actions of the core object for automatic upgrades will be related to the packet object.
diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs
index fc26a55e..d8f4a7cf 100644
--- a/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs
+++ b/src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs
@@ -9,348 +9,558 @@
using System.Threading.Tasks;
using GeneralUpdate.Core.FileSystem;
using GeneralUpdate.Core.Download;
-using GeneralUpdate.Core;
using GeneralUpdate.Core.Event;
using GeneralUpdate.Core.Configuration;
using GeneralUpdate.Core.JsonContext;
using GeneralUpdate.Core.Strategy;
using GeneralUpdate.Core.Network;
-namespace GeneralUpdate.Core
+namespace GeneralUpdate.Core;
+
+///
+/// Unified update bootstrap — single entry point for both Client and Upgrade roles.
+/// Use to select the workflow:
+///
+/// - — validate versions, download, start upgrade process
+/// - — receive ProcessInfo, apply updates, start main app
+///
+///
+///
+/// Migration from GeneralClientBootstrap:
+/// Replace new GeneralClientBootstrap().SetConfig(cfg).LaunchAsync() with
+/// new GeneralUpdateBootstrap().Option(UpdateOptions.AppType, AppType.ClientApp).SetConfig(cfg).LaunchAsync().
+///
+public class GeneralUpdateBootstrap : AbstractBootstrap
{
- public class GeneralUpdateBootstrap : AbstractBootstrap
- {
- private GlobalConfigInfo _configInfo = new();
- private IStrategy? _strategy;
- private Func? _customSkipOption;
+ private GlobalConfigInfo _configInfo = new();
+ private IStrategy? _strategy;
+ private Func? _customSkipOption;
+ private Func? _updatePrecheck;
+ private readonly List> _customOptions = new();
- public GeneralUpdateBootstrap()
- {
- InitializeFromEnvironment();
- }
+ public GeneralUpdateBootstrap()
+ {
+ InitializeFromEnvironment();
+ }
- #region Launch
+ // ════════════════════════════════════════════════════════════════
+ // Launch — AppType dispatch
+ // ════════════════════════════════════════════════════════════════
- public override async Task LaunchAsync()
+ public override async Task LaunchAsync()
+ {
+ int appType = GetOption(UpdateOptions.AppType);
+ return appType switch
{
- GeneralTracer.Debug("GeneralUpdateBootstrap Launch.");
- StrategyFactory();
-
- switch (GetOption(UpdateOption.Mode) ?? UpdateMode.Default)
- {
- case UpdateMode.Default:
- GeneralTracer.Info("GeneralUpdateBootstrap.LaunchAsync: Default mode - applying runtime options, creating strategy, downloading and executing.");
- ApplyRuntimeOptions();
- _strategy!.Create(_configInfo);
- await DownloadAsync();
- await _strategy.ExecuteAsync();
- GeneralTracer.Info("GeneralUpdateBootstrap.LaunchAsync: Default mode execution completed.");
- break;
+ AppType.ClientApp => await LaunchClientAsync(),
+ AppType.UpgradeApp => await LaunchUpgradeAsync(),
+ _ => await LaunchClientAsync() // default to Client for backward compatibility
+ };
+ }
- case UpdateMode.Scripts:
- GeneralTracer.Info("GeneralUpdateBootstrap.LaunchAsync: Scripts mode - executing workflow.");
- await ExecuteWorkflowAsync();
- break;
+ /// Client workflow: validate versions, download, start upgrade process.
+ private async Task LaunchClientAsync()
+ {
+ try
+ {
+ GeneralTracer.Debug("GeneralUpdateBootstrap.LaunchClientAsync start.");
+ CallSmallBowlHome(_configInfo.Bowl);
+ ExecuteCustomOptions();
+ await ExecuteClientWorkflowAsync();
+ }
+ catch (Exception ex)
+ {
+ GeneralTracer.Error("LaunchClientAsync threw an exception.", ex);
+ EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, ex.Message));
+ }
+ return this;
+ }
- default:
- throw new ArgumentOutOfRangeException();
- }
+ /// Upgrade workflow: receive ProcessInfo, apply updates, start main app.
+ private async Task LaunchUpgradeAsync()
+ {
+ GeneralTracer.Debug("GeneralUpdateBootstrap.LaunchUpgradeAsync start.");
+ StrategyFactory();
- return this;
+ switch (GetOption(UpdateOption.Mode) ?? UpdateMode.Default)
+ {
+ case UpdateMode.Default:
+ ApplyRuntimeOptions();
+ _strategy!.Create(_configInfo);
+ await DownloadAsync();
+ await _strategy.ExecuteAsync();
+ break;
+ case UpdateMode.Scripts:
+ await ExecuteUpgradeWorkflowAsync();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
}
- #endregion
+ return this;
+ }
+
+ // ════════════════════════════════════════════════════════════════
+ // Configuration
+ // ════════════════════════════════════════════════════════════════
- #region Configuration
+ public GeneralUpdateBootstrap SetConfig(Configinfo configInfo)
+ {
+ _configInfo = ConfigurationMapper.MapToGlobalConfigInfo(configInfo);
- ///
- /// Configure the update bootstrap with user-provided configuration.
- /// Uses ConfigurationMapper to ensure consistent field mapping and reduce maintenance burden.
- ///
- /// User-provided configuration containing update parameters
- /// This bootstrap instance for method chaining
- public GeneralUpdateBootstrap SetConfig(Configinfo configInfo)
+ var appType = GetOption(UpdateOptions.AppType);
+ if (appType != AppType.UpgradeApp)
{
- // Use ConfigurationMapper instead of manual field mapping
- // This ensures all fields are consistently mapped and reduces maintenance burden
- _configInfo = ConfigurationMapper.MapToGlobalConfigInfo(configInfo);
-
- // Set runtime-specific values that are not part of user configuration
_configInfo.TempPath = StorageManager.GetTempDirectory("upgrade_temp");
_configInfo.DriveEnabled = GetOption(UpdateOption.Drive) ?? false;
_configInfo.PatchEnabled = GetOption(UpdateOption.Patch) ?? true;
-
InitBlackList();
- return this;
- }
-
- public GeneralUpdateBootstrap SetCustomSkipOption(Func? func)
- {
- _customSkipOption = func;
- return this;
}
- #endregion
+ return this;
+ }
+
+ public GeneralUpdateBootstrap SetCustomSkipOption(Func? func)
+ {
+ _customSkipOption = func;
+ return this;
+ }
+
+ ///
+ /// Registers a callback invoked when update information is available.
+ /// Returns true to skip the update; false to proceed.
+ /// Forced-update protection still applies — the callback return value is
+ /// ignored if any version is marked as forcibly required.
+ ///
+ public GeneralUpdateBootstrap AddListenerUpdatePrecheck(Func func)
+ {
+ _updatePrecheck = func ?? throw new ArgumentNullException(nameof(func));
+ return this;
+ }
- #region Workflow
+ ///
+ /// Add custom operations to execute before the update workflow.
+ /// Recommended for environment checks to ensure dependencies are available after update.
+ ///
+ public GeneralUpdateBootstrap AddCustomOption(List> funcList)
+ {
+ Debug.Assert(funcList != null && funcList.Any());
+ _customOptions.AddRange(funcList);
+ return this;
+ }
+
+ // ════════════════════════════════════════════════════════════════
+ // Client Workflow
+ // ════════════════════════════════════════════════════════════════
- private async Task ExecuteWorkflowAsync()
+ private async Task ExecuteClientWorkflowAsync()
+ {
+ try
{
- try
+ Debug.Assert(_configInfo != null);
+
+ // Silent mode
+ if (GetOption(UpdateOption.EnableSilentUpdate))
{
- GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: validating version against server. UpdateUrl={_configInfo.UpdateUrl}, ClientVersion={_configInfo.ClientVersion}");
- var mainResp = await VersionService.Validate(
- _configInfo.UpdateUrl,
- _configInfo.ClientVersion,
- AppType.ClientApp,
- _configInfo.AppSecretKey,
- GetPlatform(),
- _configInfo.ProductId,
- _configInfo.Scheme,
- _configInfo.Token);
-
- _configInfo.IsMainUpdate = CheckUpgrade(mainResp);
- GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: version validation completed. IsMainUpdate={_configInfo.IsMainUpdate}, ResponseCode={mainResp?.Code}");
-
- EventManager.Instance.Dispatch(this, new UpdateInfoEventArgs(mainResp));
-
- if (CanSkip(CheckForcibly(mainResp.Body)))
- {
- GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: update skipped by custom skip option.");
- return;
- }
+ GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteClientWorkflowAsync: silent mode, delegating to SilentUpdateMode.");
+ await new SilentUpdateMode(
+ _configInfo,
+ GetOption(UpdateOption.Encoding) ?? Encoding.Default,
+ GetOption(UpdateOption.Format) ?? Format.ZIP,
+ GetOption(UpdateOption.DownloadTimeOut) ?? 60,
+ GetOption(UpdateOption.Patch) ?? true,
+ GetOption(UpdateOption.BackUp) ?? true).StartAsync();
+ return;
+ }
- InitBlackList();
- ApplyRuntimeOptions();
+ // Dual version validation
+ GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteClientWorkflowAsync: validating client={_configInfo.ClientVersion}, upgrade={_configInfo.UpgradeClientVersion}");
+ var mainResp = await VersionService.Validate(_configInfo.UpdateUrl
+ , _configInfo.ClientVersion, AppType.ClientApp, _configInfo.AppSecretKey
+ , GetPlatform(), _configInfo.ProductId, _configInfo.Scheme, _configInfo.Token);
- _configInfo.TempPath = StorageManager.GetTempDirectory("main_temp");
- _configInfo.BackupDirectory = Path.Combine(
- _configInfo.InstallPath,
- $"{StorageManager.DirectoryName}{_configInfo.ClientVersion}");
+ var upgradeResp = await VersionService.Validate(_configInfo.UpdateUrl
+ , _configInfo.UpgradeClientVersion, AppType.UpgradeApp, _configInfo.AppSecretKey
+ , GetPlatform(), _configInfo.ProductId, _configInfo.Scheme, _configInfo.Token);
- _configInfo.UpdateVersions = mainResp.Body!
- .OrderBy(x => x.ReleaseDate)
- .ToList();
+ _configInfo.IsUpgradeUpdate = CheckUpgrade(upgradeResp);
+ _configInfo.IsMainUpdate = CheckUpgrade(mainResp);
+ GeneralTracer.Info($"ExecuteClientWorkflowAsync: IsMainUpdate={_configInfo.IsMainUpdate}, IsUpgradeUpdate={_configInfo.IsUpgradeUpdate}");
- GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: {_configInfo.UpdateVersions.Count} version(s) queued for update.");
+ var updateInfoArgs = new UpdateInfoEventArgs(mainResp);
+ EventManager.Instance.Dispatch(this, updateInfoArgs);
- if (GetOption(UpdateOption.BackUp) ?? true)
- {
- GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: backing up from {_configInfo.InstallPath} to {_configInfo.BackupDirectory}");
- StorageManager.Backup(
- _configInfo.InstallPath,
- _configInfo.BackupDirectory,
- BlackListManager.Instance.SkipDirectorys);
- GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: backup completed.");
- }
+ var isForcibly = CheckForcibly(mainResp.Body) || CheckForcibly(upgradeResp.Body);
+ if (CanSkipClient(isForcibly, updateInfoArgs))
+ {
+ GeneralTracer.Info("ExecuteClientWorkflowAsync: update skipped by precheck callback.");
+ return;
+ }
- _strategy!.Create(_configInfo);
+ InitBlackList();
+ ApplyRuntimeOptions();
- if (_configInfo.IsMainUpdate)
- {
- GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: main update required, starting download and execution.");
- await DownloadAsync();
- await _strategy.ExecuteAsync();
- GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: main update execution completed.");
- }
- else
+ _configInfo.TempPath = StorageManager.GetTempDirectory("main_temp");
+ _configInfo.BackupDirectory = Path.Combine(_configInfo.InstallPath,
+ $"{StorageManager.DirectoryName}{_configInfo.ClientVersion}");
+
+ _configInfo.UpdateVersions = _configInfo.IsUpgradeUpdate
+ ? upgradeResp.Body.OrderBy(x => x.ReleaseDate).ToList()
+ : new List();
+
+ if (_configInfo.IsMainUpdate)
+ {
+ _configInfo.LastVersion = mainResp.Body.OrderBy(x => x.ReleaseDate).Last().Version;
+ GeneralTracer.Info($"ExecuteClientWorkflowAsync: main update, LastVersion={_configInfo.LastVersion}");
+
+ var failed = CheckFail(_configInfo.LastVersion);
+ if (failed)
{
- GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: no main update needed, starting application directly.");
- _strategy.StartApp();
+ GeneralTracer.Warn($"ExecuteClientWorkflowAsync: version {_configInfo.LastVersion} matches known-failed upgrade, aborting.");
+ return;
}
+
+ var processInfo = ConfigurationMapper.MapToProcessInfo(
+ _configInfo, mainResp.Body,
+ BlackListManager.Instance.BlackFormats.ToList(),
+ BlackListManager.Instance.BlackFiles.ToList(),
+ BlackListManager.Instance.SkipDirectorys.ToList());
+
+ _configInfo.ProcessInfo = JsonSerializer.Serialize(
+ processInfo, ProcessInfoJsonContext.Default.ProcessInfo);
}
- catch (Exception ex)
+
+ if (GetOption(UpdateOption.BackUp) ?? true)
{
- GeneralTracer.Error(
- "The ExecuteWorkflowAsync method in the GeneralUpdateBootstrap class throws an exception.",
- ex);
- EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, ex.Message));
+ GeneralTracer.Info($"ExecuteClientWorkflowAsync: backing up {_configInfo.InstallPath} -> {_configInfo.BackupDirectory}");
+ StorageManager.Backup(_configInfo.InstallPath, _configInfo.BackupDirectory,
+ BlackListManager.Instance.SkipDirectorys);
}
- }
- #endregion
+ StrategyFactory();
+ GeneralTracer.Info($"ExecuteClientWorkflowAsync: IsUpgradeUpdate={_configInfo.IsUpgradeUpdate}, IsMainUpdate={_configInfo.IsMainUpdate}");
+
+ switch (_configInfo.IsUpgradeUpdate)
+ {
+ case true when _configInfo.IsMainUpdate:
+ GeneralTracer.Info("ExecuteClientWorkflowAsync: both upgrade+main — downloading and executing.");
+ await DownloadAsync();
+ await _strategy!.ExecuteAsync();
+ _strategy.StartApp();
+ break;
+ case true when !_configInfo.IsMainUpdate:
+ GeneralTracer.Info("ExecuteClientWorkflowAsync: upgrade-only — downloading and executing.");
+ await DownloadAsync();
+ await _strategy!.ExecuteAsync();
+ break;
+ case false when _configInfo.IsMainUpdate:
+ GeneralTracer.Info("ExecuteClientWorkflowAsync: main-only — starting updater.");
+ _strategy!.StartApp();
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ GeneralTracer.Error("ExecuteClientWorkflowAsync threw an exception.", ex);
+ EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, ex.Message));
+ }
+ }
- #region Download
+ // ════════════════════════════════════════════════════════════════
+ // Upgrade Workflow
+ // ════════════════════════════════════════════════════════════════
- private async Task DownloadAsync()
+ private async Task ExecuteUpgradeWorkflowAsync()
+ {
+ try
{
- var manager = new DownloadManager(
- _configInfo.TempPath,
- _configInfo.Format,
- _configInfo.DownloadTimeOut);
+ GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteUpgradeWorkflowAsync: validating version. UpdateUrl={_configInfo.UpdateUrl}, ClientVersion={_configInfo.ClientVersion}");
+ var mainResp = await VersionService.Validate(
+ _configInfo.UpdateUrl, _configInfo.ClientVersion,
+ AppType.ClientApp, _configInfo.AppSecretKey,
+ GetPlatform(), _configInfo.ProductId,
+ _configInfo.Scheme, _configInfo.Token);
- manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted;
- manager.MultiDownloadCompleted += OnMultiDownloadCompleted;
- manager.MultiDownloadError += OnMultiDownloadError;
- manager.MultiDownloadStatistics += OnMultiDownloadStatistics;
+ _configInfo.IsMainUpdate = CheckUpgrade(mainResp);
+ GeneralTracer.Info($"ExecuteUpgradeWorkflowAsync: IsMainUpdate={_configInfo.IsMainUpdate}");
- foreach (var version in _configInfo.UpdateVersions)
- manager.Add(new DownloadTask(manager, version));
+ EventManager.Instance.Dispatch(this, new UpdateInfoEventArgs(mainResp));
- await manager.LaunchTasksAsync();
- }
+ if (CanSkip(CheckForcibly(mainResp.Body)))
+ {
+ GeneralTracer.Info("ExecuteUpgradeWorkflowAsync: update skipped.");
+ return;
+ }
- #endregion
+ InitBlackList();
+ ApplyRuntimeOptions();
- #region Helpers
+ _configInfo.TempPath = StorageManager.GetTempDirectory("main_temp");
+ _configInfo.BackupDirectory = Path.Combine(
+ _configInfo.InstallPath,
+ $"{StorageManager.DirectoryName}{_configInfo.ClientVersion}");
- private void InitializeFromEnvironment()
- {
- var json = Environments.GetEnvironmentVariable("ProcessInfo");
- if (string.IsNullOrWhiteSpace(json)) return;
+ _configInfo.UpdateVersions = mainResp.Body!
+ .OrderBy(x => x.ReleaseDate).ToList();
- var processInfo = JsonSerializer.Deserialize(
- json,
- ProcessInfoJsonContext.Default.ProcessInfo);
+ GeneralTracer.Info($"ExecuteUpgradeWorkflowAsync: {_configInfo.UpdateVersions.Count} version(s) queued.");
- if (processInfo == null) return;
+ if (GetOption(UpdateOption.BackUp) ?? true)
+ {
+ GeneralTracer.Info($"ExecuteUpgradeWorkflowAsync: backing up {_configInfo.InstallPath} -> {_configInfo.BackupDirectory}");
+ StorageManager.Backup(
+ _configInfo.InstallPath, _configInfo.BackupDirectory,
+ BlackListManager.Instance.SkipDirectorys);
+ }
- BlackListManager.Instance.AddBlackFormats(processInfo.BlackFileFormats);
- BlackListManager.Instance.AddBlackFiles(processInfo.BlackFiles);
- BlackListManager.Instance.AddSkipDirectorys(processInfo.SkipDirectorys);
+ _strategy!.Create(_configInfo);
- _configInfo = new GlobalConfigInfo
+ if (_configInfo.IsMainUpdate)
{
- MainAppName = processInfo.AppName,
- InstallPath = processInfo.InstallPath,
- ClientVersion = processInfo.CurrentVersion,
- LastVersion = processInfo.LastVersion,
- UpdateLogUrl = processInfo.UpdateLogUrl,
- Encoding = Encoding.GetEncoding(processInfo.CompressEncoding),
- Format = processInfo.CompressFormat,
- DownloadTimeOut = processInfo.DownloadTimeOut,
- AppSecretKey = processInfo.AppSecretKey,
- UpdateVersions = processInfo.UpdateVersions,
- TempPath = StorageManager.GetTempDirectory("upgrade_temp"),
- ReportUrl = processInfo.ReportUrl,
- BackupDirectory = processInfo.BackupDirectory,
- Scheme = processInfo.Scheme,
- Token = processInfo.Token,
- DriveEnabled = GetOption(UpdateOption.Drive) ?? false,
- PatchEnabled = GetOption(UpdateOption.Patch) ?? true,
- Script = processInfo.Script,
- DriverDirectory = processInfo.DriverDirectory
- };
+ GeneralTracer.Info("ExecuteUpgradeWorkflowAsync: main update required, starting download and execution.");
+ await DownloadAsync();
+ await _strategy.ExecuteAsync();
+ }
+ else
+ {
+ GeneralTracer.Info("ExecuteUpgradeWorkflowAsync: no update needed, starting application.");
+ _strategy.StartApp();
+ }
}
-
- private void ApplyRuntimeOptions()
+ catch (Exception ex)
{
- _configInfo.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default;
- _configInfo.Format = GetOption(UpdateOption.Format) ?? Format.ZIP;
- _configInfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) ?? 60;
- _configInfo.DriveEnabled = GetOption(UpdateOption.Drive) ?? false;
- _configInfo.PatchEnabled = GetOption(UpdateOption.Patch) ?? true;
+ GeneralTracer.Error("ExecuteUpgradeWorkflowAsync threw an exception.", ex);
+ EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, ex.Message));
}
+ }
- private void InitBlackList()
- {
- BlackListManager.Instance.AddBlackFiles(_configInfo.BlackFiles);
- BlackListManager.Instance.AddBlackFormats(_configInfo.BlackFormats);
- BlackListManager.Instance.AddSkipDirectorys(_configInfo.SkipDirectorys);
- }
+ // ════════════════════════════════════════════════════════════════
+ // Download
+ // ════════════════════════════════════════════════════════════════
- private bool CanSkip(bool isForcibly)
- {
- if (isForcibly)
- {
- return false;
- }
+ private async Task DownloadAsync()
+ {
+ var manager = new DownloadManager(
+ _configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut);
- // Treat a null custom skip option as "do not skip".
- if (_customSkipOption is null)
- {
- return false;
- }
+ manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted;
+ manager.MultiDownloadCompleted += OnMultiDownloadCompleted;
+ manager.MultiDownloadError += OnMultiDownloadError;
+ manager.MultiDownloadStatistics += OnMultiDownloadStatistics;
- return _customSkipOption();
- }
- private static bool CheckUpgrade(VersionRespDTO? response)
- => response?.Code == 200 && response.Body?.Count > 0;
+ foreach (var version in _configInfo.UpdateVersions)
+ manager.Add(new DownloadTask(manager, version));
- private static bool CheckForcibly(IEnumerable? versions)
- => versions?.Any(v => v.IsForcibly == true) == true;
+ await manager.LaunchTasksAsync();
+ }
- private static int GetPlatform()
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return PlatformType.Windows;
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return PlatformType.Linux;
- throw new PlatformNotSupportedException("The current operating system is not supported!");
- }
+ // ════════════════════════════════════════════════════════════════
+ // Helpers
+ // ════════════════════════════════════════════════════════════════
+
+ private void InitializeFromEnvironment()
+ {
+ var json = Environments.GetEnvironmentVariable("ProcessInfo");
+ if (string.IsNullOrWhiteSpace(json)) return;
- #endregion
+ var processInfo = JsonSerializer.Deserialize(
+ json, ProcessInfoJsonContext.Default.ProcessInfo);
+ if (processInfo == null) return;
- #region Strategy & Events
+ BlackListManager.Instance.AddBlackFormats(processInfo.BlackFileFormats);
+ BlackListManager.Instance.AddBlackFiles(processInfo.BlackFiles);
+ BlackListManager.Instance.AddSkipDirectorys(processInfo.SkipDirectorys);
- protected override GeneralUpdateBootstrap StrategyFactory()
+ _configInfo = new GlobalConfigInfo
{
- _strategy = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
- ? new WindowsStrategy()
- : RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
- ? new LinuxStrategy()
- : throw new PlatformNotSupportedException("The current operating system is not supported!");
+ MainAppName = processInfo.AppName,
+ InstallPath = processInfo.InstallPath,
+ ClientVersion = processInfo.CurrentVersion,
+ LastVersion = processInfo.LastVersion,
+ UpdateLogUrl = processInfo.UpdateLogUrl,
+ Encoding = Encoding.GetEncoding(processInfo.CompressEncoding),
+ Format = processInfo.CompressFormat,
+ DownloadTimeOut = processInfo.DownloadTimeOut,
+ AppSecretKey = processInfo.AppSecretKey,
+ UpdateVersions = processInfo.UpdateVersions,
+ TempPath = StorageManager.GetTempDirectory("upgrade_temp"),
+ ReportUrl = processInfo.ReportUrl,
+ BackupDirectory = processInfo.BackupDirectory,
+ Scheme = processInfo.Scheme,
+ Token = processInfo.Token,
+ DriveEnabled = GetOption(UpdateOption.Drive) ?? false,
+ PatchEnabled = GetOption(UpdateOption.Patch) ?? true,
+ Script = processInfo.Script,
+ DriverDirectory = processInfo.DriverDirectory
+ };
+ }
- return this;
- }
+ private void ApplyRuntimeOptions()
+ {
+ _configInfo.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default;
+ _configInfo.Format = GetOption(UpdateOption.Format) ?? Format.ZIP;
+ _configInfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) ?? 60;
+ _configInfo.DriveEnabled = GetOption(UpdateOption.Drive) ?? false;
+ _configInfo.PatchEnabled = GetOption(UpdateOption.Patch) ?? true;
+ }
+
+ private void InitBlackList()
+ {
+ BlackListManager.Instance.AddBlackFiles(_configInfo.BlackFiles);
+ BlackListManager.Instance.AddBlackFormats(_configInfo.BlackFormats);
+ BlackListManager.Instance.AddSkipDirectorys(_configInfo.SkipDirectorys);
+ }
- protected override Task ExecuteStrategyAsync() => throw new NotImplementedException();
- protected override void ExecuteStrategy() => throw new NotImplementedException();
+ private bool CanSkip(bool isForcibly)
+ {
+ if (isForcibly) return false;
+ return _customSkipOption?.Invoke() == true;
+ }
- private GeneralUpdateBootstrap AddListener(Action