diff --git a/ReleaseUpdater/ReleaseProvider.cs b/ReleaseUpdater/ReleaseProvider.cs
index 00a7f88..3fe117c 100644
--- a/ReleaseUpdater/ReleaseProvider.cs
+++ b/ReleaseUpdater/ReleaseProvider.cs
@@ -17,7 +17,7 @@ public sealed class ReleaseProvider
/// Токен авторизации (если требуется).
/// Список релизов.
- public async Task> GetReleasesAsync(string apiUrl, string? token = null)
+ public async Task> GetReleasesAsync(Uri apiUrl, string? token = null)
{
using var client = CreateClient(token);
using var resp = await client.GetAsync(apiUrl);
@@ -38,7 +38,7 @@ public sealed class ReleaseProvider
/// Токен авторизации (если требуется).
/// Информация о релизе или null.
- public async Task FindReleaseAsync(string apiUrl, string? versionOrLatest, string? token = null)
+ public async Task FindReleaseAsync(Uri apiUrl, string? versionOrLatest, string? token = null)
{
var all = await GetReleasesAsync(apiUrl, token);
if (string.IsNullOrWhiteSpace(versionOrLatest) || versionOrLatest.Equals("latest", StringComparison.OrdinalIgnoreCase))
diff --git a/ReleaseUpdater/ReleaseUpdaterFacade.cs b/ReleaseUpdater/ReleaseUpdaterFacade.cs
index 1736d7a..7249d96 100644
--- a/ReleaseUpdater/ReleaseUpdaterFacade.cs
+++ b/ReleaseUpdater/ReleaseUpdaterFacade.cs
@@ -1,5 +1,6 @@
using System.Diagnostics;
using System.IO.Compression;
+using System.Reflection;
namespace ReleaseUpdater;
@@ -14,16 +15,16 @@ public static class ReleaseUpdaterFacade
///
public static event Action? BeforeInstall;
- ///
- /// Событие вызывается после успешной установки новой версии.
- ///
- public static event Action? AfterInstall;
-
///
/// Событие вызывается при ошибке обновления.
///
public static event Action? UpdateFailed;
+ ///
+ /// Событие вызывается, если текущая версия совпадает с требуемой.
+ ///
+ public static event Action? AlreadyUp;
+
///
/// Получает список доступных версий из Gitea.
///
@@ -34,45 +35,27 @@ public static class ReleaseUpdaterFacade
return releases.Select(r => r.TagName).ToList();
}
- ///
- /// Обновление без Updater.exe: скачивание, распаковка и перезапуск прямо из DLL.
- ///
- public static async Task UpdateInlineAsync(
- string apiUrl, string? token, string installPath, string appExe, string versionOrLatest = "latest")
- {
- try
- {
- var provider = new ReleaseProvider();
- var release = await provider.FindReleaseAsync(apiUrl, versionOrLatest, token)
- ?? throw new Exception("Release not found");
-
- var asset = release.Assets.FirstOrDefault(a => a.Name.EndsWith(".zip"))
- ?? throw new Exception("No zip asset found");
-
- var downloader = new HttpAssetDownloader();
- var zipPath = await downloader.DownloadAssetAsync(asset.DownloadUrl, token);
-
- BeforeInstall?.Invoke();
-
- ZipFile.ExtractToDirectory(zipPath, installPath, true);
-
- AfterInstall?.Invoke();
-
- Process.Start(Path.Combine(installPath, appExe));
- Environment.Exit(0);
- }
- catch (Exception ex)
- {
- UpdateFailed?.Invoke(ex);
- RestartCurrent(installPath, appExe);
- }
- }
-
///
/// Обновление через внешний Updater.exe.
///
- public static async Task UpdateWithExternalAsync(
- string apiUrl, string? token, string installPath, string appExe, string versionOrLatest = "latest", string? updaterExePath = null, bool exitCurrentApp = false, string? tempUpdaterDirectory = null)
+ /// API github/gitea release
+ /// Token авторизации
+ /// Путь для установки приложения
+ /// Наименование файла приложения
+ /// Папка для временного хранилища zip архива
+ /// Путь к updater.exe
+ /// Тэг с версией / "latest"
+ /// Маска наименовая ассета обновления. В маске может содержаться {version}
+ public static async Task UpdateAsync(
+ Uri apiUrl,
+ string? token,
+ string installPath,
+ string appExeName,
+ string tempDirectory,
+ string updaterExePath,
+ string versionOrLatest = "latest",
+ string? assetMask = null
+ )
{
try
{
@@ -80,32 +63,42 @@ public static class ReleaseUpdaterFacade
var release = await provider.FindReleaseAsync(apiUrl, versionOrLatest, token)
?? throw new Exception("Release not found");
- var asset = release.Assets.FirstOrDefault(a => a.Name.EndsWith(".zip"))
- ?? throw new Exception("No zip asset found");
+
+ var currentVersion = GetCurrentVersion(); // реализуй сам
+
+
+ if (SemVerService.Compare(release.TagName, currentVersion) > 0)
+ {
+ AlreadyUp?.Invoke(currentVersion);
+ return;
+ }
+
+ // Маска: myapp-{version}.zip
+ string? mask = assetMask?.Replace("{version}", release.TagName);
+
+ var asset = release.Assets.FirstOrDefault(a =>
+ mask != null
+ ? a.Name.Equals(mask, StringComparison.OrdinalIgnoreCase)
+ : a.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)
+ ) ?? throw new Exception("No matching asset found");
string tempNumber = $"{Guid.NewGuid():N}";
var tempUpdaterName = $"updater_{tempNumber}.exe";
- if (updaterExePath == null) updaterExePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Updater.exe");
-
- if (string.IsNullOrWhiteSpace(tempUpdaterDirectory))
+ if (string.IsNullOrWhiteSpace(tempDirectory))
{
- tempUpdaterDirectory = Path.GetDirectoryName(updaterExePath);
- tempUpdaterDirectory = Path.Combine(tempUpdaterDirectory!, "tempUpdater");
+ tempDirectory = Path.GetTempPath();
+ }
+ else if (!Directory.Exists(tempDirectory))
+ {
+ Directory.CreateDirectory(tempDirectory);
}
- if (!Directory.Exists(tempUpdaterDirectory))
- {
- Directory.CreateDirectory(tempUpdaterDirectory);
- }
-
- var tempUpdaterPath = Path.Combine(tempUpdaterDirectory, tempUpdaterName!);
-
+ var tempUpdaterPath = Path.Combine(tempDirectory, tempUpdaterName);
var downloader = new HttpAssetDownloader();
- var zipPath = await downloader.DownloadAssetAsync(asset.DownloadUrl, token, Path.Combine(tempUpdaterDirectory, $"updater_{tempNumber}.zip"));
+ var zipPath = await downloader.DownloadAssetAsync(asset.DownloadUrl, token, Path.Combine(tempDirectory, $"updater_{tempNumber}.zip"));
- BeforeInstall?.Invoke();
File.Copy(updaterExePath, tempUpdaterPath);
@@ -118,17 +111,16 @@ public static class ReleaseUpdaterFacade
{
ZipPath = zipPath,
InstallPath = installPath,
- AppExe = appExe,
+ AppExe = appExeName,
};
- if (exitCurrentApp)
- {
- int pid = Process.GetCurrentProcess().Id;
- updaterOptions.WaitProcess = pid;
- }
+ int pid = Process.GetCurrentProcess().Id;
+ updaterOptions.WaitProcess = pid;
var args = ArgumentsToolkit.ArgumentsParser.ToArguments(updaterOptions, true);
+ BeforeInstall?.Invoke();
+
var process = Process.Start(new ProcessStartInfo
{
FileName = tempUpdaterPath,
@@ -137,36 +129,24 @@ public static class ReleaseUpdaterFacade
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
});
- if (exitCurrentApp) { Environment.Exit(0); }
-
- process?.WaitForExit();
-
- if (process?.ExitCode == 0)
- AfterInstall?.Invoke();
- else
- throw new Exception($"Updater.exe завершился с кодом {process?.ExitCode}");
-
Environment.Exit(0);
}
catch (Exception ex)
{
UpdateFailed?.Invoke(ex);
- RestartCurrent(installPath, appExe);
}
}
- private static void RestartCurrent(string installPath, string appExe)
+ ///
+ /// Получение текущей версии приложения
+ ///
+ ///
+ public static string GetCurrentVersion()
{
- var currentApp = Path.Combine(installPath, appExe);
- if (File.Exists(currentApp))
- {
- Process.Start(new ProcessStartInfo
- {
- FileName = currentApp,
- UseShellExecute = true,
- WorkingDirectory = installPath
- });
- }
+ var entryAssembly = Assembly.GetEntryAssembly();
+ var attr = entryAssembly?.GetCustomAttribute();
+ return attr?.InformationalVersion
+ ?? entryAssembly?.GetName().Version?.ToString().Split("+")[0]
+ ?? "unknown";
}
-
}
diff --git a/ReleaseUpdater/SemVerService.cs b/ReleaseUpdater/SemVerService.cs
index 50ca57a..1db374b 100644
--- a/ReleaseUpdater/SemVerService.cs
+++ b/ReleaseUpdater/SemVerService.cs
@@ -11,7 +11,7 @@ public sealed class SemVerService
/// Строка версии (например, "3.5.2").
/// Результат парсинга.
/// true, если парсинг успешен.
- public bool TryParse(string version, out Version parsed)
+ public static bool TryParse(string version, out Version parsed)
{
var v = version.Trim().TrimStart('v');
return Version.TryParse(Normalize(v), out parsed);
@@ -34,7 +34,7 @@ public sealed class SemVerService
/// Первая версия.
/// Вторая версия.
/// -1 если v1 < v2, 0 если равны, 1 если v1 > v2.
- public int Compare(string v1, string v2)
+ public static int Compare(string v1, string v2)
{
TryParse(v1, out var a);
TryParse(v2, out var b);
diff --git a/Updater.Test/Program.cs b/Updater.Test/Program.cs
index ff6c6f7..61bda39 100644
--- a/Updater.Test/Program.cs
+++ b/Updater.Test/Program.cs
@@ -14,7 +14,22 @@ internal class Program
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);
+ SemVerService.TryParse("v0.1.2", out var v1);
+ Console.WriteLine($"v0.1.2 - {v1}");
+ SemVerService.TryParse(ReleaseUpdaterFacade.GetCurrentVersion(), out var v2);
+ Console.WriteLine($"{ReleaseUpdaterFacade.GetCurrentVersion()} - {v2}");
+ Console.WriteLine(SemVerService.Compare("v0.1.2", ReleaseUpdaterFacade.GetCurrentVersion()));
+
+ await ReleaseUpdaterFacade.UpdateAsync(
+ apiUrl: url,
+ token: APIKey,
+ installPath: installPath,
+ appExeName: appExe,
+ tempDirectory: Path.Combine(updaterPath, "Tools", "Temp"),
+ updaterExePath: updaterPath,
+ versionOrLatest: "latest",
+ assetMask: "RetailUpdatesBot-{version}.zip"
+ );
Console.ReadKey();
}
diff --git a/Updater.Test/Tools/Updater.exe b/Updater.Test/Tools/Updater.exe
index 8b30a80..2d822dc 100644
Binary files a/Updater.Test/Tools/Updater.exe and b/Updater.Test/Tools/Updater.exe differ
diff --git a/Updater.Test/Updater.Test.csproj b/Updater.Test/Updater.Test.csproj
index da8130a..1d8002e 100644
--- a/Updater.Test/Updater.Test.csproj
+++ b/Updater.Test/Updater.Test.csproj
@@ -5,6 +5,7 @@
net8.0
enable
enable
+ 2.0.0
diff --git a/Updater/Core/SafeFileInstaller.cs b/Updater/Core/SafeFileInstaller.cs
index 50edf60..7188952 100644
--- a/Updater/Core/SafeFileInstaller.cs
+++ b/Updater/Core/SafeFileInstaller.cs
@@ -23,12 +23,13 @@ public sealed class SafeFileInstaller : IInstaller
var rel = Path.GetRelativePath(sourceDir, src);
var dst = Path.Combine(installPath, rel);
var dstDir = Path.GetDirectoryName(dst)!;
- Directory.CreateDirectory(dstDir);
+ if (!Directory.Exists(dstDir)) Directory.CreateDirectory(dstDir);
if (File.Exists(dst))
{
var bkp = Path.Combine(backupDir, rel);
- Directory.CreateDirectory(Path.GetDirectoryName(bkp)!);
+ var bkpDir = Path.GetDirectoryName(bkp);
+ if (!Directory.Exists(bkpDir)) Directory.CreateDirectory(bkpDir!);
File.Copy(dst, bkp, overwrite: true);
}
@@ -63,7 +64,8 @@ public sealed class SafeFileInstaller : IInstaller
{
var rel = Path.GetRelativePath(backupDir, bkp);
var dst = Path.Combine(installPath, rel);
- Directory.CreateDirectory(Path.GetDirectoryName(dst)!);
+ var dstDir = Path.GetDirectoryName(dst);
+ if (!Directory.Exists(dstDir)) Directory.CreateDirectory(dstDir!);
File.Copy(bkp, dst, overwrite: true);
}
}
diff --git a/Updater/Core/UpdaterApp.cs b/Updater/Core/UpdaterApp.cs
index b4de7c5..3ea853d 100644
--- a/Updater/Core/UpdaterApp.cs
+++ b/Updater/Core/UpdaterApp.cs
@@ -12,6 +12,7 @@ public sealed class UpdaterApp
private readonly IInstaller _installer;
private readonly IProcessManager _proc;
+ ///
public UpdaterApp(ILogger log, IExtractor extractor, IInstaller installer, IProcessManager proc)
{
_log = log;
@@ -34,6 +35,7 @@ public sealed class UpdaterApp
{
_log.Error($"Extraction failed: {ex.Message}");
Cleanup(tempExtractDir);
+ _proc.StartApp(opts.InstallPath, opts.AppExe, opts.RestartDelayMs);
return ExitCodes.ExtractFailed;
}
@@ -45,6 +47,7 @@ public sealed class UpdaterApp
{
_log.Error($"Install failed: {ex.Message}");
Cleanup(tempExtractDir);
+ _proc.StartApp(opts.InstallPath, opts.AppExe, opts.RestartDelayMs);
return ExitCodes.InstallFailed;
}
diff --git a/Updater/Properties/launchSettings.json b/Updater/Properties/launchSettings.json
new file mode 100644
index 0000000..3b6b9cb
--- /dev/null
+++ b/Updater/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Updater": {
+ "commandName": "Project",
+ "commandLineArgs": "-z \"C:\\Job\\Projects\\FrigaT\\ReleaseUpdater\\Updater.Test\\bin\\Debug\\net8.0\\Tools\\tempUpdater\\updater_32f606dc1cda473ab953206927bf047b.zip\" -i \"C:\\Job\\Projects\\FrigaT\\ReleaseUpdater\\Updater.Test\\bin\\Debug\\net8.0\" -a \"RetailUpdatesBot.exe\" -rd \"500\" -ud \"500\" -wp \"31408\""
+ }
+ }
+}
\ No newline at end of file
diff --git a/Updater/Updater.csproj b/Updater/Updater.csproj
index 67d8837..5e4e07e 100644
--- a/Updater/Updater.csproj
+++ b/Updater/Updater.csproj
@@ -6,7 +6,7 @@
enable
enable
true
- 1.0.0
+ 2.0.0
FrigaT
FrigaT
ReleaseUpdater