75 lines
2.6 KiB
C#
75 lines
2.6 KiB
C#
namespace Updater.Core;
|
||
|
||
/// <summary>
|
||
/// Безопасный установщик: создает резервную копию замененных файлов, выполняет атомарную замену,
|
||
/// и откатываемся в случае неудачи.
|
||
/// </summary>
|
||
public sealed class SafeFileInstaller : IInstaller
|
||
{
|
||
private readonly ILogger _log;
|
||
public SafeFileInstaller(ILogger log) => _log = log;
|
||
|
||
public void Install(string sourceDir, string installPath)
|
||
{
|
||
var backupDir = Path.Combine(Path.GetTempPath(), $"upd_backup_{Guid.NewGuid():N}");
|
||
Directory.CreateDirectory(backupDir);
|
||
var completed = false;
|
||
|
||
try
|
||
{
|
||
// Копирование файлов с резервной копией целей
|
||
foreach (var src in Directory.EnumerateFiles(sourceDir, "*", SearchOption.AllDirectories))
|
||
{
|
||
var rel = Path.GetRelativePath(sourceDir, src);
|
||
var dst = Path.Combine(installPath, rel);
|
||
var dstDir = Path.GetDirectoryName(dst)!;
|
||
Directory.CreateDirectory(dstDir);
|
||
|
||
if (File.Exists(dst))
|
||
{
|
||
var bkp = Path.Combine(backupDir, rel);
|
||
Directory.CreateDirectory(Path.GetDirectoryName(bkp)!);
|
||
File.Copy(dst, bkp, overwrite: true);
|
||
}
|
||
|
||
File.Copy(src, dst, overwrite: true);
|
||
}
|
||
|
||
completed = true;
|
||
_log.Info("Install completed successfully.");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_log.Error($"Install failed: {ex.Message}");
|
||
_log.Warn("Rolling back...");
|
||
Rollback(backupDir, installPath);
|
||
throw;
|
||
}
|
||
finally
|
||
{
|
||
// Очистка резервной копии только в случае успеха
|
||
if (completed)
|
||
{
|
||
TryDeleteDirectory(backupDir);
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void Rollback(string backupDir, string installPath)
|
||
{
|
||
if (!Directory.Exists(backupDir)) return;
|
||
|
||
foreach (var bkp in Directory.EnumerateFiles(backupDir, "*", SearchOption.AllDirectories))
|
||
{
|
||
var rel = Path.GetRelativePath(backupDir, bkp);
|
||
var dst = Path.Combine(installPath, rel);
|
||
Directory.CreateDirectory(Path.GetDirectoryName(dst)!);
|
||
File.Copy(bkp, dst, overwrite: true);
|
||
}
|
||
}
|
||
|
||
private static void TryDeleteDirectory(string dir)
|
||
{
|
||
try { Directory.Delete(dir, true); } catch { /* ignore */ }
|
||
}
|
||
} |