Доработан вызов updater.exe
All checks were successful
CI / build-test (push) Successful in 30s
Release / pack-and-publish (release) Successful in 30s

This commit is contained in:
2025-11-27 10:55:38 +03:00
parent cc490c7112
commit 2cb585c222
14 changed files with 173 additions and 98 deletions

15
Updater/Core/ExitCodes.cs Normal file
View File

@@ -0,0 +1,15 @@
namespace Updater.Core;
public static class ExitCodes
{
/// <summary>Успешное обновление.</summary>
public const int Ok = 0;
/// <summary>Неверные аргументы командной строки.</summary>
public const int InvalidArgs = 2;
/// <summary>Ошибка извлечения.</summary>
public const int ExtractFailed = 3;
/// <summary>Ошибка установки (копировать/заменить).</summary>
public const int InstallFailed = 4;
/// <summary>Ошибка перезапуска.</summary>
public const int RestartFailed = 5;
}

View File

@@ -1,74 +0,0 @@
namespace Updater.Core;
/// <summary>Параметры CLI для Updater.exe.</summary>
public sealed class Options
{
/// <summary>Путь к скачанному архиву (.zip).</summary>
public required string ZipPath { get; init; }
/// <summary>Целевой каталог установки.</summary>
public required string InstallPath { get; init; }
/// <summary>Имя исполняемого файла приложения для перезапуска (e.g., MyBot.exe).</summary>
public required string AppExe { get; init; }
/// <summary>Необязательно: подождите миллисекунды перед перезапуском.</summary>
public int RestartDelayMs { get; init; } = 500;
/// <summary>Необязательно: подождите миллисекунды перед запуском обновления.</summary>
public int UpdateDelayMs { get; init; } = 500;
/// <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);
for (int i = 0; i < args.Length; i++)
{
if (!args[i].StartsWith("--")) continue;
var key = args[i][2..];
var val = (i + 1 < args.Length && !args[i + 1].StartsWith("--")) ? args[i + 1] : "true";
dict[key] = val;
}
var zip = Require(dict, "zip");
var install = Require(dict, "installPath");
var exe = Require(dict, "appExe");
if (!File.Exists(zip)) throw new FileNotFoundException("Zip not found", zip);
Directory.CreateDirectory(install);
return new 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,
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,
};
}
private static string Require(IDictionary<string, string> dict, string key)
=> dict.TryGetValue(key, out var v) && !string.IsNullOrWhiteSpace(v)
? v : throw new ArgumentException($"Missing --{key}");
}
public static class ExitCodes
{
/// <summary>Успешное обновление.</summary>
public const int Ok = 0;
/// <summary>Неверные аргументы командной строки.</summary>
public const int InvalidArgs = 2;
/// <summary>Ошибка извлечения.</summary>
public const int ExtractFailed = 3;
/// <summary>Ошибка установки (копировать/заменить).</summary>
public const int InstallFailed = 4;
/// <summary>Ошибка перезапуска.</summary>
public const int RestartFailed = 5;
}

View File

@@ -1,4 +1,6 @@
namespace Updater.Core;
using ReleaseUpdater.Common;
namespace Updater.Core;
/// <summary>
/// Управляет потоком обновлений: извлечение, установка, перезапуск.

View File

@@ -1,5 +1,5 @@
using System.Diagnostics;
using System.Security.Cryptography;
using ReleaseUpdater.Common;
using System.Diagnostics;
using Updater.Core;
namespace Updater;
@@ -9,35 +9,34 @@ internal sealed class Program
static int Main(string[] args)
{
var logger = new ConsoleLogger();
Options? options;
try
var parseResult = ArgumentsToolkit.ArgumentsParser.Parse<Options>(args);
if (!parseResult.Success)
{
options = Options.Parse(args);
}
catch (Exception ex)
{
logger.Error($"Arguments error: {ex.Message}");
Console.WriteLine(Options.Usage);
logger.Error($"Arguments error:");
parseResult.Errors.ForEach(t => logger.Error(t.Message));
return ExitCodes.InvalidArgs;
}
var options = parseResult.Value!;
Thread.Sleep(options.UpdateDelayMs);
if (options.WaitProcess != null)
try
{
using (var proc = Process.GetProcessById(options.WaitProcess.Value))
try
{
logger.Info($"Waiting for the process to complete {options.WaitProcess}...");
proc.WaitForExit(); // блокирует выполнение до завершения процесса
logger.Info("Process is completed.");
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)
{
catch (ArgumentException)
{
logger.Info($"Process with PID {options.WaitProcess} not found.");
}
}
var extractor = new ZipExtractor(logger);
@@ -45,6 +44,8 @@ internal sealed class Program
var procMgr = new ProcessManager(logger);
var app = new UpdaterApp(logger, extractor, installer, procMgr);
return app.Run(options);
var exitCode = app.Run(options);
return exitCode;
}
}

View File

@@ -7,4 +7,12 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ArgumentsToolkit" Version="0.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ReleaseUpdater.Common\ReleaseUpdater.Common.csproj" />
</ItemGroup>
</Project>