From 4f9feafbbe4655921ae8282bb02f88b1c5b02959 Mon Sep 17 00:00:00 2001 From: lincube Date: Sun, 19 Apr 2026 02:12:34 +0800 Subject: [PATCH] =?UTF-8?q?fix.=E7=BB=A7=E7=BB=AD=E4=BF=AEci=EF=BC=8Cci?= =?UTF-8?q?=E6=80=8E=E4=B9=88=E5=A4=A9=E5=A4=A9=E7=82=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 11 ++- .github/workflows/code-quality.yml | 1 + .github/workflows/release.yml | 10 ++- LanMountainDesktop.Launcher/AppJsonContext.cs | 23 ++++++ .../LanMountainDesktop.Launcher.AOT.props | 8 +- .../Services/Commands.cs | 5 +- .../Services/DeploymentLocator.cs | 4 +- .../Services/FlexibleHostLocator.cs | 20 ++--- .../Services/Ipc/LauncherIpcServer.cs | 2 +- .../Services/PluginInstallerService.cs | 2 +- .../Services/PluginUpgradeQueueService.cs | 31 ++++---- .../Services/UpdateCheckService.cs | 77 +++++++++---------- .../Services/UpdateEngineService.cs | 11 +-- 13 files changed, 117 insertions(+), 88 deletions(-) create mode 100644 LanMountainDesktop.Launcher/AppJsonContext.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f12b35a..f052018 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,7 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Restore run: dotnet restore ${{ env.Solution_Name }} @@ -66,12 +67,15 @@ jobs: libx11-6 libxrandr2 libxinerama1 \ libxi6 libxcursor1 libxext6 \ libxrender1 libxkbcommon-x11-0 \ - clang zlib1g-dev + clang zlib1g-dev \ + libportaudio2 libasound2 \ + libwebkit2gtk-4.1-dev - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Restore run: dotnet restore ${{ env.Solution_Name }} @@ -98,10 +102,14 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Install dependencies + run: brew install portaudio + - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Restore run: dotnet restore ${{ env.Solution_Name }} @@ -132,6 +140,7 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Pack SDK and template packages shell: pwsh diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 744395a..9e64933 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -31,6 +31,7 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Restore run: dotnet restore ${{ env.Solution_Name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ef6a4ef..aff2ac8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,6 +88,7 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Restore run: dotnet restore ${{ env.Solution_Name }} @@ -515,12 +516,15 @@ jobs: libx11-6 libxrandr2 libxinerama1 \ libxi6 libxcursor1 libxext6 \ libxrender1 libxkbcommon-x11-0 \ - clang zlib1g-dev + clang zlib1g-dev \ + libportaudio2 libasound2 \ + libwebkit2gtk-4.1-dev - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Restore run: dotnet restore ${{ env.Solution_Name }} @@ -707,10 +711,14 @@ jobs: submodules: recursive ref: ${{ needs.prepare.outputs.checkout_ref }} + - name: Install dependencies + run: brew install portaudio + - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: 'preview' - name: Restore run: dotnet restore ${{ env.Solution_Name }} diff --git a/LanMountainDesktop.Launcher/AppJsonContext.cs b/LanMountainDesktop.Launcher/AppJsonContext.cs new file mode 100644 index 0000000..7b02ce9 --- /dev/null +++ b/LanMountainDesktop.Launcher/AppJsonContext.cs @@ -0,0 +1,23 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using LanMountainDesktop.Launcher.Models; +using LanMountainDesktop.Launcher.Services; +using LanMountainDesktop.Shared.Contracts.Launcher; + +namespace LanMountainDesktop.Launcher; + +[JsonSourceGenerationOptions(WriteIndented = true, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] +[JsonSerializable(typeof(SignedFileMap))] +[JsonSerializable(typeof(UpdateFileEntry))] +[JsonSerializable(typeof(SnapshotMetadata))] +[JsonSerializable(typeof(AppVersionInfo))] +[JsonSerializable(typeof(StartupProgressMessage))] +[JsonSerializable(typeof(LauncherResult))] +[JsonSerializable(typeof(HostDiscoveryConfig))] +[JsonSerializable(typeof(PluginManifest))] +[JsonSerializable(typeof(PendingUpgrade))] +[JsonSerializable(typeof(List))] +[JsonSerializable(typeof(GitHubRelease))] +[JsonSerializable(typeof(GitHubAsset))] +[JsonSerializable(typeof(List))] +internal sealed partial class AppJsonContext : JsonSerializerContext; diff --git a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.AOT.props b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.AOT.props index 03c4308..d714c3d 100644 --- a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.AOT.props +++ b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.AOT.props @@ -56,7 +56,11 @@ false - - true + + + false + + + true diff --git a/LanMountainDesktop.Launcher/Services/Commands.cs b/LanMountainDesktop.Launcher/Services/Commands.cs index d4d8896..4c038a2 100644 --- a/LanMountainDesktop.Launcher/Services/Commands.cs +++ b/LanMountainDesktop.Launcher/Services/Commands.cs @@ -149,10 +149,7 @@ internal static class Commands Directory.CreateDirectory(dir); } - var json = JsonSerializer.Serialize(result, new JsonSerializerOptions - { - WriteIndented = true - }); + var json = JsonSerializer.Serialize(result, AppJsonContext.Default.LauncherResult); await File.WriteAllTextAsync(fullPath, json, Encoding.UTF8).ConfigureAwait(false); } diff --git a/LanMountainDesktop.Launcher/Services/DeploymentLocator.cs b/LanMountainDesktop.Launcher/Services/DeploymentLocator.cs index c0d3f72..3e1ab99 100644 --- a/LanMountainDesktop.Launcher/Services/DeploymentLocator.cs +++ b/LanMountainDesktop.Launcher/Services/DeploymentLocator.cs @@ -322,7 +322,7 @@ internal sealed class DeploymentLocator try { var json = File.ReadAllText(snapshotFile); - var snapshot = System.Text.Json.JsonSerializer.Deserialize(json); + var snapshot = System.Text.Json.JsonSerializer.Deserialize(json, AppJsonContext.Default.SnapshotMetadata); if (snapshot != null && !string.IsNullOrEmpty(snapshot.SourceDirectory)) { if (Directory.Exists(snapshot.SourceDirectory)) @@ -445,7 +445,7 @@ internal sealed class DeploymentLocator try { var json = File.ReadAllText(versionFile); - var info = JsonSerializer.Deserialize(json); + var info = JsonSerializer.Deserialize(json, AppJsonContext.Default.AppVersionInfo); if (info is not null) { return info; diff --git a/LanMountainDesktop.Launcher/Services/FlexibleHostLocator.cs b/LanMountainDesktop.Launcher/Services/FlexibleHostLocator.cs index 856dc88..1d4d0ce 100644 --- a/LanMountainDesktop.Launcher/Services/FlexibleHostLocator.cs +++ b/LanMountainDesktop.Launcher/Services/FlexibleHostLocator.cs @@ -159,7 +159,7 @@ namespace LanMountainDesktop.Launcher.Services; try { var json = File.ReadAllText(configPath); - var config = JsonSerializer.Deserialize(json); + var config = JsonSerializer.Deserialize(json, AppJsonContext.Default.HostDiscoveryConfig); if (config?.HostPath != null && File.Exists(config.HostPath)) { return config.HostPath; @@ -617,13 +617,13 @@ namespace LanMountainDesktop.Launcher.Services; public required string AppRoot { get; set; } public required HostDiscoveryOptions Options { get; set; } } - - /// - /// 发现配置文件 - /// - private class HostDiscoveryConfig - { - public string? HostPath { get; set; } - public List? AdditionalPaths { get; set; } - } +} + +/// +/// 发现配置文件 +/// +internal class HostDiscoveryConfig +{ + public string? HostPath { get; set; } + public List? AdditionalPaths { get; set; } } diff --git a/LanMountainDesktop.Launcher/Services/Ipc/LauncherIpcServer.cs b/LanMountainDesktop.Launcher/Services/Ipc/LauncherIpcServer.cs index d526bbe..ddc5d8a 100644 --- a/LanMountainDesktop.Launcher/Services/Ipc/LauncherIpcServer.cs +++ b/LanMountainDesktop.Launcher/Services/Ipc/LauncherIpcServer.cs @@ -143,7 +143,7 @@ public class LauncherIpcServer : IDisposable // 3. 反序列化并回调 var json = System.Text.Encoding.UTF8.GetString(payloadBuffer, 0, payloadLength); - var message = JsonSerializer.Deserialize(json); + var message = JsonSerializer.Deserialize(json, AppJsonContext.Default.StartupProgressMessage); if (message is not null) { _onProgress(message); diff --git a/LanMountainDesktop.Launcher/Services/PluginInstallerService.cs b/LanMountainDesktop.Launcher/Services/PluginInstallerService.cs index 30f4650..e6dc390 100644 --- a/LanMountainDesktop.Launcher/Services/PluginInstallerService.cs +++ b/LanMountainDesktop.Launcher/Services/PluginInstallerService.cs @@ -73,7 +73,7 @@ internal sealed class PluginInstallerService using var stream = entries[0].Open(); using var reader = new StreamReader(stream); var json = reader.ReadToEnd(); - var manifest = JsonSerializer.Deserialize(json); + var manifest = JsonSerializer.Deserialize(json, AppJsonContext.Default.PluginManifest); if (manifest == null) { throw new InvalidOperationException($"Failed to deserialize manifest from '{packagePath}'."); diff --git a/LanMountainDesktop.Launcher/Services/PluginUpgradeQueueService.cs b/LanMountainDesktop.Launcher/Services/PluginUpgradeQueueService.cs index c839b69..25d57fd 100644 --- a/LanMountainDesktop.Launcher/Services/PluginUpgradeQueueService.cs +++ b/LanMountainDesktop.Launcher/Services/PluginUpgradeQueueService.cs @@ -29,7 +29,7 @@ internal sealed class PluginUpgradeQueueService } var text = File.ReadAllText(pendingPath); - var pending = JsonSerializer.Deserialize>(text) ?? []; + var pending = JsonSerializer.Deserialize(text, AppJsonContext.Default.ListPendingUpgrade) ?? []; var failures = new List(); var succeeded = new List(); @@ -63,10 +63,7 @@ internal sealed class PluginUpgradeQueueService } else { - File.WriteAllText(pendingPath, JsonSerializer.Serialize(remaining, new JsonSerializerOptions - { - WriteIndented = true - })); + File.WriteAllText(pendingPath, JsonSerializer.Serialize(remaining, AppJsonContext.Default.ListPendingUpgrade)); } return new LauncherResult @@ -79,19 +76,19 @@ internal sealed class PluginUpgradeQueueService : $"Applied {succeeded.Count} upgrades, failed: {string.Join(", ", failures)}." }; } +} - private sealed record PendingUpgrade( - string PluginId, - string SourcePackagePath, - string TargetVersion, - DateTimeOffset CreatedAt) +internal sealed record PendingUpgrade( + string PluginId, + string SourcePackagePath, + string TargetVersion, + DateTimeOffset CreatedAt) +{ + public bool IsValid() { - public bool IsValid() - { - return !string.IsNullOrWhiteSpace(PluginId) && - !string.IsNullOrWhiteSpace(SourcePackagePath) && - !string.IsNullOrWhiteSpace(TargetVersion) && - File.Exists(SourcePackagePath); - } + return !string.IsNullOrWhiteSpace(PluginId) && + !string.IsNullOrWhiteSpace(SourcePackagePath) && + !string.IsNullOrWhiteSpace(TargetVersion) && + File.Exists(SourcePackagePath); } } diff --git a/LanMountainDesktop.Launcher/Services/UpdateCheckService.cs b/LanMountainDesktop.Launcher/Services/UpdateCheckService.cs index a4302c8..5f4e3dd 100644 --- a/LanMountainDesktop.Launcher/Services/UpdateCheckService.cs +++ b/LanMountainDesktop.Launcher/Services/UpdateCheckService.cs @@ -15,7 +15,6 @@ internal sealed class UpdateCheckService private readonly string _repoOwner; private readonly string _repoName; private readonly HttpClient _httpClient; - private readonly JsonSerializerOptions _jsonOptions; public UpdateCheckService(string repoOwner, string repoName) { @@ -24,12 +23,6 @@ internal sealed class UpdateCheckService _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("User-Agent", "LanMountainDesktop-Launcher"); _httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github+json"); - - _jsonOptions = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower - }; } /// @@ -97,7 +90,7 @@ internal sealed class UpdateCheckService response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(cancellationToken); - var releases = JsonSerializer.Deserialize>(json, _jsonOptions); + var releases = JsonSerializer.Deserialize(json, AppJsonContext.Default.ListGitHubRelease); return releases?.Select(r => new ReleaseInfo { @@ -131,38 +124,38 @@ internal sealed class UpdateCheckService var cleaned = ParseVersionString(versionString); return Version.TryParse(cleaned, out var version) ? version : new Version(0, 0, 0); } - - // GitHub API 响应模型 - private sealed class GitHubRelease - { - [JsonPropertyName("tag_name")] - public string? TagName { get; set; } - - [JsonPropertyName("name")] - public string? Name { get; set; } - - [JsonPropertyName("prerelease")] - public bool Prerelease { get; set; } - - [JsonPropertyName("published_at")] - public DateTime PublishedAt { get; set; } - - [JsonPropertyName("body")] - public string? Body { get; set; } - - [JsonPropertyName("assets")] - public List? Assets { get; set; } - } - - private sealed class GitHubAsset - { - [JsonPropertyName("name")] - public string? Name { get; set; } - - [JsonPropertyName("browser_download_url")] - public string? BrowserDownloadUrl { get; set; } - - [JsonPropertyName("size")] - public long Size { get; set; } - } +} + +// GitHub API 响应模型 +internal sealed class GitHubRelease +{ + [JsonPropertyName("tag_name")] + public string? TagName { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("prerelease")] + public bool Prerelease { get; set; } + + [JsonPropertyName("published_at")] + public DateTime PublishedAt { get; set; } + + [JsonPropertyName("body")] + public string? Body { get; set; } + + [JsonPropertyName("assets")] + public List? Assets { get; set; } +} + +internal sealed class GitHubAsset +{ + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("browser_download_url")] + public string? BrowserDownloadUrl { get; set; } + + [JsonPropertyName("size")] + public long Size { get; set; } } diff --git a/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs b/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs index 4f2f917..a637a80 100644 --- a/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs +++ b/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs @@ -48,7 +48,7 @@ internal sealed class UpdateEngineService } var fileMapText = File.ReadAllText(fileMapPath); - var fileMap = JsonSerializer.Deserialize(fileMapText); + var fileMap = JsonSerializer.Deserialize(fileMapText, AppJsonContext.Default.SignedFileMap); if (fileMap is null) { return Failed("update.check", "invalid_manifest", "files.json is invalid."); @@ -137,7 +137,7 @@ internal sealed class UpdateEngineService } var fileMapText = await File.ReadAllTextAsync(fileMapPath); - var fileMap = JsonSerializer.Deserialize(fileMapText); + var fileMap = JsonSerializer.Deserialize(fileMapText, AppJsonContext.Default.SignedFileMap); if (fileMap is null || fileMap.Files.Count == 0) { return Failed("update.apply", "invalid_manifest", "No update file entries were found."); @@ -438,7 +438,7 @@ internal sealed class UpdateEngineService return Failed("update.rollback", "no_snapshot", "No snapshot found."); } - var snapshot = JsonSerializer.Deserialize(File.ReadAllText(snapshotPath)); + var snapshot = JsonSerializer.Deserialize(File.ReadAllText(snapshotPath), AppJsonContext.Default.SnapshotMetadata); if (snapshot is null || string.IsNullOrWhiteSpace(snapshot.SourceDirectory)) { return Failed("update.rollback", "invalid_snapshot", "Invalid snapshot metadata."); @@ -656,10 +656,7 @@ internal sealed class UpdateEngineService private static void SaveSnapshot(string path, SnapshotMetadata snapshot) { - File.WriteAllText(path, JsonSerializer.Serialize(snapshot, new JsonSerializerOptions - { - WriteIndented = true - })); + File.WriteAllText(path, JsonSerializer.Serialize(snapshot, AppJsonContext.Default.SnapshotMetadata)); } private static LauncherResult Failed(string stage, string code, string message)