diff --git a/ReleaseUpdater.Common/Options.cs b/ReleaseUpdater.Common/Options.cs new file mode 100644 index 0000000..8cc64d3 --- /dev/null +++ b/ReleaseUpdater.Common/Options.cs @@ -0,0 +1,34 @@ +using ArgumentsToolkit; + +namespace ReleaseUpdater.Common; + +/// Параметры CLI для Updater.exe. +public sealed class Options +{ + [Option(nameof(ZipPath), "z", "Путь к скачанному архиву (.zip).", true)] + /// Путь к скачанному архиву (.zip). + public string ZipPath { get; set; } + + [Option(nameof(InstallPath), "i", "Целевой каталог установки.", true)] + /// Целевой каталог установки. + public string InstallPath { get; set; } + + [Option(nameof(AppExe), "a", "Имя исполняемого файла приложения для перезапуска", true)] + /// Имя исполняемого файла приложения для перезапуска (e.g., MyBot.exe). + public string AppExe { get; set; } + + [Option(nameof(RestartDelayMs), "rd", "Миллисекунды ожидания перед перезапуском", false, 500)] + /// Необязательно: подождите миллисекунды перед перезапуском. + public int RestartDelayMs { get; set; } = 500; + + [Option(nameof(UpdateDelayMs), "ud", "Миллисекунды ожидания перед обновления", false, 500)] + /// Необязательно: подождите миллисекунды перед запуском обновления. + public int UpdateDelayMs { get; set; } = 500; + + [Option(nameof(WaitProcess), "wp", "PID процесса, завершение которого необходимо дождаться", false)] + /// Необязательно: дождаться завершения процесса. + public int? WaitProcess { get; set; } = null; + + public static string Usage => + "Usage: Updater.exe --zip --installPath --appExe [--restartDelayMs ] [--updateDelayMs ] [--waitProcess ]"; +} diff --git a/ReleaseUpdater.Common/ReleaseUpdater.Common.csproj b/ReleaseUpdater.Common/ReleaseUpdater.Common.csproj new file mode 100644 index 0000000..99658e6 --- /dev/null +++ b/ReleaseUpdater.Common/ReleaseUpdater.Common.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/ReleaseUpdater.slnx b/ReleaseUpdater.slnx index 6430c89..d18bd49 100644 --- a/ReleaseUpdater.slnx +++ b/ReleaseUpdater.slnx @@ -2,6 +2,8 @@ + + diff --git a/ReleaseUpdater/ReleaseUpdater.csproj b/ReleaseUpdater/ReleaseUpdater.csproj index fa71b7a..00fe7f2 100644 --- a/ReleaseUpdater/ReleaseUpdater.csproj +++ b/ReleaseUpdater/ReleaseUpdater.csproj @@ -6,4 +6,12 @@ enable + + + + + + + + diff --git a/ReleaseUpdater/ReleaseUpdaterFacade.cs b/ReleaseUpdater/ReleaseUpdaterFacade.cs index f333cdc..bedfe27 100644 --- a/ReleaseUpdater/ReleaseUpdaterFacade.cs +++ b/ReleaseUpdater/ReleaseUpdaterFacade.cs @@ -90,14 +90,27 @@ public static class ReleaseUpdaterFacade if (updaterExePath == null) updaterExePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Updater.exe"); - var args = $"--zip \"{zipPath}\" --installPath \"{installPath}\" --appExe \"{appExe}\""; + + if (installPath.EndsWith("\\")) + { + installPath = installPath[0..(installPath.Length - 1)]; + } + + var updaterOptions = new Common.Options() + { + ZipPath = zipPath, + InstallPath = installPath, + AppExe = appExe, + }; if (exitCurrentApp) { int pid = Process.GetCurrentProcess().Id; - args += $" --waitProcess \"{pid}\""; + updaterOptions.WaitProcess = pid; } + var args = ArgumentsToolkit.ArgumentsParser.ToArguments(updaterOptions, true); + var process = Process.Start(new ProcessStartInfo { FileName = updaterExePath, diff --git a/Updater.Test/Program.cs b/Updater.Test/Program.cs new file mode 100644 index 0000000..ff6c6f7 --- /dev/null +++ b/Updater.Test/Program.cs @@ -0,0 +1,21 @@ +using ReleaseUpdater; + +namespace Updater.Test; + +internal class Program +{ + static async Task Main(string[] args) + { + + string installPath = AppDomain.CurrentDomain.BaseDirectory; + string appExe = "RetailUpdatesBot.exe"; + string updaterPath = Path.Combine(installPath, "Tools\\Updater.exe"); + + var url = "https://git.frigat.duckdns.org/api/v1/repos/automacon/RetailUpdatesBot/releases"; + var APIKey = "0552a77699d7506711946fc71cc6635515726bd1"; //токен + + await ReleaseUpdaterFacade.UpdateWithExternalAsync(url, APIKey, installPath, appExe, "latest", updaterPath, true); + + Console.ReadKey(); + } +} diff --git a/Updater.Test/Tools/Updater.exe b/Updater.Test/Tools/Updater.exe new file mode 100644 index 0000000..8b30a80 Binary files /dev/null and b/Updater.Test/Tools/Updater.exe differ diff --git a/Updater.Test/Updater.Test.csproj b/Updater.Test/Updater.Test.csproj new file mode 100644 index 0000000..da8130a --- /dev/null +++ b/Updater.Test/Updater.Test.csproj @@ -0,0 +1,20 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + Always + + + + diff --git a/Updater/Core/ExitCodes.cs b/Updater/Core/ExitCodes.cs new file mode 100644 index 0000000..c32b4e7 --- /dev/null +++ b/Updater/Core/ExitCodes.cs @@ -0,0 +1,15 @@ +namespace Updater.Core; + +public static class ExitCodes +{ + /// Успешное обновление. + public const int Ok = 0; + /// Неверные аргументы командной строки. + public const int InvalidArgs = 2; + /// Ошибка извлечения. + public const int ExtractFailed = 3; + /// Ошибка установки (копировать/заменить). + public const int InstallFailed = 4; + /// Ошибка перезапуска. + public const int RestartFailed = 5; +} diff --git a/Updater/Core/Options.cs b/Updater/Core/Options.cs deleted file mode 100644 index 31ced91..0000000 --- a/Updater/Core/Options.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace Updater.Core; - -/// Параметры CLI для Updater.exe. -public sealed class Options -{ - /// Путь к скачанному архиву (.zip). - public required string ZipPath { get; init; } - - /// Целевой каталог установки. - public required string InstallPath { get; init; } - - /// Имя исполняемого файла приложения для перезапуска (e.g., MyBot.exe). - public required string AppExe { get; init; } - - /// Необязательно: подождите миллисекунды перед перезапуском. - public int RestartDelayMs { get; init; } = 500; - - /// Необязательно: подождите миллисекунды перед запуском обновления. - public int UpdateDelayMs { get; init; } = 500; - - /// Необязательно: дождаться завершения процесса. - public int? WaitProcess { get; init; } = null; - - public static string Usage => - "Usage: Updater.exe --zip --installPath --appExe [--restartDelayMs ] [--updateDelayMs ] [--waitProcess ]"; - - /// Парсинг CLI аргументов в Options. - public static Options Parse(string[] args) - { - var dict = new Dictionary(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 dict, string key) - => dict.TryGetValue(key, out var v) && !string.IsNullOrWhiteSpace(v) - ? v : throw new ArgumentException($"Missing --{key}"); -} - -public static class ExitCodes -{ - /// Успешное обновление. - public const int Ok = 0; - /// Неверные аргументы командной строки. - public const int InvalidArgs = 2; - /// Ошибка извлечения. - public const int ExtractFailed = 3; - /// Ошибка установки (копировать/заменить). - public const int InstallFailed = 4; - /// Ошибка перезапуска. - public const int RestartFailed = 5; -} diff --git a/Updater/Core/UpdaterApp.cs b/Updater/Core/UpdaterApp.cs index d267e59..b4de7c5 100644 --- a/Updater/Core/UpdaterApp.cs +++ b/Updater/Core/UpdaterApp.cs @@ -1,4 +1,6 @@ -namespace Updater.Core; +using ReleaseUpdater.Common; + +namespace Updater.Core; /// /// Управляет потоком обновлений: извлечение, установка, перезапуск. diff --git a/Updater/Program.cs b/Updater/Program.cs index cb91585..2be4ef5 100644 --- a/Updater/Program.cs +++ b/Updater/Program.cs @@ -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(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; } } diff --git a/Updater/Updater.csproj b/Updater/Updater.csproj index 2150e37..3c2e96f 100644 --- a/Updater/Updater.csproj +++ b/Updater/Updater.csproj @@ -7,4 +7,12 @@ enable + + + + + + + + diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..91ba526 --- /dev/null +++ b/nuget.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file