From 1c1c6d18634f0236b446aef3c98fdec619fef435 Mon Sep 17 00:00:00 2001 From: Fan Yang Date: Fri, 12 Mar 2021 12:33:37 -0500 Subject: [PATCH 01/10] Add msbuild task to generate binary runtimeconfig format --- Directory.Build.props | 2 + .../RuntimeconfigParser.cs | 108 ++++++++++++++++++ .../RuntimeconfigParser.csproj | 27 +++++ 3 files changed, 137 insertions(+) create mode 100644 src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs create mode 100644 src/tasks/RuntimeconfigParser/RuntimeconfigParser.csproj diff --git a/Directory.Build.props b/Directory.Build.props index 5736fce3341c2..5bddb1aee2d15 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -70,6 +70,7 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(NetCoreAppToolCurrent)')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'RuntimeconfigParser', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'installer.tasks.dll')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', 'net461', 'installer.tasks.dll')) @@ -78,6 +79,7 @@ $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) + $([MSBuild]::NormalizePath('$(RuntimeconfigParserDir)', 'RuntimeconfigParser.dll')) diff --git a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs new file mode 100644 index 0000000000000..44831d9176bc7 --- /dev/null +++ b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs @@ -0,0 +1,108 @@ +// 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.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Reflection.Metadata; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + + +public class RuntimeconfigParserTask : Task +{ + /// + /// The path to runtimeconfig.json file. + /// + [Required] + public string runtimeConfigFile { get; set; } = ""!; + + /// + /// The path to the output binary file. + /// + [Required] + public string outputFile { get; set; } = ""!; + + /// + /// List of reserved properties. + /// + public ITaskItem[] reservedProperties { get; set; } = Array.Empty(); + + public override bool Execute() + { + if (string.IsNullOrEmpty(runtimeConfigFile)) + { + throw new ArgumentException($"'{nameof(runtimeConfigFile)}' is required.", nameof(runtimeConfigFile)); + } + + if (string.IsNullOrEmpty(outputFile)) + { + throw new ArgumentException($"'{nameof(outputFile)}' is required.", nameof(outputFile)); + } + + Dictionary configProperties = ConvertInputToDictionary (runtimeConfigFile); + + if (reservedProperties.Length != 0) + { + checkDuplicateProperties (configProperties, reservedProperties); + } + + var blobBuilder = new BlobBuilder(); + ConvertDictionaryToBlob (configProperties, blobBuilder); + + using var stream = File.OpenWrite(outputFile); + blobBuilder.WriteContentTo(stream); + + return true; + } + + /// Reads a json file from the given path and extracts the "configProperties" key (assumed to be a string to string dictionary) + private Dictionary ConvertInputToDictionary(string inputFilePath) + { + var options = new JsonSerializerOptions { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + }; + + var jsonString = File.ReadAllText(inputFilePath); + var parsedJson = JsonSerializer.Deserialize(jsonString, options); + + return parsedJson!.ConfigProperties; + } + + /// Just write the dictionary out to a blob as a count followed by + /// a length-prefixed UTF8 encoding of each key and value + private void ConvertDictionaryToBlob (IReadOnlyDictionary properties, BlobBuilder b) + { + int count = properties.Count; + + b.WriteCompressedInteger (count); + foreach (var kvp in properties) + { + b.WriteSerializedString (kvp.Key); + b.WriteSerializedString (kvp.Value); + } + } + + private void checkDuplicateProperties (IReadOnlyDictionary properties, ITaskItem[] keys) + { + foreach (var key in keys) + { + if (properties.ContainsKey (key.ItemSpec)) + { + throw new ArgumentException($"Property '{key}' can't be set by the user!"); + } + } + } +} + +public class Root { + // the configProperties key + [JsonPropertyName ("configProperties")] + public Dictionary ConfigProperties {get; set;} = new Dictionary (); + // everything other than configProperties + [JsonExtensionData] + public Dictionary ExtensionData {get; set;} = new Dictionary (); +} diff --git a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.csproj b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.csproj new file mode 100644 index 0000000000000..993ef0f14d9e0 --- /dev/null +++ b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.csproj @@ -0,0 +1,27 @@ + + + $(NetCoreAppToolCurrent) + Library + true + false + enable + $(NoWarn),CA1050 + + + + + + + + + + + + + + + + + From 75f407665c9a2361c3f675a7d5f55f95c33973b9 Mon Sep 17 00:00:00 2001 From: Fan Yang Date: Fri, 12 Mar 2021 13:13:07 -0500 Subject: [PATCH 02/10] Update property name due to naming conversion. --- .../RuntimeconfigParser.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs index 44831d9176bc7..fb59571d817f8 100644 --- a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs +++ b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs @@ -17,42 +17,42 @@ public class RuntimeconfigParserTask : Task /// The path to runtimeconfig.json file. /// [Required] - public string runtimeConfigFile { get; set; } = ""!; + public string RuntimeConfigFile { get; set; } = ""!; /// /// The path to the output binary file. /// [Required] - public string outputFile { get; set; } = ""!; + public string OutputFile { get; set; } = ""!; /// /// List of reserved properties. /// - public ITaskItem[] reservedProperties { get; set; } = Array.Empty(); + public ITaskItem[] ReservedProperties { get; set; } = Array.Empty(); public override bool Execute() { - if (string.IsNullOrEmpty(runtimeConfigFile)) + if (string.IsNullOrEmpty(RuntimeConfigFile)) { - throw new ArgumentException($"'{nameof(runtimeConfigFile)}' is required.", nameof(runtimeConfigFile)); + throw new ArgumentException($"'{nameof(RuntimeConfigFile)}' is required.", nameof(RuntimeConfigFile)); } - if (string.IsNullOrEmpty(outputFile)) + if (string.IsNullOrEmpty(OutputFile)) { - throw new ArgumentException($"'{nameof(outputFile)}' is required.", nameof(outputFile)); + throw new ArgumentException($"'{nameof(OutputFile)}' is required.", nameof(OutputFile)); } - Dictionary configProperties = ConvertInputToDictionary (runtimeConfigFile); + Dictionary configProperties = ConvertInputToDictionary (RuntimeConfigFile); - if (reservedProperties.Length != 0) + if (ReservedProperties.Length != 0) { - checkDuplicateProperties (configProperties, reservedProperties); + checkDuplicateProperties (configProperties, ReservedProperties); } var blobBuilder = new BlobBuilder(); ConvertDictionaryToBlob (configProperties, blobBuilder); - using var stream = File.OpenWrite(outputFile); + using var stream = File.OpenWrite(OutputFile); blobBuilder.WriteContentTo(stream); return true; From 8ccb9c12af35d57e11abe9df81ba56103bd9e903 Mon Sep 17 00:00:00 2001 From: Fan Yang Date: Fri, 12 Mar 2021 14:04:25 -0500 Subject: [PATCH 03/10] Fixed more formatting issue --- .../RuntimeconfigParser.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs index fb59571d817f8..0330267896a0a 100644 --- a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs +++ b/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs @@ -10,20 +10,19 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; - -public class RuntimeconfigParserTask : Task +public class RuntimeConfigParserTask : Task { /// /// The path to runtimeconfig.json file. /// [Required] - public string RuntimeConfigFile { get; set; } = ""!; + public string RuntimeConfigFile { get; set; } = ""; /// /// The path to the output binary file. /// [Required] - public string OutputFile { get; set; } = ""!; + public string OutputFile { get; set; } = ""; /// /// List of reserved properties. @@ -42,15 +41,15 @@ public override bool Execute() throw new ArgumentException($"'{nameof(OutputFile)}' is required.", nameof(OutputFile)); } - Dictionary configProperties = ConvertInputToDictionary (RuntimeConfigFile); + Dictionary configProperties = ConvertInputToDictionary(RuntimeConfigFile); if (ReservedProperties.Length != 0) { - checkDuplicateProperties (configProperties, ReservedProperties); + checkDuplicateProperties(configProperties, ReservedProperties); } var blobBuilder = new BlobBuilder(); - ConvertDictionaryToBlob (configProperties, blobBuilder); + ConvertDictionaryToBlob(configProperties, blobBuilder); using var stream = File.OpenWrite(OutputFile); blobBuilder.WriteContentTo(stream); @@ -74,23 +73,23 @@ private Dictionary ConvertInputToDictionary(string inputFilePath /// Just write the dictionary out to a blob as a count followed by /// a length-prefixed UTF8 encoding of each key and value - private void ConvertDictionaryToBlob (IReadOnlyDictionary properties, BlobBuilder b) + private void ConvertDictionaryToBlob(IReadOnlyDictionary properties, BlobBuilder b) { int count = properties.Count; - b.WriteCompressedInteger (count); + b.WriteCompressedInteger(count); foreach (var kvp in properties) { - b.WriteSerializedString (kvp.Key); - b.WriteSerializedString (kvp.Value); + b.WriteSerializedString(kvp.Key); + b.WriteSerializedString(kvp.Value); } } - private void checkDuplicateProperties (IReadOnlyDictionary properties, ITaskItem[] keys) + private void checkDuplicateProperties(IReadOnlyDictionary properties, ITaskItem[] keys) { foreach (var key in keys) { - if (properties.ContainsKey (key.ItemSpec)) + if (properties.ContainsKey(key.ItemSpec)) { throw new ArgumentException($"Property '{key}' can't be set by the user!"); } @@ -98,7 +97,8 @@ private void checkDuplicateProperties (IReadOnlyDictionary prope } } -public class Root { +public class Root +{ // the configProperties key [JsonPropertyName ("configProperties")] public Dictionary ConfigProperties {get; set;} = new Dictionary (); From 47c019209112d676440f0cf547302b369fd6c516 Mon Sep 17 00:00:00 2001 From: Fan Yang Date: Fri, 12 Mar 2021 14:21:31 -0500 Subject: [PATCH 04/10] Fixed one more naming convention --- Directory.Build.props | 4 ++-- .../RuntimeConfigParser.cs} | 0 .../RuntimeConfigParser.csproj} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/tasks/{RuntimeconfigParser/RuntimeconfigParser.cs => RuntimeConfigParser/RuntimeConfigParser.cs} (100%) rename src/tasks/{RuntimeconfigParser/RuntimeconfigParser.csproj => RuntimeConfigParser/RuntimeConfigParser.csproj} (95%) diff --git a/Directory.Build.props b/Directory.Build.props index 5bddb1aee2d15..f971fa0a774fc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -70,7 +70,7 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(NetCoreAppToolCurrent)')) - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'RuntimeconfigParser', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'RuntimeConfigParser', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'installer.tasks.dll')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', 'net461', 'installer.tasks.dll')) @@ -79,7 +79,7 @@ $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) - $([MSBuild]::NormalizePath('$(RuntimeconfigParserDir)', 'RuntimeconfigParser.dll')) + $([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll')) diff --git a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs similarity index 100% rename from src/tasks/RuntimeconfigParser/RuntimeconfigParser.cs rename to src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs diff --git a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.csproj b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj similarity index 95% rename from src/tasks/RuntimeconfigParser/RuntimeconfigParser.csproj rename to src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj index 993ef0f14d9e0..39204be9b0bbf 100644 --- a/src/tasks/RuntimeconfigParser/RuntimeconfigParser.csproj +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj @@ -15,7 +15,7 @@ - + Date: Mon, 15 Mar 2021 14:53:31 -0400 Subject: [PATCH 05/10] Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs Co-authored-by: Ryan Lucia --- src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs index 0330267896a0a..6eff7278fce64 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs @@ -100,7 +100,7 @@ private void checkDuplicateProperties(IReadOnlyDictionary proper public class Root { // the configProperties key - [JsonPropertyName ("configProperties")] + [JsonPropertyName("configProperties")] public Dictionary ConfigProperties {get; set;} = new Dictionary (); // everything other than configProperties [JsonExtensionData] From 3a3a63a270bb987acbdb423ec16a367891c12219 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Mon, 15 Mar 2021 14:53:41 -0400 Subject: [PATCH 06/10] Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs Co-authored-by: Ryan Lucia --- src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs index 6eff7278fce64..c3fc7bff61f3e 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs @@ -101,7 +101,7 @@ public class Root { // the configProperties key [JsonPropertyName("configProperties")] - public Dictionary ConfigProperties {get; set;} = new Dictionary (); + public Dictionary ConfigProperties { get; set; } = new Dictionary(); // everything other than configProperties [JsonExtensionData] public Dictionary ExtensionData {get; set;} = new Dictionary (); From 24553e3d68b96b82d71818c626ce491ca4361841 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Mon, 15 Mar 2021 14:53:48 -0400 Subject: [PATCH 07/10] Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs Co-authored-by: Ryan Lucia --- src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs index c3fc7bff61f3e..06cab5860e2d4 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs @@ -104,5 +104,5 @@ public class Root public Dictionary ConfigProperties { get; set; } = new Dictionary(); // everything other than configProperties [JsonExtensionData] - public Dictionary ExtensionData {get; set;} = new Dictionary (); + public Dictionary ExtensionData { get; set; } = new Dictionary(); } From fc22558a9146e3a0773e25fc0c88d486aebdce82 Mon Sep 17 00:00:00 2001 From: Fan Yang Date: Mon, 15 Mar 2021 15:35:25 -0400 Subject: [PATCH 08/10] Fixed comments --- .../RuntimeConfigParser/RuntimeConfigParser.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs index 06cab5860e2d4..c7c03c6595428 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs @@ -25,7 +25,7 @@ public class RuntimeConfigParserTask : Task public string OutputFile { get; set; } = ""; /// - /// List of reserved properties. + /// List of properties reserved for the host. /// public ITaskItem[] ReservedProperties { get; set; } = Array.Empty(); @@ -68,20 +68,25 @@ private Dictionary ConvertInputToDictionary(string inputFilePath var jsonString = File.ReadAllText(inputFilePath); var parsedJson = JsonSerializer.Deserialize(jsonString, options); - return parsedJson!.ConfigProperties; + if (parsedJson == null) + { + throw new ArgumentException("Wasn't able to parse the json file successfully."); + } + + return parsedJson.ConfigProperties; } /// Just write the dictionary out to a blob as a count followed by /// a length-prefixed UTF8 encoding of each key and value - private void ConvertDictionaryToBlob(IReadOnlyDictionary properties, BlobBuilder b) + private void ConvertDictionaryToBlob(IReadOnlyDictionary properties, BlobBuilder builder) { int count = properties.Count; - b.WriteCompressedInteger(count); + builder.WriteCompressedInteger(count); foreach (var kvp in properties) { - b.WriteSerializedString(kvp.Key); - b.WriteSerializedString(kvp.Value); + builder.WriteSerializedString(kvp.Key); + builder.WriteSerializedString(kvp.Value); } } From 4f6ac11f2c9d453be376bd2ec57dd49c6b77eebc Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Tue, 16 Mar 2021 09:36:35 -0400 Subject: [PATCH 09/10] Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs Co-authored-by: Dan Moseley --- src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs index c7c03c6595428..6619245efd5cd 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs @@ -90,7 +90,7 @@ private void ConvertDictionaryToBlob(IReadOnlyDictionary propert } } - private void checkDuplicateProperties(IReadOnlyDictionary properties, ITaskItem[] keys) + private void CheckDuplicateProperties(IReadOnlyDictionary properties, ITaskItem[] keys) { foreach (var key in keys) { From 218bca6364e4b0a8f3b96e5b825667ff17a7176c Mon Sep 17 00:00:00 2001 From: Fan Yang Date: Tue, 16 Mar 2021 10:01:10 -0400 Subject: [PATCH 10/10] Fix error handling --- src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs index 6619245efd5cd..12cbf59442f5f 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs @@ -33,19 +33,19 @@ public override bool Execute() { if (string.IsNullOrEmpty(RuntimeConfigFile)) { - throw new ArgumentException($"'{nameof(RuntimeConfigFile)}' is required.", nameof(RuntimeConfigFile)); + Log.LogError($"'{nameof(RuntimeConfigFile)}' is required."); } if (string.IsNullOrEmpty(OutputFile)) { - throw new ArgumentException($"'{nameof(OutputFile)}' is required.", nameof(OutputFile)); + Log.LogError($"'{nameof(OutputFile)}' is required."); } Dictionary configProperties = ConvertInputToDictionary(RuntimeConfigFile); if (ReservedProperties.Length != 0) { - checkDuplicateProperties(configProperties, ReservedProperties); + CheckDuplicateProperties(configProperties, ReservedProperties); } var blobBuilder = new BlobBuilder(); @@ -54,7 +54,7 @@ public override bool Execute() using var stream = File.OpenWrite(OutputFile); blobBuilder.WriteContentTo(stream); - return true; + return !Log.HasLoggedErrors; } /// Reads a json file from the given path and extracts the "configProperties" key (assumed to be a string to string dictionary)