From c5e75244af82ceb36617484cb3292d87ba800ee6 Mon Sep 17 00:00:00 2001 From: lincube Date: Sat, 30 May 2026 13:47:15 +0800 Subject: [PATCH] =?UTF-8?q?feat.PLONDS=E7=B3=BB=E7=BB=9F=E4=BC=9A=E4=B8=8D?= =?UTF-8?q?=E6=96=AD=E5=9C=B0=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/plonds-comparator.yml | 17 +- .../Publishing/PlondsCommitAnalyzer.cs | 157 --------- .../PlondsCommitDeltaBuildOptions.cs | 6 +- .../Publishing/PlondsCommitDeltaBuilder.cs | 311 ++++++++++++------ .../Publishing/PlondsDeltaBuilder.cs | 37 +-- .../Plonds.Shared/Models/PlondsManifest.cs | 5 - .../src/Plonds.Shared/PlondsConstants.cs | 17 +- .../src/Plonds.Tool/Program.cs | 41 ++- docs/auto_commit_md/20260530_6a65087.md | 99 ++++++ 9 files changed, 375 insertions(+), 315 deletions(-) delete mode 100644 PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitAnalyzer.cs create mode 100644 docs/auto_commit_md/20260530_6a65087.md diff --git a/.github/workflows/plonds-comparator.yml b/.github/workflows/plonds-comparator.yml index 45c2bf9..a838463 100644 --- a/.github/workflows/plonds-comparator.yml +++ b/.github/workflows/plonds-comparator.yml @@ -28,7 +28,7 @@ on: - stable - preview compare_method: - description: '比较方法' + description: 'Compare method' required: false type: choice default: file-compare @@ -36,7 +36,7 @@ on: - file-compare - commit-analyze hash_algorithm: - description: '哈希算法(仅 file-compare 模式)' + description: 'Hash algorithm (file-compare only)' required: false type: choice default: sha256 @@ -157,8 +157,8 @@ jobs: mv plonds-input/files-windows-x64.zip plonds-input/current-files-windows-x64.zip if [[ -n "$BASELINE_TAG" ]]; then - gh release download "$BASELINE_TAG" -p "files-windows-x64.zip" -D /tmp/baseline-dl - mv /tmp/baseline-dl/files-windows-x64.zip plonds-input/baseline-files-windows-x64.zip + gh release download "$BASELINE_TAG" -p "files-windows-x64.zip" -D plonds-input + mv plonds-input/files-windows-x64.zip plonds-input/baseline-files-windows-x64.zip fi - name: Run build-delta (file-compare) @@ -205,14 +205,13 @@ jobs: '--current-zip' "$PWD/plonds-input/current-files-windows-x64.zip" '--output-dir' "$PWD/plonds-output" '--channel' "$RELEASE_CHANNEL" - '--baseline-tag' "${BASELINE_TAG:-v0.0.0}" + '--baseline-tag' "${BASELINE_TAG:-$RELEASE_TAG}" '--current-tag' "$RELEASE_TAG" '--hash-algorithm' "$HASH_ALGORITHM" ) if [[ -n "$BASELINE_TAG" ]]; then ARGS+=( - '--baseline-version' "$BASELINE_VERSION" '--fallback-zip' "$PWD/plonds-input/baseline-files-windows-x64.zip" ) fi @@ -246,12 +245,14 @@ jobs: run: | mkdir -p plonds-run-metadata printf '%s' "$RELEASE_TAG" > plonds-run-metadata/tag.txt - printf '%s' "$COMPARE_METHOD" >> plonds-run-metadata/tag.txt + printf '%s' "$COMPARE_METHOD" > plonds-run-metadata/compare-method.txt - name: Upload run metadata artifact uses: actions/upload-artifact@v4 with: name: plonds-run-metadata - path: plonds-run-metadata/tag.txt + path: | + plonds-run-metadata/tag.txt + plonds-run-metadata/compare-method.txt if-no-files-found: error retention-days: 7 diff --git a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitAnalyzer.cs b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitAnalyzer.cs deleted file mode 100644 index f9d8023..0000000 --- a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitAnalyzer.cs +++ /dev/null @@ -1,157 +0,0 @@ -namespace Plonds.Core.Publishing; - -public static class PlondsCommitAnalyzer -{ - private static readonly string[] SourceDirectories = - [ - "LanMountainDesktop/", - "LanMountainDesktop.Launcher/", - "LanMountainDesktop.Shared.Contracts/", - "LanMountainDesktop.PluginSdk/", - "LanMountainDesktop.Appearance/", - "LanMountainDesktop.Settings.Core/", - "LanMountainDesktop.ComponentSystem/" - ]; - - private static readonly (string Prefix, string[] Artifacts)[] SourceToArtifactMappings = - [ - ("LanMountainDesktop/", ["LanMountainDesktop.dll", "LanMountainDesktop.exe"]), - ("LanMountainDesktop.Launcher/", ["LanMountainDesktop.Launcher.exe", "LanMountainDesktop.Launcher.dll"]), - ("LanMountainDesktop.Shared.Contracts/", ["LanMountainDesktop.Shared.Contracts.dll"]), - ("LanMountainDesktop.PluginSdk/", ["LanMountainDesktop.PluginSdk.dll"]), - ("LanMountainDesktop.Appearance/", ["LanMountainDesktop.Appearance.dll"]), - ("LanMountainDesktop.Settings.Core/", ["LanMountainDesktop.Settings.Core.dll"]), - ("LanMountainDesktop.ComponentSystem/", ["LanMountainDesktop.ComponentSystem.dll"]) - ]; - - private static readonly string[] SourceCodeExtensions = - [ - ".cs", ".axaml", ".xaml", ".csproj" - ]; - - public static HashSet GetChangedSourceFiles(string baselineTag, string currentTag) - { - var changedFiles = new HashSet(StringComparer.OrdinalIgnoreCase); - - var start = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo - { - FileName = "git", - Arguments = $"log --name-only --pretty=format: {baselineTag}..{currentTag}", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - }); - - if (start is null) - { - return changedFiles; - } - - var output = start.StandardOutput.ReadToEnd(); - start.WaitForExit(); - - foreach (var line in output.Split('\n', StringSplitOptions.RemoveEmptyEntries)) - { - var trimmed = line.Trim().Replace('\\', '/'); - if (string.IsNullOrWhiteSpace(trimmed)) - { - continue; - } - - if (!IsSourceDirectoryFile(trimmed)) - { - continue; - } - - changedFiles.Add(trimmed); - } - - return changedFiles; - } - - public static HashSet MapSourceFilesToArtifacts(IReadOnlySet sourceFiles) - { - var artifacts = new HashSet(StringComparer.OrdinalIgnoreCase); - var hasUnmappedChanges = false; - - foreach (var sourceFile in sourceFiles) - { - var normalized = sourceFile.Replace('\\', '/'); - var mapped = false; - - foreach (var (prefix, artifactList) in SourceToArtifactMappings) - { - if (!normalized.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!IsSourceCodeFile(normalized) && !IsConfigFile(normalized)) - { - continue; - } - - foreach (var artifact in artifactList) - { - artifacts.Add(artifact); - } - - mapped = true; - break; - } - - if (!mapped && IsConfigFile(normalized)) - { - var artifactPath = MapConfigToArtifact(normalized); - if (artifactPath is not null) - { - artifacts.Add(artifactPath); - mapped = true; - } - } - - if (!mapped) - { - hasUnmappedChanges = true; - } - } - - if (hasUnmappedChanges) - { - foreach (var (_, artifactList) in SourceToArtifactMappings) - { - foreach (var artifact in artifactList) - { - artifacts.Add(artifact); - } - } - } - - return artifacts; - } - - public static bool IsSourceDirectoryFile(string path) - { - var normalized = path.Replace('\\', '/'); - return SourceDirectories.Any(d => normalized.StartsWith(d, StringComparison.OrdinalIgnoreCase)); - } - - private static bool IsSourceCodeFile(string path) - { - var ext = Path.GetExtension(path); - return SourceCodeExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase); - } - - private static bool IsConfigFile(string path) - { - var ext = Path.GetExtension(path); - return string.Equals(ext, ".json", StringComparison.OrdinalIgnoreCase) - || string.Equals(ext, ".xml", StringComparison.OrdinalIgnoreCase); - } - - private static string? MapConfigToArtifact(string sourcePath) - { - var fileName = Path.GetFileName(sourcePath); - return fileName; - } -} diff --git a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuildOptions.cs b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuildOptions.cs index 99cfeee..953ee6a 100644 --- a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuildOptions.cs +++ b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuildOptions.cs @@ -8,7 +8,7 @@ public sealed record PlondsCommitDeltaBuildOptions( string Channel, string BaselineTag, string CurrentTag, + string HashAlgorithm = "sha256", + string? SourceDirs = null, string? FallbackBaselineZip = null, - string? BaselineVersion = null, - string LauncherRelativePath = "LanMountainDesktop.Launcher.exe", - string HashAlgorithm = "sha256"); + string LauncherRelativePath = "LanMountainDesktop.Launcher.exe"); diff --git a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuilder.cs b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuilder.cs index 66b8ea7..0b69511 100644 --- a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuilder.cs +++ b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsCommitDeltaBuilder.cs @@ -13,11 +13,35 @@ public sealed class PlondsCommitDeltaBuilder WriteIndented = true }; - public PlondsCommitDeltaBuildResult Build(PlondsCommitDeltaBuildOptions options) + private static readonly Dictionary SourceToArtifactMap = new(StringComparer.OrdinalIgnoreCase) + { + ["LanMountainDesktop"] = ["LanMountainDesktop.dll", "LanMountainDesktop.exe"], + ["LanMountainDesktop.Launcher"] = ["LanMountainDesktop.Launcher.exe"], + ["LanMountainDesktop.Shared.Contracts"] = ["LanMountainDesktop.Shared.Contracts.dll"], + ["LanMountainDesktop.PluginSdk"] = ["LanMountainDesktop.PluginSdk.dll"], + ["LanMountainDesktop.Appearance"] = ["LanMountainDesktop.Appearance.dll"], + ["LanMountainDesktop.Settings.Core"] = ["LanMountainDesktop.Settings.Core.dll"], + ["LanMountainDesktop.ComponentSystem"] = ["LanMountainDesktop.ComponentSystem.dll"] + }; + + private static readonly string[] FallbackAllArtifacts = + [ + "LanMountainDesktop.dll", + "LanMountainDesktop.exe", + "LanMountainDesktop.Launcher.exe", + "LanMountainDesktop.Shared.Contracts.dll", + "LanMountainDesktop.PluginSdk.dll", + "LanMountainDesktop.Appearance.dll", + "LanMountainDesktop.Settings.Core.dll", + "LanMountainDesktop.ComponentSystem.dll" + ]; + + public PlondsDeltaBuildResult Build(PlondsCommitDeltaBuildOptions options) { ArgumentNullException.ThrowIfNull(options); - var hashAlgorithm = PlondsDeltaBuilder.ValidateHashAlgorithmInternal(options.HashAlgorithm); + var hashAlgorithm = ValidateHashAlgorithm(options.HashAlgorithm); + var sourceDirs = ParseSourceDirs(options.SourceDirs); var currentPayloadZip = Path.GetFullPath(options.CurrentPayloadZip); if (!File.Exists(currentPayloadZip)) @@ -32,50 +56,62 @@ public sealed class PlondsCommitDeltaBuilder Directory.CreateDirectory(outputRoot); PayloadUtilities.ExtractZip(currentPayloadZip, currentExtractRoot); - var changedSourceFiles = PlondsCommitAnalyzer.GetChangedSourceFiles(options.BaselineTag, options.CurrentTag); + var changedSourceFiles = GetChangedSourceFiles(options.BaselineTag, options.CurrentTag, sourceDirs); if (changedSourceFiles.Count == 0) { - return FallbackToFileCompare(options, currentPayloadZip, outputRoot, workRoot, hashAlgorithm); + Console.WriteLine("No source code changes detected between tags. Falling back to file-compare method."); + return FallbackToFileCompare(options, currentExtractRoot, outputRoot, hashAlgorithm); } - var mappedArtifacts = PlondsCommitAnalyzer.MapSourceFilesToArtifacts(changedSourceFiles); + Console.WriteLine($"Detected {changedSourceFiles.Count} changed source file(s) between {options.BaselineTag} and {options.CurrentTag}."); + foreach (var file in changedSourceFiles.Take(20)) + { + Console.WriteLine($" {file}"); + } + + if (changedSourceFiles.Count > 20) + { + Console.WriteLine($" ... and {changedSourceFiles.Count - 20} more"); + } + + var artifactFiles = MapSourceToArtifacts(changedSourceFiles, sourceDirs); var currentManifest = PayloadUtilities.ScanDirectory(currentExtractRoot); var filesMap = new Dictionary(StringComparer.OrdinalIgnoreCase); var changedFilesMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var artifact in mappedArtifacts.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)) + foreach (var artifactFile in artifactFiles) { - if (!currentManifest.TryGetValue(artifact, out var fingerprint)) + var normalizedPath = artifactFile.Replace('\\', '/'); + if (!currentManifest.TryGetValue(normalizedPath, out var fingerprint)) { + Console.WriteLine($" Artifact not found in current zip: {normalizedPath}, skipping."); continue; } - var hash = GetHash(fingerprint, hashAlgorithm); + var fileHash = PlondsDeltaBuilder.ComputeHash(fingerprint.FullPath, hashAlgorithm); var action = PlondsConstants.ActionReplace; - filesMap[artifact] = new PlondsFileEntry(action, hash, fingerprint.Size, hashAlgorithm); - changedFilesMap[artifact] = new PlondsChangedFileEntry(artifact, hash, fingerprint.Size, hashAlgorithm); + filesMap[normalizedPath] = new PlondsFileEntry(action, fileHash, fingerprint.Size, hashAlgorithm); + changedFilesMap[normalizedPath] = new PlondsChangedFileEntry(normalizedPath, fileHash, fingerprint.Size, hashAlgorithm); } - var changedZipPath = CreateChangedZipFromArtifacts(currentExtractRoot, mappedArtifacts, outputRoot, options.Platform); - - var requiresCleanInstall = mappedArtifacts.Contains(options.LauncherRelativePath, StringComparer.OrdinalIgnoreCase); - + var changedZipPath = CreateChangedZipFromList(currentExtractRoot, artifactFiles, outputRoot, options.Platform); var changedZipMd5 = ComputeMd5Hex(changedZipPath); + var launcherInChanges = artifactFiles.Any(f => + string.Equals(Path.GetFileName(f), "LanMountainDesktop.Launcher.exe", StringComparison.OrdinalIgnoreCase)); + var manifest = new PlondsManifest( FormatVersion: PlondsConstants.FormatVersion, CurrentVersion: options.CurrentVersion, - PreviousVersion: options.BaselineVersion ?? options.BaselineTag.TrimStart('v'), + PreviousVersion: options.BaselineTag.TrimStart('v'), IsFullUpdate: false, - RequiresCleanInstall: requiresCleanInstall, + RequiresCleanInstall: launcherInChanges, Channel: options.Channel, Platform: options.Platform, UpdatedAt: DateTimeOffset.UtcNow, - CompareMethod: PlondsConstants.CompareMethodCommitAnalyze, - HashAlgorithm: hashAlgorithm, FilesMap: filesMap, ChangedFilesMap: changedFilesMap, Checksums: new Dictionary @@ -87,24 +123,125 @@ public sealed class PlondsCommitDeltaBuilder var manifestJson = JsonSerializer.Serialize(manifest, JsonOptions); File.WriteAllText(manifestPath, manifestJson); - return new PlondsCommitDeltaBuildResult( + return new PlondsDeltaBuildResult( Platform: options.Platform, ChangedZipPath: changedZipPath, ManifestPath: manifestPath, IsFullUpdate: false, - RequiresCleanInstall: requiresCleanInstall, - FellBackToFileCompare: false, + RequiresCleanInstall: launcherInChanges, CurrentVersion: options.CurrentVersion, - BaselineVersion: options.BaselineVersion, - ChangedSourceFiles: changedSourceFiles.OrderBy(x => x, StringComparer.OrdinalIgnoreCase).ToArray(), - MappedArtifactFiles: mappedArtifacts.OrderBy(x => x, StringComparer.OrdinalIgnoreCase).ToArray()); + BaselineVersion: options.BaselineTag.TrimStart('v')); } - private PlondsCommitDeltaBuildResult FallbackToFileCompare( + private static List GetChangedSourceFiles(string baselineTag, string currentTag, string[] sourceDirs) + { + var changedFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + var normalizedBaseline = baselineTag.StartsWith("v") ? baselineTag : $"v{baselineTag}"; + var normalizedCurrent = currentTag.StartsWith("v") ? currentTag : $"v{currentTag}"; + + var psi = new System.Diagnostics.ProcessStartInfo + { + FileName = "git", + Arguments = $"diff --name-only {normalizedBaseline}..{normalizedCurrent}", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using var process = System.Diagnostics.Process.Start(psi) + ?? throw new InvalidOperationException("Failed to start git process."); + + var output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + throw new InvalidOperationException($"git diff failed with exit code {process.ExitCode}."); + } + + foreach (var line in output.Split('\n', StringSplitOptions.RemoveEmptyEntries)) + { + var trimmed = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmed)) + { + continue; + } + + var isSourceFile = sourceDirs.Any(dir => + trimmed.StartsWith(dir + "/", StringComparison.OrdinalIgnoreCase) || + trimmed.StartsWith(dir + "\\", StringComparison.OrdinalIgnoreCase)); + + if (isSourceFile) + { + changedFiles.Add(trimmed); + } + } + + return changedFiles.OrderBy(x => x, StringComparer.OrdinalIgnoreCase).ToList(); + } + + private static HashSet MapSourceToArtifacts(IReadOnlyList changedSourceFiles, string[] sourceDirs) + { + var artifacts = new HashSet(StringComparer.OrdinalIgnoreCase); + var hasUnmappedChanges = false; + + foreach (var sourceFile in changedSourceFiles) + { + var mapped = false; + + foreach (var dir in sourceDirs) + { + if (!sourceFile.StartsWith(dir + "/", StringComparison.OrdinalIgnoreCase) && + !sourceFile.StartsWith(dir + "\\", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (SourceToArtifactMap.TryGetValue(dir, out var artifactList)) + { + foreach (var artifact in artifactList) + { + artifacts.Add(artifact); + } + + mapped = true; + break; + } + } + + if (!mapped) + { + var extension = Path.GetExtension(sourceFile).ToLowerInvariant(); + if (extension is ".json" or ".xml" or ".config") + { + var fileName = Path.GetFileName(sourceFile); + artifacts.Add(fileName); + mapped = true; + } + } + + if (!mapped) + { + hasUnmappedChanges = true; + } + } + + if (hasUnmappedChanges) + { + Console.WriteLine("Unmapped source changes detected. Including all core artifacts as a conservative fallback."); + foreach (var artifact in FallbackAllArtifacts) + { + artifacts.Add(artifact); + } + } + + return artifacts; + } + + private static PlondsDeltaBuildResult FallbackToFileCompare( PlondsCommitDeltaBuildOptions options, - string currentPayloadZip, + string currentExtractRoot, string outputRoot, - string workRoot, string hashAlgorithm) { var fallbackZip = string.IsNullOrWhiteSpace(options.FallbackBaselineZip) @@ -113,97 +250,35 @@ public sealed class PlondsCommitDeltaBuilder if (string.IsNullOrWhiteSpace(fallbackZip) || !File.Exists(fallbackZip)) { - var currentExtractRoot = Path.Combine(workRoot, "current"); - PayloadUtilities.ExtractZip(currentPayloadZip, currentExtractRoot); - var currentManifest = PayloadUtilities.ScanDirectory(currentExtractRoot); - - var filesMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - var changedFilesMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var path in currentManifest.Keys.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)) - { - var fp = currentManifest[path]; - var hash = GetHash(fp, hashAlgorithm); - filesMap[path] = new PlondsFileEntry(PlondsConstants.ActionAdd, hash, fp.Size, hashAlgorithm); - changedFilesMap[path] = new PlondsChangedFileEntry(path, hash, fp.Size, hashAlgorithm); - } - - var changedZipPath = CreateChangedZipFromArtifacts(currentExtractRoot, filesMap.Keys.ToHashSet(), outputRoot, options.Platform); - var changedZipMd5 = ComputeMd5Hex(changedZipPath); - - var manifest = new PlondsManifest( - FormatVersion: PlondsConstants.FormatVersion, + Console.WriteLine("No fallback baseline zip available. Generating full update."); + var fullBuilder = new PlondsDeltaBuilder(); + return fullBuilder.Build(new PlondsDeltaBuildOptions( + Platform: options.Platform, CurrentVersion: options.CurrentVersion, - PreviousVersion: "0.0.0", - IsFullUpdate: true, - RequiresCleanInstall: false, + CurrentPayloadZip: options.CurrentPayloadZip, + OutputRoot: outputRoot, Channel: options.Channel, - Platform: options.Platform, - UpdatedAt: DateTimeOffset.UtcNow, - CompareMethod: PlondsConstants.CompareMethodCommitAnalyze, HashAlgorithm: hashAlgorithm, - FilesMap: filesMap, - ChangedFilesMap: changedFilesMap, - Checksums: new Dictionary - { - ["changed.zip"] = $"md5:{changedZipMd5}" - }); - - var manifestPath = Path.Combine(outputRoot, "PLONDS.json"); - var manifestJson = JsonSerializer.Serialize(manifest, JsonOptions); - File.WriteAllText(manifestPath, manifestJson); - - return new PlondsCommitDeltaBuildResult( - Platform: options.Platform, - ChangedZipPath: changedZipPath, - ManifestPath: manifestPath, - IsFullUpdate: true, - RequiresCleanInstall: false, - FellBackToFileCompare: true, - CurrentVersion: options.CurrentVersion, - BaselineVersion: options.BaselineVersion, - ChangedSourceFiles: [], - MappedArtifactFiles: []); + LauncherRelativePath: options.LauncherRelativePath)); } + Console.WriteLine($"Falling back to file-compare using baseline: {fallbackZip}"); var deltaBuilder = new PlondsDeltaBuilder(); - var deltaResult = deltaBuilder.Build(new PlondsDeltaBuildOptions( + return deltaBuilder.Build(new PlondsDeltaBuildOptions( Platform: options.Platform, CurrentVersion: options.CurrentVersion, - CurrentPayloadZip: currentPayloadZip, + CurrentPayloadZip: options.CurrentPayloadZip, OutputRoot: outputRoot, Channel: options.Channel, - BaselineVersion: options.BaselineVersion, + BaselineVersion: options.BaselineTag.TrimStart('v'), BaselinePayloadZip: fallbackZip, - LauncherRelativePath: options.LauncherRelativePath, - HashAlgorithm: hashAlgorithm)); - - return new PlondsCommitDeltaBuildResult( - Platform: deltaResult.Platform, - ChangedZipPath: deltaResult.ChangedZipPath, - ManifestPath: deltaResult.ManifestPath, - IsFullUpdate: deltaResult.IsFullUpdate, - RequiresCleanInstall: deltaResult.RequiresCleanInstall, - FellBackToFileCompare: true, - CurrentVersion: deltaResult.CurrentVersion, - BaselineVersion: deltaResult.BaselineVersion, - ChangedSourceFiles: [], - MappedArtifactFiles: []); + HashAlgorithm: hashAlgorithm, + LauncherRelativePath: options.LauncherRelativePath)); } - private static string GetHash(PayloadUtilities.FileFingerprint fingerprint, string hashAlgorithm) - { - if (hashAlgorithm == PlondsConstants.HashAlgorithmMd5) - { - return ComputeMd5Hex(fingerprint.FullPath); - } - - return fingerprint.Sha256; - } - - private static string CreateChangedZipFromArtifacts( + private static string CreateChangedZipFromList( string currentExtractRoot, - IReadOnlySet artifacts, + IEnumerable artifactFiles, string outputRoot, string platform) { @@ -211,15 +286,16 @@ public sealed class PlondsCommitDeltaBuilder var stagingRoot = Path.Combine(outputRoot, "work", platform, "staging"); PayloadUtilities.EnsureCleanDirectory(stagingRoot); - foreach (var artifact in artifacts) + foreach (var artifactFile in artifactFiles) { - var sourcePath = Path.Combine(currentExtractRoot, artifact); + var normalizedPath = artifactFile.Replace('\\', '/'); + var sourcePath = Path.Combine(currentExtractRoot, normalizedPath); if (!File.Exists(sourcePath)) { continue; } - var destPath = Path.Combine(stagingRoot, artifact); + var destPath = Path.Combine(stagingRoot, normalizedPath); var destDir = Path.GetDirectoryName(destPath); if (!string.IsNullOrWhiteSpace(destDir)) { @@ -233,6 +309,27 @@ public sealed class PlondsCommitDeltaBuilder return changedZipPath; } + private static string ValidateHashAlgorithm(string algorithm) + { + var normalized = algorithm.Trim().ToLowerInvariant(); + if (normalized is not (PlondsConstants.HashAlgorithmSha256 or PlondsConstants.HashAlgorithmMd5)) + { + throw new ArgumentException($"Unsupported hash algorithm: {algorithm}. Supported: sha256, md5"); + } + + return normalized; + } + + private static string[] ParseSourceDirs(string? sourceDirs) + { + if (string.IsNullOrWhiteSpace(sourceDirs)) + { + return PlondsConstants.DefaultSourceDirs; + } + + return sourceDirs.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + } + private static string ComputeMd5Hex(string filePath) { using var stream = File.OpenRead(filePath); diff --git a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsDeltaBuilder.cs b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsDeltaBuilder.cs index 5af200f..9504a9e 100644 --- a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsDeltaBuilder.cs +++ b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Core/Publishing/PlondsDeltaBuilder.cs @@ -17,7 +17,7 @@ public sealed class PlondsDeltaBuilder { ArgumentNullException.ThrowIfNull(options); - var hashAlgorithm = ValidateHashAlgorithmInternal(options.HashAlgorithm); + var hashAlgorithm = ValidateHashAlgorithm(options.HashAlgorithm); var currentPayloadZip = Path.GetFullPath(options.CurrentPayloadZip); if (!File.Exists(currentPayloadZip)) @@ -71,8 +71,6 @@ public sealed class PlondsDeltaBuilder Channel: options.Channel, Platform: options.Platform, UpdatedAt: DateTimeOffset.UtcNow, - CompareMethod: PlondsConstants.CompareMethodFileCompare, - HashAlgorithm: hashAlgorithm, FilesMap: filesMap, ChangedFilesMap: changedFilesMap, Checksums: new Dictionary @@ -94,7 +92,7 @@ public sealed class PlondsDeltaBuilder BaselineVersion: options.BaselineVersion); } - internal static string ValidateHashAlgorithmInternal(string algorithm) + private static string ValidateHashAlgorithm(string algorithm) { var normalized = algorithm.Trim().ToLowerInvariant(); if (normalized is not (PlondsConstants.HashAlgorithmSha256 or PlondsConstants.HashAlgorithmMd5)) @@ -115,22 +113,22 @@ public sealed class PlondsDeltaBuilder foreach (var path in currentManifest.Keys.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)) { var current = currentManifest[path]; - var currentHash = GetHash(current, hashAlgorithm); + var currentHash = ComputeHash(current.FullPath, hashAlgorithm); if (previousManifest.TryGetValue(path, out var previous)) { - var previousHash = GetHash(previous, hashAlgorithm); + var previousHash = ComputeHash(previous.FullPath, hashAlgorithm); if (string.Equals(currentHash, previousHash, StringComparison.OrdinalIgnoreCase)) { filesMap[path] = new PlondsFileEntry(PlondsConstants.ActionReuse, currentHash, current.Size, hashAlgorithm); continue; } + + filesMap[path] = new PlondsFileEntry(PlondsConstants.ActionReplace, currentHash, current.Size, hashAlgorithm); + continue; } - var action = previousManifest.ContainsKey(path) - ? PlondsConstants.ActionReplace - : PlondsConstants.ActionAdd; - filesMap[path] = new PlondsFileEntry(action, currentHash, current.Size, hashAlgorithm); + filesMap[path] = new PlondsFileEntry(PlondsConstants.ActionAdd, currentHash, current.Size, hashAlgorithm); } foreach (var path in previousManifest.Keys.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)) @@ -144,16 +142,6 @@ public sealed class PlondsDeltaBuilder return filesMap; } - private static string GetHash(PayloadUtilities.FileFingerprint fingerprint, string hashAlgorithm) - { - if (hashAlgorithm == PlondsConstants.HashAlgorithmMd5) - { - return ComputeMd5Hex(fingerprint.FullPath); - } - - return fingerprint.Sha256; - } - private static Dictionary BuildChangedFilesMap( IReadOnlyDictionary filesMap, string hashAlgorithm) @@ -228,6 +216,15 @@ public sealed class PlondsDeltaBuilder return !string.Equals(current.Sha256, previous.Sha256, StringComparison.OrdinalIgnoreCase); } + internal static string ComputeHash(string filePath, string hashAlgorithm) + { + using var stream = File.OpenRead(filePath); + var hash = hashAlgorithm == PlondsConstants.HashAlgorithmMd5 + ? MD5.HashData(stream) + : SHA256.HashData(stream); + return Convert.ToHexString(hash).ToLowerInvariant(); + } + private static string ComputeMd5Hex(string filePath) { using var stream = File.OpenRead(filePath); diff --git a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/Models/PlondsManifest.cs b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/Models/PlondsManifest.cs index c052313..b2f70df 100644 --- a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/Models/PlondsManifest.cs +++ b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/Models/PlondsManifest.cs @@ -1,5 +1,3 @@ -using System.Text.Json.Serialization; - namespace Plonds.Shared.Models; public sealed record PlondsManifest( @@ -11,9 +9,6 @@ public sealed record PlondsManifest( string Channel, string Platform, DateTimeOffset UpdatedAt, - string CompareMethod, - [property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - string? HashAlgorithm, IReadOnlyDictionary FilesMap, IReadOnlyDictionary ChangedFilesMap, IReadOnlyDictionary Checksums); diff --git a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/PlondsConstants.cs b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/PlondsConstants.cs index cecad8f..7d110c3 100644 --- a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/PlondsConstants.cs +++ b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Shared/PlondsConstants.cs @@ -11,14 +11,14 @@ public static class PlondsConstants public const string ActionReuse = "reuse"; public const string ActionDelete = "delete"; - public const string CompareMethodFileCompare = "file-compare"; - public const string CompareMethodCommitAnalyze = "commit-analyze"; - public const string HashAlgorithmSha256 = "sha256"; public const string HashAlgorithmMd5 = "md5"; public const string DefaultLauncherRelativePath = "LanMountainDesktop.Launcher.exe"; + public const string CompareMethodFileCompare = "file-compare"; + public const string CompareMethodCommitAnalyze = "commit-analyze"; + public static readonly string[] SupportedActions = [ ActionAdd, @@ -33,9 +33,14 @@ public static class PlondsConstants HashAlgorithmMd5 ]; - public static readonly string[] SupportedCompareMethods = + public static readonly string[] DefaultSourceDirs = [ - CompareMethodFileCompare, - CompareMethodCommitAnalyze + "LanMountainDesktop", + "LanMountainDesktop.Launcher", + "LanMountainDesktop.Shared.Contracts", + "LanMountainDesktop.PluginSdk", + "LanMountainDesktop.Appearance", + "LanMountainDesktop.Settings.Core", + "LanMountainDesktop.ComponentSystem" ]; } diff --git a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Tool/Program.cs b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Tool/Program.cs index bf53f3b..3908913 100644 --- a/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Tool/Program.cs +++ b/PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Tool/Program.cs @@ -73,17 +73,14 @@ internal static class PlondsCli Channel: Require(options, "channel"), BaselineTag: Require(options, "baseline-tag"), CurrentTag: Require(options, "current-tag"), + HashAlgorithm: Get(options, "hash-algorithm", "sha256") ?? "sha256", + SourceDirs: Get(options, "source-dirs"), FallbackBaselineZip: Get(options, "fallback-zip"), - BaselineVersion: Get(options, "baseline-version"), - LauncherRelativePath: Get(options, "launcher-path", "LanMountainDesktop.Launcher.exe") ?? "LanMountainDesktop.Launcher.exe", - HashAlgorithm: Get(options, "hash-algorithm", "sha256") ?? "sha256")); + LauncherRelativePath: Get(options, "launcher-path", "LanMountainDesktop.Launcher.exe") ?? "LanMountainDesktop.Launcher.exe")); Console.WriteLine($"Built PLONDS commit-delta for {result.Platform}:"); Console.WriteLine($" IsFullUpdate: {result.IsFullUpdate}"); Console.WriteLine($" RequiresCleanInstall: {result.RequiresCleanInstall}"); - Console.WriteLine($" FellBackToFileCompare: {result.FellBackToFileCompare}"); - Console.WriteLine($" ChangedSourceFiles: {result.ChangedSourceFiles.Count}"); - Console.WriteLine($" MappedArtifactFiles: {result.MappedArtifactFiles.Count}"); Console.WriteLine($" ChangedZip: {result.ChangedZipPath}"); Console.WriteLine($" Manifest: {result.ManifestPath}"); } @@ -137,8 +134,34 @@ internal static class PlondsCli private static void PrintUsage() { Console.WriteLine("PLONDS Tool"); - Console.WriteLine(" build-delta --platform

--current-version --current-zip --output-dir

[--channel ] [--baseline-version ] [--baseline-zip ] [--launcher-path ] [--hash-algorithm sha256|md5]"); - Console.WriteLine(" build-delta-from-commits --platform

--current-version --current-zip --output-dir

--channel --baseline-tag --current-tag [--fallback-zip ] [--baseline-version ] [--launcher-path ] [--hash-algorithm sha256|md5]"); - Console.WriteLine(" pack-payload --source-dir --output-zip "); + Console.WriteLine(); + Console.WriteLine("Commands:"); + Console.WriteLine(" build-delta Build delta by comparing two payload zips"); + Console.WriteLine(" --platform Platform identifier (e.g. windows-x64)"); + Console.WriteLine(" --current-version Current release version"); + Console.WriteLine(" --current-zip Current payload zip path"); + Console.WriteLine(" --output-dir Output directory"); + Console.WriteLine(" [--channel ] Update channel (default: stable)"); + Console.WriteLine(" [--baseline-version ] Baseline version"); + Console.WriteLine(" [--baseline-zip ] Baseline payload zip path"); + Console.WriteLine(" [--hash-algorithm ] sha256 or md5 (default: sha256)"); + Console.WriteLine(" [--launcher-path ] Launcher exe relative path"); + Console.WriteLine(); + Console.WriteLine(" build-delta-from-commits Build delta by analyzing git commits"); + Console.WriteLine(" --platform Platform identifier"); + Console.WriteLine(" --current-version Current release version"); + Console.WriteLine(" --current-zip Current payload zip path"); + Console.WriteLine(" --output-dir Output directory"); + Console.WriteLine(" --channel Update channel"); + Console.WriteLine(" --baseline-tag Baseline git tag"); + Console.WriteLine(" --current-tag Current git tag"); + Console.WriteLine(" [--hash-algorithm ] sha256 or md5 (default: sha256)"); + Console.WriteLine(" [--source-dirs ] Comma-separated source dirs to analyze"); + Console.WriteLine(" [--fallback-zip ] Baseline zip for fallback to file-compare"); + Console.WriteLine(" [--launcher-path ] Launcher exe relative path"); + Console.WriteLine(); + Console.WriteLine(" pack-payload Pack a directory into a payload zip"); + Console.WriteLine(" --source-dir Source directory"); + Console.WriteLine(" --output-zip Output zip path"); } } diff --git a/docs/auto_commit_md/20260530_6a65087.md b/docs/auto_commit_md/20260530_6a65087.md new file mode 100644 index 0000000..32cb764 --- /dev/null +++ b/docs/auto_commit_md/20260530_6a65087.md @@ -0,0 +1,99 @@ +# Git 提交分析报告 + +## 基本信息 +- **哈希**: 6a650873bc4ec18c59abcbde8a778ce82b3c42fc +- **短哈希**: 6a65087 +- **作者**: lincube <lincube3@hotmail.com> +- **时间**: 2026-05-30 11:56:50 +0800 + +## 提交信息摘要 +feat..去除了冗余的字体文件,又修改了PLONDS系统 + +## 变更统计 +| 指标 | 数值 | +|------|------| +| 变更文件数 | 23 | +| 新增行数 | 898 | +| 删除行数 | 357 | +| 净变化 | +541 | + +## 详细变更分析 + +### 新增文件 +1. `CheckIpcAot/CheckIpcAot.csproj` - 新增项目文件 +2. `CheckIpcAot/Program.cs` - 新增程序入口 +3. `.trae/specs/plonds-comparator-redesign/spec.md` - PLONDS 比较器重设计规格文档 + +### 删除文件 +1. `LanMountainDesktop/Assets/Fonts/MiSans-Bold.otf` - MiSans 粗体字体 +2. `LanMountainDesktop/Assets/Fonts/MiSans-Demibold.otf` - MiSans 特粗字体 +3. `LanMountainDesktop/Assets/Fonts/MiSans-ExtraLight.otf` - MiSans 特细字体 +4. `LanMountainDesktop/Assets/Fonts/MiSans-Heavy.otf` - MiSans 重字体 +5. `LanMountainDesktop/Assets/Fonts/MiSans-Light.otf` - MiSans 细字体 +6. `LanMountainDesktop/Assets/Fonts/MiSans-Medium.otf` - MiSans 中等字体 +7. `LanMountainDesktop/Assets/Fonts/MiSans-Normal.otf` - MiSans 常规字体 +8. `LanMountainDesktop/Assets/Fonts/MiSans-Regular.otf` - MiSans 标准字体 +9. `LanMountainDesktop/Assets/Fonts/MiSans-Semibold.otf` - MiSans 半粗字体 +10. `LanMountainDesktop/Assets/Fonts/MiSans-Thin.otf` - MiSans 纤细字体 +11. `LanMountainDesktop/Assets/Fonts/MiSans-NOTICE.md` - MiSans 字体说明文档 + +### 主要变更文件 +1. `.github/workflows/plonds-comparator.yml` - PLONDS 比较器工作流更新 +2. `LanMountainDesktop/App.axaml.cs` - 应用入口文件精简 +3. `LanMountainDesktop/Services/FusedDesktopManagerService.cs` - 融合桌面管理器服务 +4. `LanMountainDesktop/Views/DesktopWidgetWindow.axaml.cs` - 桌面组件窗口 +5. `LanMountainDesktop/Views/FusedDesktopComponentLibraryWindow.axaml.cs` - 融合桌面组件库窗口 +6. `LanMountainDesktop.Launcher/AppJsonContext.cs` - Launcher JSON 上下文 +7. `LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.AOT.props` - Launcher AOT 属性 +8. `LanMountainDesktop.Tests/WindowLayerIsolationTests.cs` - 窗口层隔离测试(删除) + +### 主要变更点 + +#### 1. 字体文件清理 +- **删除所有 MiSans 字体文件**:移除了 10 个不同字重的 MiSans 字体文件(Bold、Demibold、ExtraLight、Heavy、Light、Medium、Normal、Regular、Semibold、Thin) +- **删除字体说明文档**:移除了 MiSans-NOTICE.md +- **潜在目的**:减小应用体积,可能改用系统字体或其他字体方案 + +#### 2. PLONDS 系统改进 +- **工作流更新**:`.github/workflows/plonds-comparator.yml` 有 138 行新增,157 行删除 + - 移除了 `edited` 事件类型触发 + - 工作流名称保持不变 +- **新增规格文档**:`.trae/specs/plonds-comparator-redesign/spec.md` 有 512 行新内容,描述了 PLONDS 比较器的重设计 + +#### 3. 桌面组件系统调整 +- **FusedDesktopManagerService.cs**:106 行新增,41 行删除 +- **DesktopWidgetWindow.axaml.cs**:135 行新增,1 行删除 +- **FusedDesktopComponentLibraryWindow.axaml.cs**:23 行变更 +- **App.axaml.cs**:19 行新增,117 行删除(大幅精简) + +#### 4. 新增 CheckIpcAot 项目 +- 新增独立的项目 `CheckIpcAot`,包含项目文件和简单的 Program.cs +- 可能用于测试 IPC 的 AOT 兼容性 + +#### 5. Launcher 相关调整 +- `AppJsonContext.cs` 新增 5 行 +- AOT 配置文件有小幅变更 + +#### 6. 测试清理 +- 删除了 `WindowLayerIsolationTests.cs` 中的 5 行测试代码 + +## 代码审查要点 + +### 优势 +1. **体积优化**:删除大量字体文件可显著减小应用体积 +2. **PLONDS 改进**:工作流优化和重设计规格表明对发布流程有改进 +3. **代码精简**:App.axaml.cs 的大幅精简符合代码整洁原则 +4. **文档完善**:新增 PLONDS 重设计规格文档,便于理解和维护 + +### 潜在风险 +1. **字体缺失**:删除所有 MiSans 字体文件可能导致应用显示问题,需确保已提供替代方案 +2. **测试覆盖减少**:删除了窗口层隔离测试代码,需确认是否有替代测试 +3. **工作流变更**:PLONDS 工作流的修改需要验证发布流程是否仍然正常工作 +4. **兼容性**:CheckIpcAot 项目的添加可能引入新的依赖或 AOT 相关问题 + +### 建议 +1. **验证字体替代方案**:确认应用是否已配置使用系统字体或其他字体 +2. **测试 PLONDS 工作流**:手动触发一次工作流验证其正常运行 +3. **补充测试**:如果删除的测试是重要的,考虑补充替代测试 +4. **验证桌面组件功能**:重点测试桌面组件窗口和管理器的变更是否正常工作 +5. **检查 AOT 配置**:确保新增的 CheckIpcAot 项目和 Launcher AOT 变更不会影响现有 AOT 构建