diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ArtifactWriter.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ArtifactWriter.cs new file mode 100644 index 00000000000000..331e8b28fb771f --- /dev/null +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ArtifactWriter.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.WebAssembly; + +public static class ArtifactWriter +{ + public static bool PersistFileIfChanged(TaskLoggingHelper log, T manifest, string artifactPath, JsonTypeInfo serializer) + { + var data = JsonSerializer.SerializeToUtf8Bytes(manifest, serializer); + return PersistFileIfChanged(log, data, artifactPath); + } + + public static bool PersistFileIfChanged(TaskLoggingHelper log, byte[] data, string artifactPath) + { + var newHash = ComputeHash(data); + var fileExists = File.Exists(artifactPath); + var existingManifestHash = fileExists ? ComputeHash(artifactPath) : null; + + if (!fileExists) + { + log.LogMessage(MessageImportance.Low, $"Creating artifact because artifact file '{artifactPath}' does not exist."); + File.WriteAllBytes(artifactPath, data); + return true; + } + else if (!string.Equals(newHash, existingManifestHash, StringComparison.Ordinal)) + { + log.LogMessage(MessageImportance.Low, $"Updating artifact because artifact version '{newHash}' is different from existing artifact hash '{existingManifestHash}'."); + File.WriteAllBytes(artifactPath, data); + return true; + } + else + { + log.LogMessage(MessageImportance.Low, $"Skipping artifact updated because artifact version '{existingManifestHash}' has not changed."); + return false; + } + } + + private static string ComputeHash(string artifactPath) => ComputeHash(File.ReadAllBytes(artifactPath)); + + private static string ComputeHash(byte[] data) + { +#if NET6_0_OR_GREATER + var hash = SHA256.HashData(data); + return Convert.ToBase64String(hash); +#else + using var sha256 = SHA256.Create(); + return Convert.ToBase64String(sha256.ComputeHash(data)); +#endif + } +} diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs index 93ead55e48c113..86b52178de5ccb 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs @@ -87,7 +87,7 @@ public void WriteConfigToFile(BootJsonData config, string outputPath, string? ou output = $"export const config = /*json-start*/{output}/*json-end*/;"; } - File.WriteAllText(outputPath, output); + ArtifactWriter.PersistFileIfChanged(Log, Encoding.UTF8.GetBytes(output), outputPath); } private string ReplaceWithAssert(Regex regex, string content, string replacement, string errorMessage) diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index addcf203c6a2eb..77bdf6cafb6789 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -21,6 +21,7 @@ +