Skip to content

Commit d3ed677

Browse files
CopilotJusterZhu
andauthored
feat: add encapsulated silent update mode entry path
Agent-Logs-Url: https://github.com/GeneralLibrary/GeneralUpdate/sessions/44d443fb-0e3e-420e-8e14-98bb6a47d8ad Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
1 parent c3cfc4e commit d3ed677

3 files changed

Lines changed: 203 additions & 2 deletions

File tree

src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ private async Task ExecuteWorkflowAsync()
128128
try
129129
{
130130
Debug.Assert(_configInfo != null);
131+
if (GetOption(UpdateOption.EnableSilentUpdate))
132+
{
133+
await new SilentUpdateMode(
134+
_configInfo,
135+
GetOption(UpdateOption.Encoding) ?? Encoding.Default,
136+
GetOption(UpdateOption.Format) ?? Format.ZIP,
137+
GetOption(UpdateOption.DownloadTimeOut) ?? 60,
138+
GetOption(UpdateOption.Patch) ?? true,
139+
GetOption(UpdateOption.BackUp) ?? true).StartAsync();
140+
return;
141+
}
131142
//Request the upgrade information needed by the client and upgrade end, and determine if an upgrade is necessary.
132143
var mainResp = await VersionService.Validate(_configInfo.UpdateUrl
133144
, _configInfo.ClientVersion
@@ -423,4 +434,4 @@ private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadComplete
423434
}
424435

425436
#endregion Private Methods
426-
}
437+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.InteropServices;
6+
using System.Text;
7+
using System.Text.Json;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using GeneralUpdate.ClientCore.Strategys;
11+
using GeneralUpdate.Common.Download;
12+
using GeneralUpdate.Common.FileBasic;
13+
using GeneralUpdate.Common.Internal;
14+
using GeneralUpdate.Common.Internal.Bootstrap;
15+
using GeneralUpdate.Common.Internal.JsonContext;
16+
using GeneralUpdate.Common.Internal.Strategy;
17+
using GeneralUpdate.Common.Shared;
18+
using GeneralUpdate.Common.Shared.Object;
19+
using GeneralUpdate.Common.Shared.Object.Enum;
20+
using GeneralUpdate.Common.Shared.Service;
21+
22+
namespace GeneralUpdate.ClientCore;
23+
24+
internal sealed class SilentUpdateMode
25+
{
26+
private static readonly TimeSpan PollingInterval = TimeSpan.FromMinutes(20);
27+
private readonly GlobalConfigInfo _configInfo;
28+
private readonly Encoding _encoding;
29+
private readonly string _format;
30+
private readonly int _downloadTimeOut;
31+
private readonly bool _patchEnabled;
32+
private readonly bool _backupEnabled;
33+
private int _prepared;
34+
private int _updaterStarted;
35+
36+
public SilentUpdateMode(GlobalConfigInfo configInfo, Encoding encoding, string format, int downloadTimeOut, bool patchEnabled, bool backupEnabled)
37+
{
38+
_configInfo = configInfo;
39+
_encoding = encoding;
40+
_format = format;
41+
_downloadTimeOut = downloadTimeOut;
42+
_patchEnabled = patchEnabled;
43+
_backupEnabled = backupEnabled;
44+
}
45+
46+
public Task StartAsync()
47+
{
48+
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
49+
_ = Task.Run(PollLoopAsync);
50+
return Task.CompletedTask;
51+
}
52+
53+
private async Task PollLoopAsync()
54+
{
55+
while (Volatile.Read(ref _prepared) == 0)
56+
{
57+
try
58+
{
59+
await PrepareUpdateIfNeededAsync();
60+
}
61+
catch (Exception exception)
62+
{
63+
GeneralTracer.Error("The PollLoopAsync method in SilentUpdateMode throws an exception.", exception);
64+
}
65+
66+
if (Volatile.Read(ref _prepared) == 1)
67+
break;
68+
69+
await Task.Delay(PollingInterval);
70+
}
71+
}
72+
73+
private async Task PrepareUpdateIfNeededAsync()
74+
{
75+
var mainResp = await VersionService.Validate(_configInfo.UpdateUrl
76+
, _configInfo.ClientVersion
77+
, AppType.ClientApp
78+
, _configInfo.AppSecretKey
79+
, GetPlatform()
80+
, _configInfo.ProductId
81+
, _configInfo.Scheme
82+
, _configInfo.Token);
83+
84+
if (mainResp?.Code != 200 || mainResp.Body == null || mainResp.Body.Count == 0)
85+
return;
86+
87+
var versions = mainResp.Body.OrderBy(x => x.ReleaseDate).ToList();
88+
var lastVersion = versions.Last().Version;
89+
if (CheckFail(lastVersion))
90+
return;
91+
92+
BlackListManager.Instance?.AddBlackFiles(_configInfo.BlackFiles);
93+
BlackListManager.Instance?.AddBlackFileFormats(_configInfo.BlackFormats);
94+
BlackListManager.Instance?.AddSkipDirectorys(_configInfo.SkipDirectorys);
95+
96+
_configInfo.Encoding = _encoding;
97+
_configInfo.Format = _format;
98+
_configInfo.DownloadTimeOut = _downloadTimeOut;
99+
_configInfo.PatchEnabled = _patchEnabled;
100+
_configInfo.IsMainUpdate = true;
101+
_configInfo.LastVersion = lastVersion;
102+
_configInfo.UpdateVersions = versions;
103+
_configInfo.TempPath = StorageManager.GetTempDirectory("main_temp");
104+
_configInfo.BackupDirectory = Path.Combine(_configInfo.InstallPath, $"{StorageManager.DirectoryName}{_configInfo.ClientVersion}");
105+
106+
if (_backupEnabled)
107+
{
108+
StorageManager.Backup(_configInfo.InstallPath, _configInfo.BackupDirectory, BlackListManager.Instance.SkipDirectorys);
109+
}
110+
111+
var processInfo = ConfigurationMapper.MapToProcessInfo(
112+
_configInfo,
113+
versions,
114+
BlackListManager.Instance.BlackFileFormats.ToList(),
115+
BlackListManager.Instance.BlackFiles.ToList(),
116+
BlackListManager.Instance.SkipDirectorys.ToList());
117+
_configInfo.ProcessInfo = JsonSerializer.Serialize(processInfo, ProcessInfoJsonContext.Default.ProcessInfo);
118+
119+
var manager = new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut);
120+
foreach (var versionInfo in _configInfo.UpdateVersions)
121+
{
122+
manager.Add(new DownloadTask(manager, versionInfo));
123+
}
124+
await manager.LaunchTasksAsync();
125+
126+
var strategy = CreateStrategy();
127+
strategy.Create(_configInfo);
128+
await strategy.ExecuteAsync();
129+
130+
Interlocked.Exchange(ref _prepared, 1);
131+
}
132+
133+
private void OnProcessExit(object? sender, EventArgs e)
134+
{
135+
if (Volatile.Read(ref _prepared) != 1 || Interlocked.Exchange(ref _updaterStarted, 1) == 1)
136+
return;
137+
138+
try
139+
{
140+
Environments.SetEnvironmentVariable("ProcessInfo", _configInfo.ProcessInfo);
141+
var appPath = Path.Combine(_configInfo.InstallPath, _configInfo.AppName);
142+
if (File.Exists(appPath))
143+
{
144+
Process.Start(new ProcessStartInfo
145+
{
146+
UseShellExecute = true,
147+
FileName = appPath
148+
});
149+
}
150+
}
151+
catch (Exception exception)
152+
{
153+
GeneralTracer.Error("The OnProcessExit method in SilentUpdateMode throws an exception.", exception);
154+
}
155+
}
156+
157+
private IStrategy CreateStrategy()
158+
{
159+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
160+
return new WindowsStrategy();
161+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
162+
return new LinuxStrategy();
163+
throw new PlatformNotSupportedException("The current operating system is not supported!");
164+
}
165+
166+
private static int GetPlatform()
167+
{
168+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
169+
return PlatformType.Windows;
170+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
171+
return PlatformType.Linux;
172+
return -1;
173+
}
174+
175+
private static bool CheckFail(string version)
176+
{
177+
var fail = Environments.GetEnvironmentVariable("UpgradeFail");
178+
if (string.IsNullOrEmpty(fail) || string.IsNullOrEmpty(version))
179+
return false;
180+
181+
var failVersion = new Version(fail);
182+
var lastVersion = new Version(version);
183+
return failVersion >= lastVersion;
184+
}
185+
}

src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ private class UpdateOptionPool : ConstantPool
5252
/// Specifies the update execution mode.
5353
/// </summary>
5454
public static readonly UpdateOption<UpdateMode?> Mode = ValueOf<UpdateMode?>("MODE");
55+
56+
/// <summary>
57+
/// Whether to enable silent update mode.
58+
/// </summary>
59+
public static readonly UpdateOption<bool> EnableSilentUpdate = ValueOf<bool>("ENABLESILENTUPDATE");
5560

5661
internal UpdateOption(int id, string name)
5762
: base(id, name) { }
@@ -232,4 +237,4 @@ public int CompareTo(T o)
232237
throw new System.Exception("failed to compare two different constants");
233238
}
234239
}
235-
}
240+
}

0 commit comments

Comments
 (0)