mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
161 lines
4.6 KiB
C#
161 lines
4.6 KiB
C#
using System.Globalization;
|
|
|
|
namespace LanMountainDesktop.Launcher.Services;
|
|
|
|
internal sealed class DeploymentLocator
|
|
{
|
|
private readonly string _appRoot;
|
|
|
|
public DeploymentLocator(string appRoot)
|
|
{
|
|
_appRoot = appRoot;
|
|
}
|
|
|
|
public string GetAppRoot() => _appRoot;
|
|
|
|
public string? FindCurrentDeploymentDirectory()
|
|
{
|
|
var candidates = Directory.Exists(_appRoot)
|
|
? Directory.GetDirectories(_appRoot, "app-*", SearchOption.TopDirectoryOnly)
|
|
: [];
|
|
|
|
// 过滤掉无效的部署目录
|
|
var validCandidates = candidates
|
|
.Where(path =>
|
|
!File.Exists(Path.Combine(path, ".destroy")) && // 排除待删除
|
|
!File.Exists(Path.Combine(path, ".partial"))) // 排除未完成
|
|
.ToList();
|
|
|
|
// 优先选择带 .current 标记的版本
|
|
var withMarkers = validCandidates
|
|
.Where(path => File.Exists(Path.Combine(path, ".current")))
|
|
.Select(path => new
|
|
{
|
|
Path = path,
|
|
Version = ParseVersionFromDirectory(path)
|
|
})
|
|
.OrderByDescending(item => item.Version)
|
|
.ToList();
|
|
|
|
if (withMarkers.Count > 0)
|
|
{
|
|
return withMarkers[0].Path;
|
|
}
|
|
|
|
// 如果没有 .current 标记,选择最新版本
|
|
var byVersion = validCandidates
|
|
.Select(path => new
|
|
{
|
|
Path = path,
|
|
Version = ParseVersionFromDirectory(path)
|
|
})
|
|
.OrderByDescending(item => item.Version)
|
|
.ToList();
|
|
|
|
return byVersion.Count > 0 ? byVersion[0].Path : null;
|
|
}
|
|
|
|
public string? ResolveHostExecutablePath()
|
|
{
|
|
var executable = OperatingSystem.IsWindows() ? "LanMountainDesktop.exe" : "LanMountainDesktop";
|
|
var currentDeployment = FindCurrentDeploymentDirectory();
|
|
if (!string.IsNullOrWhiteSpace(currentDeployment))
|
|
{
|
|
var inDeployment = Path.Combine(currentDeployment, executable);
|
|
if (File.Exists(inDeployment))
|
|
{
|
|
return inDeployment;
|
|
}
|
|
}
|
|
|
|
var inRoot = Path.Combine(_appRoot, executable);
|
|
if (File.Exists(inRoot))
|
|
{
|
|
return inRoot;
|
|
}
|
|
|
|
var parent = Path.GetFullPath(Path.Combine(_appRoot, ".."));
|
|
var inParent = Path.Combine(parent, executable);
|
|
return File.Exists(inParent) ? inParent : null;
|
|
}
|
|
|
|
public string GetCurrentVersion()
|
|
{
|
|
var deployment = FindCurrentDeploymentDirectory();
|
|
if (string.IsNullOrWhiteSpace(deployment))
|
|
{
|
|
return "0.0.0";
|
|
}
|
|
|
|
return ParseVersionTextFromDirectory(deployment) ?? "0.0.0";
|
|
}
|
|
|
|
public string BuildNextDeploymentDirectory(string targetVersion)
|
|
{
|
|
var sanitized = string.IsNullOrWhiteSpace(targetVersion) ? "0.0.0" : targetVersion.Trim();
|
|
var index = 0;
|
|
while (true)
|
|
{
|
|
var candidate = Path.Combine(_appRoot, $"app-{sanitized}-{index.ToString(CultureInfo.InvariantCulture)}");
|
|
if (!Directory.Exists(candidate))
|
|
{
|
|
return candidate;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
public void CleanupDestroyedDeployments()
|
|
{
|
|
try
|
|
{
|
|
var candidates = Directory.Exists(_appRoot)
|
|
? Directory.GetDirectories(_appRoot, "app-*", SearchOption.TopDirectoryOnly)
|
|
: [];
|
|
|
|
var destroyedDirs = candidates
|
|
.Where(path => File.Exists(Path.Combine(path, ".destroy")));
|
|
|
|
foreach (var dir in destroyedDirs)
|
|
{
|
|
try
|
|
{
|
|
Directory.Delete(dir, recursive: true);
|
|
}
|
|
catch
|
|
{
|
|
// 忽略删除失败(可能文件被占用),下次启动再试
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// 忽略清理失败
|
|
}
|
|
}
|
|
|
|
public static Version ParseVersionFromDirectory(string path)
|
|
{
|
|
var text = ParseVersionTextFromDirectory(path);
|
|
return Version.TryParse(text, out var version) ? version : new Version(0, 0, 0);
|
|
}
|
|
|
|
private static string? ParseVersionTextFromDirectory(string path)
|
|
{
|
|
var fileName = Path.GetFileName(path);
|
|
if (string.IsNullOrWhiteSpace(fileName))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var segments = fileName.Split('-');
|
|
if (segments.Length < 2)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return segments[1];
|
|
}
|
|
}
|