Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cc490c7112 | |||
| d4977a8708 | |||
| 6a01813602 | |||
| 60a3ec4dc7 | |||
| fb83e2d060 | |||
| a5055f7f26 |
@@ -20,14 +20,14 @@ jobs:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore
|
||||
- name: Restore ReleaseUpdater
|
||||
run: dotnet restore ReleaseUpdater
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --no-restore -c Release
|
||||
- name: Build ReleaseUpdater
|
||||
run: dotnet build ReleaseUpdater --no-restore -c Release
|
||||
|
||||
- name: Test
|
||||
run: dotnet test --no-build -c Release --collect:"XPlat Code Coverage" --logger "trx;LogFileName=test-results.trx"
|
||||
- name: Test ReleaseUpdater
|
||||
run: dotnet test ReleaseUpdater --no-build -c Release --collect:"XPlat Code Coverage" --logger "trx;LogFileName=test-results.trx"
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Updater
|
||||

|
||||
|
||||
## 📖 Описание
|
||||
`Updater` — это инструмент для безопасного обновления приложений.
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ReleaseUpdater;
|
||||
/// <summary>
|
||||
/// Провайдер для получения информации о релизах из Github / Gitea API.
|
||||
/// </summary>
|
||||
public sealed class GiteaReleaseProvider
|
||||
public sealed class ReleaseProvider
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOpts = new(JsonSerializerDefaults.Web);
|
||||
|
||||
@@ -29,7 +29,7 @@ public static class ReleaseUpdaterFacade
|
||||
/// </summary>
|
||||
public static async Task<IReadOnlyList<string>> GetVersionsAsync(string apiUrl, string? token = null)
|
||||
{
|
||||
var provider = new GiteaReleaseProvider();
|
||||
var provider = new ReleaseProvider();
|
||||
var releases = await provider.GetReleasesAsync(apiUrl, token);
|
||||
return releases.Select(r => r.TagName).ToList();
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public static class ReleaseUpdaterFacade
|
||||
{
|
||||
try
|
||||
{
|
||||
var provider = new GiteaReleaseProvider();
|
||||
var provider = new ReleaseProvider();
|
||||
var release = await provider.FindReleaseAsync(apiUrl, versionOrLatest, token)
|
||||
?? throw new Exception("Release not found");
|
||||
|
||||
@@ -72,11 +72,11 @@ public static class ReleaseUpdaterFacade
|
||||
/// Обновление через внешний Updater.exe.
|
||||
/// </summary>
|
||||
public static async Task UpdateWithExternalAsync(
|
||||
string apiUrl, string? token, string installPath, string appExe, string versionOrLatest = "latest")
|
||||
string apiUrl, string? token, string installPath, string appExe, string versionOrLatest = "latest", string? updaterExePath = null, bool exitCurrentApp = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
var provider = new GiteaReleaseProvider();
|
||||
var provider = new ReleaseProvider();
|
||||
var release = await provider.FindReleaseAsync(apiUrl, versionOrLatest, token)
|
||||
?? throw new Exception("Release not found");
|
||||
|
||||
@@ -88,17 +88,26 @@ public static class ReleaseUpdaterFacade
|
||||
|
||||
BeforeInstall?.Invoke();
|
||||
|
||||
var updaterExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Updater.exe");
|
||||
|
||||
if (updaterExePath == null) updaterExePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Updater.exe");
|
||||
var args = $"--zip \"{zipPath}\" --installPath \"{installPath}\" --appExe \"{appExe}\"";
|
||||
|
||||
if (exitCurrentApp)
|
||||
{
|
||||
int pid = Process.GetCurrentProcess().Id;
|
||||
args += $" --waitProcess \"{pid}\"";
|
||||
}
|
||||
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = updaterExe,
|
||||
FileName = updaterExePath,
|
||||
Arguments = args,
|
||||
UseShellExecute = true,
|
||||
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
|
||||
});
|
||||
|
||||
if (exitCurrentApp) { Environment.Exit(0); }
|
||||
|
||||
process?.WaitForExit();
|
||||
|
||||
if (process?.ExitCode == 0)
|
||||
|
||||
@@ -12,13 +12,19 @@ public sealed class Options
|
||||
/// <summary>Имя исполняемого файла приложения для перезапуска (e.g., MyBot.exe).</summary>
|
||||
public required string AppExe { get; init; }
|
||||
|
||||
/// <summary>Необязательно: подождите миллисекунды перед перезапуском (льготный период).</summary>
|
||||
/// <summary>Необязательно: подождите миллисекунды перед перезапуском.</summary>
|
||||
public int RestartDelayMs { get; init; } = 500;
|
||||
|
||||
public static string Usage =>
|
||||
"Usage: Updater.exe --zip <path.zip> --installPath <dir> --appExe <file.exe> [--restartDelayMs <int>]";
|
||||
/// <summary>Необязательно: подождите миллисекунды перед запуском обновления.</summary>
|
||||
public int UpdateDelayMs { get; init; } = 500;
|
||||
|
||||
/// <summary>Папрсинг CLI аргументов в Options.</summary>
|
||||
/// <summary>Необязательно: дождаться завершения процесса.</summary>
|
||||
public int? WaitProcess { get; init; } = null;
|
||||
|
||||
public static string Usage =>
|
||||
"Usage: Updater.exe --zip <path.zip> --installPath <dir> --appExe <file.exe> [--restartDelayMs <int>] [--updateDelayMs <int>] [--waitProcess <int>]";
|
||||
|
||||
/// <summary>Парсинг CLI аргументов в Options.</summary>
|
||||
public static Options Parse(string[] args)
|
||||
{
|
||||
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -42,7 +48,9 @@ public sealed class Options
|
||||
ZipPath = Path.GetFullPath(zip),
|
||||
InstallPath = Path.GetFullPath(install),
|
||||
AppExe = exe,
|
||||
RestartDelayMs = dict.TryGetValue("restartDelayMs", out var d) && int.TryParse(d, out var n) ? n : 500
|
||||
RestartDelayMs = dict.TryGetValue("restartDelayMs", out var d) && int.TryParse(d, out var n) ? n : 500,
|
||||
UpdateDelayMs = dict.TryGetValue("updateDelayMs", out var d2) && int.TryParse(d2, out var n2) ? n2 : 500,
|
||||
WaitProcess = dict.TryGetValue("waitProcess", out var pid) && int.TryParse(pid, out var pid_) ? pid_ : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Updater.Core;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
using Updater.Core;
|
||||
|
||||
namespace Updater;
|
||||
|
||||
@@ -19,6 +21,25 @@ internal sealed class Program
|
||||
return ExitCodes.InvalidArgs;
|
||||
}
|
||||
|
||||
|
||||
Thread.Sleep(options.UpdateDelayMs);
|
||||
|
||||
if (options.WaitProcess != null)
|
||||
try
|
||||
{
|
||||
using (var proc = Process.GetProcessById(options.WaitProcess.Value))
|
||||
{
|
||||
logger.Info($"Waiting for the process to complete {options.WaitProcess}...");
|
||||
proc.WaitForExit(); // блокирует выполнение до завершения процесса
|
||||
logger.Info("Process is completed.");
|
||||
}
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
logger.Info($"Process with PID {options.WaitProcess} not found.");
|
||||
}
|
||||
|
||||
|
||||
var extractor = new ZipExtractor(logger);
|
||||
var installer = new SafeFileInstaller(logger);
|
||||
var procMgr = new ProcessManager(logger);
|
||||
|
||||
Reference in New Issue
Block a user