From c5fede356db2daedbb231a44452487010841a9dc Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sat, 3 Jul 2021 16:09:44 -0700 Subject: [PATCH 01/17] fix race condition --- .../Fixtures/Tasks/ScssProcessorTaskFixture.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Tasks/ScssProcessorTaskFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Tasks/ScssProcessorTaskFixture.cs index 51dc05f8..58222cab 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/Tasks/ScssProcessorTaskFixture.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Tasks/ScssProcessorTaskFixture.cs @@ -9,6 +9,12 @@ namespace Mobile.BuildTools.Tests.Fixtures.Tasks { + public class Scss { } + + [CollectionDefinition(nameof(Scss), DisableParallelization = true)] + public class ScssCollection : ICollectionFixture { } + + [Collection(nameof(Scss))] public class ScssProcessorTaskFixture { private const string OutputFolder = "Generated"; @@ -211,4 +217,4 @@ private void ResetOutputFolder() Directory.CreateDirectory(OutputFolder); } } -} \ No newline at end of file +} From 9139a70f0d5729fa44f2b70beb122db1cc9eea3a Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sat, 3 Jul 2021 19:20:03 -0700 Subject: [PATCH 02/17] convert to app settings --- .../Build/BuildConfiguration.cs | 7 +- .../Build/IBuildConfiguration.cs | 9 +- src/Mobile.BuildTools.Reference/Constants.cs | 2 - .../Extensions/DictionaryExtensions.cs | 6 ++ .../Models/BuildToolsConfig.cs | 5 + .../Models/Secrets/SecretsConfig.cs | 1 + .../{Secrets => Settings}/Accessibility.cs | 2 +- .../{Secrets => Settings}/PropertyType.cs | 2 +- .../PropertyTypeExtensions.cs | 2 +- .../PropertyTypeMappingAttribute.cs | 2 +- .../Models/Settings/SettingsConfig.cs | 46 ++++++++ .../{Secrets => Settings}/ValueConfig.cs | 2 +- .../Utils/ConfigHelper.cs | 22 ++-- .../Secrets/BuildHostSecretsGenerator.cs | 11 +- .../Secrets/SecretsClassGenerator.cs | 100 ++++++++---------- .../Tasks/BuildEnvironmentDumpTask.cs | 5 +- .../Tasks/BuildHostSecretsTask.cs | 2 +- .../Tasks/BuildToolsTaskBase.cs | 7 +- .../Tasks/LocateBuildToolsConfigTask.cs | 56 ++++++++-- .../Tasks/SecretsJsonTask.cs | 11 +- .../Data/SecretsData.cs | 2 +- .../BuildHostSecretsGeneratorFixture.cs | 7 +- .../SecretsClassGeneratorFixture.cs | 53 ++++++---- .../Fixtures/Utils/BuildToolsConfigFixture.cs | 4 +- .../Mocks/TestBuildConfiguration.cs | 24 +++-- 25 files changed, 246 insertions(+), 144 deletions(-) rename src/Mobile.BuildTools.Reference/Models/{Secrets => Settings}/Accessibility.cs (65%) rename src/Mobile.BuildTools.Reference/Models/{Secrets => Settings}/PropertyType.cs (97%) rename src/Mobile.BuildTools.Reference/Models/{Secrets => Settings}/PropertyTypeExtensions.cs (92%) rename src/Mobile.BuildTools.Reference/Models/{Secrets => Settings}/PropertyTypeMappingAttribute.cs (94%) create mode 100644 src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs rename src/Mobile.BuildTools.Reference/Models/{Secrets => Settings}/ValueConfig.cs (90%) diff --git a/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs b/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs index e5d5902d..b0d2603c 100644 --- a/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs @@ -4,7 +4,8 @@ using System.Linq; using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; namespace Mobile.BuildTools.Build @@ -37,8 +38,8 @@ public BuildConfiguration(string projectDirectory, string intermediateOutputDire public BuildToolsConfig Configuration { get; } string IBuildConfiguration.BuildConfiguration { get; } - public SecretsConfig GetSecretsConfig() => - ConfigHelper.GetSecretsConfig(this); + public IEnumerable GetSettingsConfig() => + ConfigHelper.GetSettingsConfig(this); public void SaveConfiguration() => ConfigHelper.SaveConfig(Configuration, _configPath); diff --git a/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs b/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs index 238c4fa6..f4341863 100644 --- a/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using Mobile.BuildTools.Logging; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; using Mobile.BuildTools.Models; -using Mobile.BuildTools.Utils; - +using Mobile.BuildTools.Utils; +using Mobile.BuildTools.Reference.Models.Settings; + namespace Mobile.BuildTools.Build { public interface IBuildConfiguration @@ -30,6 +31,6 @@ public interface IBuildConfiguration void SaveConfiguration(); - SecretsConfig GetSecretsConfig(); + IEnumerable GetSettingsConfig(); } } diff --git a/src/Mobile.BuildTools.Reference/Constants.cs b/src/Mobile.BuildTools.Reference/Constants.cs index 84122857..89c90ad0 100644 --- a/src/Mobile.BuildTools.Reference/Constants.cs +++ b/src/Mobile.BuildTools.Reference/Constants.cs @@ -6,8 +6,6 @@ public static class Constants public const string SecretsJsonConfigurationFileFormat = "secrets.{0}.json"; - public const string SecretsConfigFileName = "secretsconfig.json"; - public const string BuildToolsConfigFileName = "buildtools.json"; public const string ManifestJsonFileName = "manifest.json"; diff --git a/src/Mobile.BuildTools.Reference/Extensions/DictionaryExtensions.cs b/src/Mobile.BuildTools.Reference/Extensions/DictionaryExtensions.cs index 95aaa265..1c6edc98 100644 --- a/src/Mobile.BuildTools.Reference/Extensions/DictionaryExtensions.cs +++ b/src/Mobile.BuildTools.Reference/Extensions/DictionaryExtensions.cs @@ -7,5 +7,11 @@ public static void Deconstruct(this KeyValuePair tuple, out T1 k key = tuple.Key; value = tuple.Value; } + + public static void ForEach(this IEnumerable items, Action action) + { + foreach (var item in items) + action(item); + } } } diff --git a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs index 9da62707..270bfb04 100644 --- a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Reference.Models.Settings; using Newtonsoft.Json; namespace Mobile.BuildTools.Models @@ -38,6 +40,9 @@ public string Schema [JsonProperty("projectSecrets")] public Dictionary ProjectSecrets { get; set; } + [JsonProperty("appSettings")] + public Dictionary> AppSettings { get; set; } + [JsonProperty("environment")] public EnvironmentSettings Environment { get; set; } diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs index 27c1b7b6..48853b4a 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Mobile.BuildTools.Models.Settings; using Newtonsoft.Json; namespace Mobile.BuildTools.Models.Secrets diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/Accessibility.cs b/src/Mobile.BuildTools.Reference/Models/Settings/Accessibility.cs similarity index 65% rename from src/Mobile.BuildTools.Reference/Models/Secrets/Accessibility.cs rename to src/Mobile.BuildTools.Reference/Models/Settings/Accessibility.cs index 22f370f5..a504ad0e 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/Accessibility.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/Accessibility.cs @@ -1,5 +1,5 @@  -namespace Mobile.BuildTools.Models.Secrets +namespace Mobile.BuildTools.Models.Settings { public enum Accessibility { diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/PropertyType.cs b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyType.cs similarity index 97% rename from src/Mobile.BuildTools.Reference/Models/Secrets/PropertyType.cs rename to src/Mobile.BuildTools.Reference/Models/Settings/PropertyType.cs index 1c13cb0b..53e627c6 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/PropertyType.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyType.cs @@ -1,7 +1,7 @@ using System; using Mobile.BuildTools.Handlers; -namespace Mobile.BuildTools.Models.Secrets +namespace Mobile.BuildTools.Models.Settings { public enum PropertyType { diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/PropertyTypeExtensions.cs b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyTypeExtensions.cs similarity index 92% rename from src/Mobile.BuildTools.Reference/Models/Secrets/PropertyTypeExtensions.cs rename to src/Mobile.BuildTools.Reference/Models/Settings/PropertyTypeExtensions.cs index 15085ef3..987f5fd6 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/PropertyTypeExtensions.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyTypeExtensions.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Reflection; -namespace Mobile.BuildTools.Models.Secrets +namespace Mobile.BuildTools.Models.Settings { public static class PropertyTypeExtensions { diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/PropertyTypeMappingAttribute.cs b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyTypeMappingAttribute.cs similarity index 94% rename from src/Mobile.BuildTools.Reference/Models/Secrets/PropertyTypeMappingAttribute.cs rename to src/Mobile.BuildTools.Reference/Models/Settings/PropertyTypeMappingAttribute.cs index 7c69c2dc..1a80888a 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/PropertyTypeMappingAttribute.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyTypeMappingAttribute.cs @@ -1,7 +1,7 @@ using System; using Mobile.BuildTools.Handlers; -namespace Mobile.BuildTools.Models.Secrets +namespace Mobile.BuildTools.Models.Settings { [AttributeUsage(AttributeTargets.Field)] public sealed class PropertyTypeMappingAttribute : Attribute diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs new file mode 100644 index 00000000..05eefbfc --- /dev/null +++ b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using Mobile.BuildTools.Models.Settings; +using Newtonsoft.Json; + +namespace Mobile.BuildTools.Reference.Models.Settings +{ + public class SettingsConfig + { + public SettingsConfig() + { + Properties = new List(); + } + + [JsonProperty("delimiter")] + public string Delimiter { get; set; } + + [JsonProperty("prefix")] + public string Prefix { get; set; } + + [JsonProperty("className")] + public string ClassName { get; set; } + + [JsonProperty("accessibility")] + public Accessibility Accessibility { get; set; } + + [JsonProperty("rootNamespace")] + public string RootNamespace { get; set; } + + [JsonProperty("namespace")] + public string Namespace { get; set; } + + [JsonProperty("properties")] + public List Properties { get; set; } + + public bool ContainsKey(string key) => Properties != null && Properties.Any(x => x.Name == key); + + public bool HasKey(string key, out ValueConfig config) + { + config = this[key]; + return config != null; + } + + public ValueConfig this[string key] => Properties?.FirstOrDefault(x => x.Name == key); + } +} diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/ValueConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs similarity index 90% rename from src/Mobile.BuildTools.Reference/Models/Secrets/ValueConfig.cs rename to src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs index 96a36b2f..b32f0076 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/ValueConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Mobile.BuildTools.Models.Secrets +namespace Mobile.BuildTools.Models.Settings { public class ValueConfig { diff --git a/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs b/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs index ac572b35..9ee70503 100644 --- a/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs +++ b/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs @@ -4,8 +4,9 @@ using Mobile.BuildTools.Build; using System.Linq; using Mobile.BuildTools.Models; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; using Newtonsoft.Json; +using Mobile.BuildTools.Reference.Models.Settings; namespace Mobile.BuildTools.Utils { @@ -115,22 +116,15 @@ private static string GetRelativePath(string filespec, string folder) return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); } - public static SecretsConfig GetSecretsConfig(IBuildConfiguration buildConfiguration) => - GetSecretsConfig(buildConfiguration.ProjectName, buildConfiguration.ProjectDirectory, buildConfiguration.Configuration); + public static IEnumerable GetSettingsConfig(IBuildConfiguration buildConfiguration) => + GetSettingsConfig(buildConfiguration.ProjectName, buildConfiguration.Configuration); - public static SecretsConfig GetSecretsConfig(string projectName, string projectDir, BuildToolsConfig config) + public static IEnumerable GetSettingsConfig(string projectName, BuildToolsConfig config) { - var configPath = Path.Combine(projectDir, Constants.SecretsConfigFileName); - if (File.Exists(configPath)) - { - var json = File.ReadAllText(configPath); - return JsonConvert.DeserializeObject(json); - } - - if (config.ProjectSecrets != null && config.ProjectSecrets.Any(x => x.Key == projectName)) - return config.ProjectSecrets.First(x => x.Key == projectName).Value; + if (config.AppSettings != null && config.AppSettings.Any(x => x.Key == projectName)) + return config.AppSettings[projectName]; - return new SecretsConfig { Disable = true }; + return Array.Empty(); } } } diff --git a/src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs b/src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs index 8ae25b43..f9c1b93b 100644 --- a/src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs +++ b/src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs @@ -19,8 +19,15 @@ public BuildHostSecretsGenerator(IBuildConfiguration buildConfiguration) protected override void ExecuteInternal() { - var config = Build.GetSecretsConfig(); - var secrets = GetSecrets(config?.Prefix); + var configs = Build.GetSettingsConfig(); + var secrets = new Dictionary(); + foreach(var config in configs) + { + foreach((var key, var value) in GetSecrets(config?.Prefix)) + { + secrets.Add(key, value); + } + } if (!secrets.Any()) { diff --git a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs index 5f439071..b88b6b24 100644 --- a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs +++ b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs @@ -8,14 +8,15 @@ using Microsoft.Build.Utilities; using Mobile.BuildTools.Build; using Mobile.BuildTools.Handlers; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Mobile.BuildTools.Generators.Secrets { - internal class SecretsClassGenerator : GeneratorBase + internal class SecretsClassGenerator : GeneratorBase> { #pragma warning disable IDE1006, IDE0040 private string CompileGeneratedAttribute { get; } @@ -30,23 +31,31 @@ public SecretsClassGenerator(IBuildConfiguration buildConfiguration, params stri CompileGeneratedAttribute = @$"[GeneratedCodeAttribute(""Mobile.BuildTools.Generators.Secrets.SecretsClassGenerator"", ""{toolVersion}"")]"; SecretsFileLocations = secretsFileLocations; + Outputs = new List(); } #pragma warning restore IDE1006, IDE0040 - public string BaseNamespace { get; set; } + public string RootNamespace { get; set; } protected override void ExecuteInternal() { - var secretsConfig = Build.GetSecretsConfig(); + var settingsConfig = Build.GetSettingsConfig(); + foreach(var settings in settingsConfig) + { + ProcessSettingsConfig(settings); + } + } + private void ProcessSettingsConfig(SettingsConfig settingsConfig) + { if (!SecretsFileLocations.Any(x => File.Exists(x))) { Log.LogMessage("No secrets.json was found in either the Project or Solution Directory."); - if(secretsConfig.Properties.Any()) + if (settingsConfig.Properties.Any()) { var jObject = new JObject(); - foreach (var propConfig in secretsConfig.Properties) + foreach (var propConfig in settingsConfig.Properties) { jObject[propConfig.Name] = $"{propConfig.PropertyType}"; Log.LogError($"Missing Secret parameter: {propConfig.Name}"); @@ -63,45 +72,33 @@ protected override void ExecuteInternal() var replacement = string.Empty; var safeReplacement = string.Empty; - var saveConfig = secretsConfig is null; - if(saveConfig) - { - secretsConfig = new SecretsConfig() - { - ClassName = "Secrets", - Namespace = "Helpers", - Delimiter = ";", - Prefix = "BuildTools_", - Properties = new List() - }; - } - if (string.IsNullOrEmpty(secretsConfig.ClassName)) - secretsConfig.ClassName = "Secrets"; + if (string.IsNullOrEmpty(settingsConfig.ClassName)) + settingsConfig.ClassName = "Secrets"; - if (string.IsNullOrEmpty(secretsConfig.Namespace)) - secretsConfig.Namespace = "Helpers"; + if (string.IsNullOrEmpty(settingsConfig.Namespace)) + settingsConfig.Namespace = "Helpers"; - if (string.IsNullOrEmpty(secretsConfig.Delimiter)) - secretsConfig.Delimiter = ";"; + if (string.IsNullOrEmpty(settingsConfig.Delimiter)) + settingsConfig.Delimiter = ";"; - if (string.IsNullOrEmpty(secretsConfig.Prefix)) - secretsConfig.Prefix = "BuildTools_"; + if (string.IsNullOrEmpty(settingsConfig.Prefix)) + settingsConfig.Prefix = "BuildTools_"; - foreach (var propConfig in secretsConfig.Properties) + foreach (var propConfig in settingsConfig.Properties) { if (string.IsNullOrEmpty(propConfig.DefaultValue)) continue; - if (secrets.ContainsKey(propConfig.Name) || secrets.ContainsKey($"{secretsConfig.Prefix}{propConfig.Name}")) + if (secrets.ContainsKey(propConfig.Name) || secrets.ContainsKey($"{settingsConfig.Prefix}{propConfig.Name}")) continue; secrets.Add(propConfig.Name, propConfig.DefaultValue == "null" || propConfig.DefaultValue == "default" ? null : propConfig.DefaultValue); } var hasErrors = false; - foreach(var propConfig in secretsConfig.Properties) + foreach(var propConfig in settingsConfig.Properties) { - if (!secrets.ContainsKey(propConfig.Name) && !secrets.ContainsKey($"{secretsConfig.Prefix}{propConfig.Name}")) + if (!secrets.ContainsKey(propConfig.Name) && !secrets.ContainsKey($"{settingsConfig.Prefix}{propConfig.Name}")) { hasErrors = true; Log.LogError($"Missing Secret parameter: {propConfig.Name}"); @@ -111,22 +108,12 @@ protected override void ExecuteInternal() if (hasErrors) return; - if(saveConfig) - { - if (Build.Configuration.ProjectSecrets is null) - { - Build.Configuration.ProjectSecrets = new Dictionary(); - } - Build.Configuration.ProjectSecrets.Add(Build.ProjectName, secretsConfig); - Build.SaveConfiguration(); - } - - var writer = GenerateClass(secretsConfig, secrets); + var writer = GenerateClass(settingsConfig, secrets); var secretsClass = writer.ToString(); Log.LogMessage(Build.Configuration.Debug ? secretsClass : writer.SafeOutput); - var namespacePath = string.Join($"{Path.PathSeparator}", secretsConfig.Namespace.Split('.').Where(x => !string.IsNullOrEmpty(x))); - var fileName = $"{secretsConfig.ClassName}.g.cs"; + var namespacePath = string.Join($"{Path.PathSeparator}", settingsConfig.Namespace.Split('.').Where(x => !string.IsNullOrEmpty(x))); + var fileName = $"{settingsConfig.ClassName}.g.cs"; var projectFile = Path.Combine(namespacePath, fileName); var intermediateFile = Path.Combine(Build.IntermediateOutputPath, namespacePath, fileName); @@ -136,10 +123,10 @@ protected override void ExecuteInternal() generatedFile.SetMetadata("Link", projectFile); if(File.Exists(projectFile)) { - generatedFile.SetMetadata("DependentUpon", $"{secretsConfig.ClassName}.cs"); + generatedFile.SetMetadata("DependentUpon", $"{settingsConfig.ClassName}.cs"); } - Outputs = generatedFile; + Outputs.Add(generatedFile); var parentDirectory = Directory.GetParent(intermediateFile); if(!Directory.Exists(parentDirectory.FullName)) @@ -212,7 +199,7 @@ internal void CreateOrMerge(string jsonFilePath, ref JObject secrets) } } - internal CodeWriter GenerateClass(SecretsConfig secretsConfig, IDictionary secrets) + internal CodeWriter GenerateClass(SettingsConfig settingsConfig, IDictionary secrets) { var writer = new CodeWriter(); writer.AppendLine("// ------------------------------------------------------------------------------"); @@ -233,16 +220,16 @@ internal CodeWriter GenerateClass(SecretsConfig secretsConfig, IDictionary secret, SecretsConfig secretsConfig, ref CodeWriter writer) + private void ProcessSecret(KeyValuePair secret, SettingsConfig secretsConfig, ref CodeWriter writer) { if (!secretsConfig.HasKey(secret.Key, out var valueConfig)) { @@ -283,7 +270,7 @@ private void ProcessSecret(KeyValuePair secret, SecretsConfig se writer.AppendLine(standardOutput, safeOutput); } - internal string ProcessSecret(KeyValuePair secret, SecretsConfig secretsConfig, bool saveOutput, bool safeOutput = false) + internal string ProcessSecret(KeyValuePair secret, SettingsConfig secretsConfig, bool saveOutput, bool safeOutput = false) { if (!secretsConfig.HasKey(secret.Key, out var valueConfig) && !saveOutput) { @@ -307,7 +294,7 @@ internal string ProcessSecret(KeyValuePair secret, SecretsConfig return PropertyBuilder(secret, mapping.Type, mapping.Handler, valueConfig.IsArray, safeOutput, accessibility); } - internal ValueConfig GenerateValueConfig(KeyValuePair secret, SecretsConfig config) + internal ValueConfig GenerateValueConfig(KeyValuePair secret, SettingsConfig config) { var value = secret.Value.ToString(); var valueArray = Regex.Split(value, $"(? x.Replace($"\\{config.Delimiter}", config.Delimiter)); @@ -339,8 +326,11 @@ internal ValueConfig GenerateValueConfig(KeyValuePair secret, Se }; } - internal string GetNamespace(string relativeNamespace) + internal string GetNamespace(SettingsConfig settings) { + var relativeNamespace = settings.Namespace; + var rootNamespace = string.IsNullOrEmpty(settings.RootNamespace) ? RootNamespace : settings.RootNamespace; + if (string.IsNullOrEmpty(relativeNamespace)) relativeNamespace = "Helpers"; @@ -348,7 +338,7 @@ internal string GetNamespace(string relativeNamespace) var count = parts.Count(); if (count == 0) { - return BaseNamespace; + return rootNamespace; } else if(count == 1) { @@ -359,7 +349,7 @@ internal string GetNamespace(string relativeNamespace) relativeNamespace = string.Join(".", parts); } - return $"{BaseNamespace}.{relativeNamespace}"; + return $"{rootNamespace}.{relativeNamespace}"; } internal string PropertyBuilder(KeyValuePair secret, Type type, IValueHandler valueHandler, bool isArray, bool safeOutput, string accessibility) diff --git a/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs b/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs index a0ef1bf6..1f56e818 100644 --- a/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs +++ b/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs @@ -3,7 +3,8 @@ using Mobile.BuildTools.Build; using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; namespace Mobile.BuildTools.Tasks @@ -59,7 +60,7 @@ public BuildConfig(string configuration, string projectDir) public BuildToolsConfig Configuration { get; } public Platform Platform { get; } - public SecretsConfig GetSecretsConfig() + public IEnumerable GetSettingsConfig() { throw new System.NotImplementedException(); } diff --git a/src/Mobile.BuildTools/Tasks/BuildHostSecretsTask.cs b/src/Mobile.BuildTools/Tasks/BuildHostSecretsTask.cs index dfbe1f6f..2548de0b 100644 --- a/src/Mobile.BuildTools/Tasks/BuildHostSecretsTask.cs +++ b/src/Mobile.BuildTools/Tasks/BuildHostSecretsTask.cs @@ -16,7 +16,7 @@ internal override void ExecuteInternal(IBuildConfiguration config) if (!string.IsNullOrEmpty(JsonSecretsFilePath) && File.Exists(JsonSecretsFilePath) && File.GetAttributes(JsonSecretsFilePath).HasFlag(FileAttributes.Normal)) return; - if (config.GetSecretsConfig().Disable || config.BuildingInsideVisualStudio) + if (config.BuildingInsideVisualStudio || !CIBuildEnvironmentUtils.IsBuildHost) return; var secretsFile = Path.Combine(ProjectDirectory, "secrets.json"); diff --git a/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs b/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs index 25b3586e..2c288d85 100644 --- a/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs +++ b/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs @@ -10,7 +10,8 @@ using Mobile.BuildTools.Build; using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; using Newtonsoft.Json; @@ -95,8 +96,8 @@ void IBuildConfiguration.SaveConfiguration() => internal abstract void ExecuteInternal(IBuildConfiguration config); - SecretsConfig IBuildConfiguration.GetSecretsConfig() => - ConfigHelper.GetSecretsConfig(this); + IEnumerable IBuildConfiguration.GetSettingsConfig() => + ConfigHelper.GetSettingsConfig(this); private string GetProperty(string name) { diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index f7ea2974..c30b0216 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -1,8 +1,10 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Mobile.BuildTools.Models; +using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; using Newtonsoft.Json; @@ -60,6 +62,7 @@ public override bool Execute() //#endif LocateSolution(); BuildToolsConfigFilePath = ConfigHelper.GetConfigurationPath(ProjectDir); + MigrateSecretsToSettings(); var crossTargetingProject = IsCrossTargeting(); var platform = TargetFrameworkIdentifier.GetTargetPlatform(); @@ -82,6 +85,46 @@ public override bool Execute() return true; } + internal void MigrateSecretsToSettings() + { + var config = ConfigHelper.GetConfig(BuildToolsConfigFilePath); + + if(config.ProjectSecrets is not null && config.ProjectSecrets.Any()) + { + config.ProjectSecrets.ForEach(x => + { + var projectName = x.Key; + var secretsConfig = x.Value; + var settings = new SettingsConfig + { + Accessibility = secretsConfig.Accessibility, + ClassName = secretsConfig.ClassName, + Delimiter = secretsConfig.Delimiter, + Namespace = secretsConfig.Namespace, + Prefix = secretsConfig.Prefix, + Properties = secretsConfig.Properties, + RootNamespace = secretsConfig.RootNamespace + }; + + if(config.AppSettings.ContainsKey(projectName)) + { + if(!config.AppSettings[projectName].Any(x => x.ClassName == settings.ClassName)) + { + var allSettings = new List(config.AppSettings[projectName]); + allSettings.Add(settings); + config.AppSettings[projectName] = allSettings; + } + } + else + { + config.AppSettings[projectName] = new[] { settings }; + } + }); + config.ProjectSecrets = null; + ConfigHelper.SaveConfig(config, BuildToolsConfigFilePath); + } + } + public static bool IsEnabled(ToolItem item) { if (item is null) return true; @@ -102,16 +145,7 @@ private bool IsCrossTargeting() private bool ShouldEnableSecrets(BuildToolsConfig config) { - var secretsFileExists = File.Exists(Path.Combine(ProjectDir, Constants.SecretsJsonFileName)); - var secretsConfig = ConfigHelper.GetSecretsConfig(ProjectName, ProjectDir, config); - - if(secretsConfig is null) - { - // Intentionally disable this condition in CI incase somebody checked in the secrets.json - return secretsFileExists && !CIBuildEnvironmentUtils.IsBuildHost; - } - - return !secretsConfig.Disable; + return config.AppSettings is not null && config.AppSettings.ContainsKey(ProjectName) && config.AppSettings[ProjectName].Any(); } private void LocateSolution() diff --git a/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs b/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs index cffa6587..c426c12b 100644 --- a/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs +++ b/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs @@ -20,10 +20,8 @@ public class SecretsJsonTask : BuildToolsTaskBase internal override void ExecuteInternal(IBuildConfiguration config) { - //System.Diagnostics.Debugger.Launch(); - - var secretsConfig = config.GetSecretsConfig(); - if (secretsConfig is null || secretsConfig.Disable) + // Sanity Check + if (!config.Configuration.AppSettings.ContainsKey(ProjectName)) return; var configJson = string.Format(Constants.SecretsJsonConfigurationFileFormat, config.BuildConfiguration.ToLower()); @@ -40,13 +38,12 @@ internal override void ExecuteInternal(IBuildConfiguration config) if (!string.IsNullOrEmpty(JsonSecretsFilePath) && !searchPaths.Contains(JsonSecretsFilePath)) searchPaths.Add(JsonSecretsFilePath); - var rootNamespace = string.IsNullOrEmpty(secretsConfig.RootNamespace) ? RootNamespace : secretsConfig.RootNamespace; var generator = new SecretsClassGenerator(config, searchPaths.ToArray()) { - BaseNamespace = rootNamespace, + RootNamespace = RootNamespace, }; generator.Execute(); - _generatedCodeFiles = new[] { generator.Outputs }; + _generatedCodeFiles = generator.Outputs.ToArray(); } } } diff --git a/tests/Mobile.BuildTools.Tests/Data/SecretsData.cs b/tests/Mobile.BuildTools.Tests/Data/SecretsData.cs index eb94f6e3..fb1a04d0 100644 --- a/tests/Mobile.BuildTools.Tests/Data/SecretsData.cs +++ b/tests/Mobile.BuildTools.Tests/Data/SecretsData.cs @@ -1,6 +1,6 @@ using System.Collections; using System.Collections.Generic; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; using static Mobile.BuildTools.Tests.Fixtures.Generators.SecretsClassGeneratorFixture; namespace Mobile.BuildTools.Tests.Data diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/BuildHostSecretsGeneratorFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/BuildHostSecretsGeneratorFixture.cs index ed579cb9..afec4556 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/BuildHostSecretsGeneratorFixture.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/BuildHostSecretsGeneratorFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Mobile.BuildTools.Build; using Mobile.BuildTools.Generators; using Mobile.BuildTools.Generators.Secrets; @@ -31,7 +32,7 @@ private BuildHostSecretsGenerator GetGenerator(IBuildConfiguration config) => private void SetTestVariable() { var buildConfig = GetConfiguration(); - var prefix = buildConfig.GetSecretsConfig().Prefix; + var prefix = buildConfig.GetSettingsConfig().First().Prefix; Environment.SetEnvironmentVariable($"{prefix}Test1", "SomeValue"); } @@ -56,7 +57,7 @@ public void CreatesJObject() SetTestVariable(); var buildConfig = GetConfiguration(); var generator = GetGenerator(buildConfig); - var secretsConfig = buildConfig.GetSecretsConfig(); + var secretsConfig = buildConfig.GetSettingsConfig().First(); var secrets = generator.GetSecrets(secretsConfig.Prefix); var jsonObject = generator.GetJObjectFromSecrets(secrets); @@ -73,7 +74,7 @@ public void RetrievesEnvironmentVariables() SetTestVariable(); var buildConfig = GetConfiguration(); var generator = GetGenerator(buildConfig); - var secretsConfig = buildConfig.GetSecretsConfig(); + var secretsConfig = buildConfig.GetSettingsConfig().First(); var secrets = generator.GetSecrets(secretsConfig.Prefix); Assert.NotNull(secrets); diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs index 57fe2db1..7bfce786 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs @@ -9,7 +9,8 @@ using Microsoft.CSharp; using Mobile.BuildTools.Build; using Mobile.BuildTools.Generators.Secrets; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Reference.Models.Settings; using Xunit; using Xunit.Abstractions; @@ -45,7 +46,7 @@ public void ProcessSecretHidesValueForSafeOutput(bool firstRun) var value = "TestValue"; var pair = new KeyValuePair(TestKey, value); var generator = GetGenerator(); - var config = new SecretsConfig() + var config = new SettingsConfig() { Properties = new List{ new ValueConfig { Name = TestKey, PropertyType = PropertyType.String } } }; @@ -65,7 +66,7 @@ public void ProcessSecretGeneratesBooleanValue(Accessibility accessibility, bool var value = bool.TrueString; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SecretsConfig() + var config = new SettingsConfig() { Accessibility = accessibility, Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Bool } } @@ -86,7 +87,7 @@ public void ProcessSecretGeneratesIntegerValue(Accessibility accessibility, bool var value = "2"; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SecretsConfig() + var config = new SettingsConfig() { Accessibility = accessibility, Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Int } } @@ -107,7 +108,7 @@ public void ProcessSecretGeneratesDoubleValue(Accessibility accessibility, bool var value = "2.2"; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SecretsConfig() + var config = new SettingsConfig() { Accessibility = accessibility, Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Double } } @@ -128,7 +129,7 @@ public void ProcessSecretGeneratesStringValue(Accessibility accessibility, bool var value = "TestValue"; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SecretsConfig() + var config = new SettingsConfig() { Accessibility = accessibility, Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.String } } @@ -149,11 +150,15 @@ public void GeneratesCorrectNamespace_WithSpecifiedBaseNamespace(string relative { var buildConfig = GetConfiguration(); var generator = GetGenerator(buildConfig); - generator.BaseNamespace = "Foo.Bar"; + generator.RootNamespace = "Foo.Bar"; buildConfig.ProjectDirectory = "file://Repos/AwesomeProject/Foo"; string @namespace = null; - var exception = Record.Exception(() => @namespace = generator.GetNamespace(relativeNamespaceConfig)); + var settingsConfig = new SettingsConfig + { + Namespace = relativeNamespaceConfig + }; + var exception = Record.Exception(() => @namespace = generator.GetNamespace(settingsConfig)); Assert.Null(exception); Assert.Equal(expectedNamespace, @namespace); @@ -202,7 +207,7 @@ public void HandleNullValue(bool isArray, PropertyType type, string typeDeclarat PropertyType = type, IsArray = isArray }; - var secretsConfig = new SecretsConfig { Properties = new List { valueConfig } }; + var secretsConfig = new SettingsConfig { Properties = new List { valueConfig } }; var output = generator.ProcessSecret(pair, secretsConfig, false); Assert.Contains($"{typeDeclaration} {TestKey}", output); Assert.Contains(valueDeclaration, output); @@ -274,7 +279,7 @@ public void ProcessSecretValue(bool firstRun, bool isArray, PropertyType type, s PropertyType = type, IsArray = isArray }; - var secretsConfig = new SecretsConfig { Properties = new List { valueConfig } }; + var secretsConfig = new SettingsConfig { Properties = new List { valueConfig } }; var output = generator.ProcessSecret(pair, secretsConfig, firstRun); Assert.Contains($"{typeDeclaration} {TestKey}", output); Assert.Contains(valueDeclaration, output); @@ -322,19 +327,29 @@ public void ProcessSecretValue(bool firstRun, bool isArray, PropertyType type, s public void GeneratesValidClass(string secretsFile, PropertyType propertyType, Type expectedType, bool isArray) { var config = GetConfiguration($"{nameof(GeneratesValidClass)}-{expectedType.Name}"); - config.SecretsConfig.Properties.Add(new ValueConfig - { - Name = "Prop", - PropertyType = propertyType, - IsArray = isArray - }); + config.SettingsConfig = new List{ + new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "Prop", + PropertyType = propertyType, + IsArray = isArray + } + } + } + }; var generator = new SecretsClassGenerator(config, Path.Combine("Templates", "Secrets", secretsFile)) { - BaseNamespace = config.ProjectName + RootNamespace = config.ProjectName }; generator.Execute(); + + Assert.Single(generator.Outputs); - var filePath = generator.Outputs.ItemSpec; + var filePath = generator.Outputs.First().ItemSpec; Assert.True(File.Exists(filePath)); var csc = new CSharpCodeProvider(); @@ -344,7 +359,7 @@ public void GeneratesValidClass(string secretsFile, PropertyType propertyType, T Assert.Empty(results.Errors); Assert.NotNull(results.CompiledAssembly); - var secretsClassType = results.CompiledAssembly.DefinedTypes.FirstOrDefault(t => t.Name == config.SecretsConfig.ClassName); + var secretsClassType = results.CompiledAssembly.DefinedTypes.FirstOrDefault(t => t.Name == config.SettingsConfig.First().ClassName); Assert.NotNull(secretsClassType); var propField = secretsClassType.GetField("Prop", BindingFlags.NonPublic | BindingFlags.Static); Assert.NotNull(propField); diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Utils/BuildToolsConfigFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Utils/BuildToolsConfigFixture.cs index 9dd0f79c..06b22f1c 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/Utils/BuildToolsConfigFixture.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Utils/BuildToolsConfigFixture.cs @@ -66,11 +66,11 @@ public void ManifestsIsNotNull() } [Fact] - public void ProjectSecretsIsNotNull() + public void AppSettingsIsNotNull() { var config = CreateConfig(); - Assert.NotNull(config.ProjectSecrets); + Assert.NotNull(config.AppSettings); } [Fact] diff --git a/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs b/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs index 31844a2e..c7865575 100644 --- a/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs +++ b/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs @@ -2,7 +2,8 @@ using Mobile.BuildTools.Build; using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; -using Mobile.BuildTools.Models.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; namespace Mobile.BuildTools.Tests.Mocks @@ -12,13 +13,16 @@ public class TestBuildConfiguration : IBuildConfiguration public TestBuildConfiguration() { ConfigHelper.SaveDefaultConfig("."); - SecretsConfig = new SecretsConfig - { - ClassName = "Secrets", - Delimiter = ";", - Namespace = "Helpers", - Prefix = "UNIT_TEST_", - Properties = new List() + SettingsConfig = new[] + { + new SettingsConfig + { + ClassName = "Secrets", + Delimiter = ";", + Namespace = "Helpers", + Prefix = "UNIT_TEST_", + Properties = new List() + } }; } @@ -39,12 +43,12 @@ public TestBuildConfiguration() public BuildToolsConfig Configuration => configuration ?? (configuration = ConfigHelper.GetConfig(".")); public Platform Platform { get; set; } - public SecretsConfig GetSecretsConfig() => SecretsConfig; + public IEnumerable GetSettingsConfig() => SettingsConfig; public void SaveConfiguration() { } - public SecretsConfig SecretsConfig { get; } + public IEnumerable SettingsConfig { get; set; } } } From 7b72afb569cc7c1d054eafd8bf8ce18c5aa8f9cf Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sat, 3 Jul 2021 21:18:26 -0700 Subject: [PATCH 03/17] use schema validation --- Packages.props | 1 + .../Mobile.BuildTools.Reference.csproj | 3 +- .../Models/AppConfig.cs | 3 +- .../Models/AppIcons/AppleIconSet.cs | 2 +- .../Models/AppIcons/AppleIconSetImage.cs | 8 ++-- .../Models/AppIcons/BaseImageConfiguration.cs | 35 +++++++++++----- .../Models/AppIcons/PlatformConfiguration.cs | 3 +- .../Models/AppIcons/PlatformResourceType.cs | 5 ++- .../Models/AppIcons/ResourceDefinition.cs | 22 ++++++---- .../Models/AppIcons/WatermarkConfiguration.cs | 25 +++++++---- .../Models/AppIcons/WatermarkPosition.cs | 5 ++- .../Models/AutomaticVersioning.cs | 11 +++-- .../Models/BuildToolsConfig.cs | 41 +++++++++++++------ .../Models/EnvironmentSettings.cs | 4 +- .../Models/GoogleConfig.cs | 6 ++- .../Models/ImageResize.cs | 7 +--- .../Models/ReleaseNotesOptions.cs | 10 ++--- .../Models/Secrets/SecretsConfig.cs | 16 ++++---- .../Models/Settings/Accessibility.cs | 4 +- .../Models/Settings/PropertyType.cs | 2 + .../Models/Settings/SettingsConfig.cs | 27 ++++++++---- .../Models/Settings/ValueConfig.cs | 8 ++-- .../Models/TemplatedManifest.cs | 6 +-- .../Models/ToolItem.cs | 2 +- .../Models/VersionBehavior.cs | 5 ++- .../Models/VersionEnvironment.cs | 5 ++- .../Models/XamarinCss.cs | 4 +- .../Tasks/LocateBuildToolsConfigTask.cs | 36 ++++++++++++++-- 28 files changed, 210 insertions(+), 96 deletions(-) diff --git a/Packages.props b/Packages.props index 56865c8d..f033c61e 100644 --- a/Packages.props +++ b/Packages.props @@ -4,6 +4,7 @@ + diff --git a/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj b/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj index aa745208..67bda900 100644 --- a/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj +++ b/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;net472; @@ -11,6 +11,7 @@ + diff --git a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs index e40161ec..64d613b9 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs @@ -5,8 +5,9 @@ namespace Mobile.BuildTools.Models { public class AppConfig : ToolItem { + [Description("Configures the bundling strategy for advanced scenarios. By default it will only transform the app.config with the app.{BuildConfiguration}.config. You may optionally bundle all app.*.config or those that do not include Debug, Release, Store.")] [DefaultValue(AppConfigStrategy.TransformOnly)] - [JsonProperty("strategy")] + [JsonProperty("strategy", Required = Required.AllowNull)] public AppConfigStrategy Strategy { get; set; } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs index 501fe9ac..b0d702fc 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs @@ -5,7 +5,7 @@ namespace Mobile.BuildTools.Models.AppIcons { public class AppleIconSet { - [JsonProperty("images")] + [JsonProperty("images", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Include)] public List Images { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs index 356f4d0e..511f0b3a 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs @@ -4,16 +4,16 @@ namespace Mobile.BuildTools.Models.AppIcons { public class AppleIconSetImage { - [JsonProperty("scale")] + [JsonProperty("scale", Required = Required.AllowNull)] public string Scale { get; set; } - [JsonProperty("size")] + [JsonProperty("size", Required = Required.AllowNull)] public string Size { get; set; } - [JsonProperty("idiom")] + [JsonProperty("idiom", Required = Required.AllowNull)] public string Idiom { get; set; } - [JsonProperty("filename")] + [JsonProperty("filename", Required = Required.AllowNull)] public string FileName { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs index 9b33c9e5..d3bbb42a 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs @@ -1,37 +1,50 @@ -using Newtonsoft.Json; +using System.ComponentModel; +using Newtonsoft.Json; namespace Mobile.BuildTools.Models.AppIcons { public class BaseImageConfiguration : IImageResource { - [JsonProperty("ignore", DefaultValueHandling = DefaultValueHandling.Ignore)] + [DefaultValue(false)] + [Description("Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.")] + [JsonProperty("ignore", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public bool Ignore { get; set; } - [JsonProperty("name")] + [Description("The name of the output image.")] + [JsonProperty("name", Required = Required.AllowNull)] public string Name { get; set; } - [JsonProperty("scale", DefaultValueHandling = DefaultValueHandling.Ignore)] + [DefaultValue(1.0)] + [Description("The scale factor to scale the largest output image based on the size of the input image.")] + [JsonProperty("scale", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public double Scale { get; set; } - [JsonProperty("backgroundColor", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.")] + [JsonProperty("backgroundColor", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public string BackgroundColor { get; set; } - [JsonProperty("width", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("A specific override for the width of the largest output image.")] + [JsonProperty("width", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public int? Width { get; set; } - [JsonProperty("height", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("A specific override for the height of the largest output image.")] + [JsonProperty("height", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public int? Height { get; set; } - [JsonProperty("padFactor", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("The padding factor will resize the canvas size by the specified factor. For example a facor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.")] + [JsonProperty("padFactor", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public double? PaddingFactor { get; set; } - [JsonProperty("padColor", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.")] + [JsonProperty("padColor", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public string PaddingColor { get; set; } - [JsonProperty("watermark", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies a configuration for adding a watermark or banner on a specified image.")] + [JsonProperty("watermark", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public WatermarkConfiguration Watermark { get; set; } - [JsonProperty("resourceType", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the output resource type for the image. This typically should be used within a platform specific configuration.")] + [JsonProperty("resourceType", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformResourceType ResourceType { get; set; } [JsonIgnore] diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs index ded7d74d..29fa67cb 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs @@ -6,7 +6,8 @@ namespace Mobile.BuildTools.Models.AppIcons { public class PlatformConfiguration : BaseImageConfiguration { - [JsonProperty("additionalOutputs", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Provides additional resource outputs for the specified source image.")] + [JsonProperty("additionalOutputs", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public List AdditionalOutputs { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformResourceType.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformResourceType.cs index 211419a0..2ee1a40d 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformResourceType.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformResourceType.cs @@ -1,5 +1,8 @@ -namespace Mobile.BuildTools.Models.AppIcons +using Newtonsoft.Json.Schema.Generation; + +namespace Mobile.BuildTools.Models.AppIcons { + [JSchemaGenerationProvider(typeof(StringEnumGenerationProvider))] public enum PlatformResourceType { Default, diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs index 9d7b887b..88ff490b 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -17,26 +18,33 @@ public string Schema set { } } - [JsonProperty("android", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the Android specific configuration for the source image.")] + [JsonProperty("android", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration Android { get; set; } - [JsonProperty("apple", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the configuration for all Apple targets if building for iOS, macOS, TVOS, WatchOS.")] + [JsonProperty("apple", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration Apple { get; set; } - [JsonProperty("ios", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the iOS specific configuration for the source image.")] + [JsonProperty("ios", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Properly named iOS")] public PlatformConfiguration iOS { get; set; } - [JsonProperty("tvos", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the TVOS specific configuration for the source image.")] + [JsonProperty("tvos", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration TVOS { get; set; } - [JsonProperty("macos", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the macOS specific configuration for the source image.")] + [JsonProperty("macos", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration MacOS { get; set; } - [JsonProperty("tizen", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the Tizen specific configuration for the source image.")] + [JsonProperty("tizen", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration Tizen { get; set; } - [JsonProperty("uwp", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Description("Specifies the UWP specific configuration for the source image.")] + [JsonProperty("uwp", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration UWP { get; set; } public IEnumerable GetConfigurations(Platform platform) diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs index 6e359d13..2c6bc6d7 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs @@ -1,32 +1,41 @@ using System.Collections.Generic; +using System.ComponentModel; using Newtonsoft.Json; namespace Mobile.BuildTools.Models.AppIcons { public class WatermarkConfiguration { - [JsonProperty("sourceFile")] + [Description("Specifies the watermark source file name.")] + [JsonProperty("sourceFile", Required = Required.AllowNull)] public string SourceFile { get; set; } - [JsonProperty("colors")] + [Description("Specifies the color or colors (for gradients) that should be used for the automatically generated Banner style watermark.")] + [JsonProperty("colors", Required = Required.AllowNull)] public IEnumerable Colors { get; set; } - [JsonProperty("position")] + [Description("Specifies the watermark position.")] + [JsonProperty("position", Required = Required.AllowNull)] public WatermarkPosition? Position { get; set; } - [JsonProperty("text")] + [Description("Specifies the text for the watermark. For example you may wish to have a banner such as 'Dev' or 'Stage' to signify which environment the app was built for. Other scenarios could include 'Free', 'Pro', 'Lite', etc.")] + [JsonProperty("text", Required = Required.AllowNull)] public string Text { get; set; } - [JsonProperty("textColor")] + [Description("The hex or name of the color to use for the text.")] + [JsonProperty("textColor", Required = Required.AllowNull)] public string TextColor { get; set; } - [JsonProperty("fontFamily")] + [Description("The Font Family name if you want to use a custom font.")] + [JsonProperty("fontFamily", Required = Required.AllowNull)] public string FontFamily { get; set; } - [JsonProperty("fontFile")] + [Description("Have a custom font you want to use, you can provide the file path to the Font File.")] + [JsonProperty("fontFile", Required = Required.AllowNull)] public string FontFile { get; set; } - [JsonProperty("opacity")] + [Description("The opacity to use for the Watermark or Banner.")] + [JsonProperty("opacity", Required = Required.AllowNull)] public double? Opacity { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkPosition.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkPosition.cs index 521c48ab..4c4e8d8b 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkPosition.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkPosition.cs @@ -1,5 +1,8 @@ -namespace Mobile.BuildTools.Models.AppIcons +using Newtonsoft.Json.Schema.Generation; + +namespace Mobile.BuildTools.Models.AppIcons { + [JSchemaGenerationProvider(typeof(StringEnumGenerationProvider))] public enum WatermarkPosition { Top, diff --git a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs index 8de3358a..b798bd63 100644 --- a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs +++ b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs @@ -5,13 +5,18 @@ namespace Mobile.BuildTools.Models { public class AutomaticVersioning : ToolItem { - [JsonProperty("behavior")] + [DefaultValue(VersionBehavior.PreferBuildNumber)] + [Description("Sets the default behavior for versioning the app. By default the Mobile.BuildTools will attempt to use a Build number and will fallback to a timestamp.")] + [JsonProperty("behavior", Required = Required.AllowNull)] public VersionBehavior Behavior { get; set; } - [JsonProperty("environment")] + [DefaultValue(VersionEnvironment.BuildHost)] + [Description("Sets the default versioning environment. You can use this locally only, on build hosts only, or everywhere for a unique build number every time.")] + [JsonProperty("environment", Required = Required.Always)] public VersionEnvironment Environment { get; set; } - [JsonProperty("versionOffset")] + [Description("If you need to offset from your build number, you may want to set the Version Offset to get a version that will work for you.")] + [JsonProperty("versionOffset", Required = Required.AllowNull)] public int VersionOffset { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs index 270bfb04..26857f74 100644 --- a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.ComponentModel; using Mobile.BuildTools.Models.Secrets; using Mobile.BuildTools.Models.Settings; using Mobile.BuildTools.Reference.Models.Settings; @@ -16,40 +18,53 @@ public string Schema set { } } - [JsonProperty("appConfig")] + [Description("Configures the settings for bundling and compiling the app.config for use with the Mobile.BuildTools.Configuration package.")] + [JsonProperty("appConfig", Required = Required.AllowNull)] public AppConfig AppConfig { get; set; } - [JsonProperty("artifactCopy")] + [Description("Confgures the Mobile.BuildTools to copy the generated APK/AAB or IPA & dSYM to the root directory making it easier to locate and stage the build artifacts.")] + [JsonProperty("artifactCopy", Required = Required.AllowNull)] public ArtifactCopy ArtifactCopy { get; set; } - [JsonProperty("automaticVersioning")] + [Description("Configures the Mobile.BuildTools to automatically version the build for Android and iOS targets.")] + [JsonProperty("automaticVersioning", Required = Required.AllowNull)] public AutomaticVersioning AutomaticVersioning { get; set; } - [JsonProperty("css")] + [Description("Configures the Mobile.BuildTools to compile SCSS files into Xamarin.Forms compliant CSS for styling your Xamarin.Forms application with CSS.")] + [JsonProperty("css", Required = Required.AllowNull)] public XamarinCss Css { get; set; } - [JsonProperty("images")] + [Description("Configures the Mobile.BuildTools to intelligently process image sources to be bundled into your Android and iOS application.")] + [JsonProperty("images", Required = Required.AllowNull)] public ImageResize Images { get; set; } - [JsonProperty("manifests")] + [Description("Configures the Mobile.BuildTools to process Tokens within the AndroidManifest.xml and Info.plist, replacing values like $AppName$ with a variable named AppName.")] + [JsonProperty("manifests", Required = Required.AllowNull)] public TemplatedManifest Manifests { get; set; } - [JsonProperty("releaseNotes")] + [Description("Configures the Mobile.BuildTools to generate Release Notes for your build, based on the Git commit messages.")] + [JsonProperty("releaseNotes", Required = Required.AllowNull)] public ReleaseNotesOptions ReleaseNotes { get; set; } - [JsonProperty("projectSecrets")] + [Obsolete] + [Description("Note: This is obsolete, please use `appSettings`.")] + [JsonProperty("projectSecrets", Required = Required.AllowNull)] public Dictionary ProjectSecrets { get; set; } - [JsonProperty("appSettings")] + [Description("Replaces the former 'Secrets' API, with a newly generated AppSettings class. This will allow you to generate one or more configuration classes.")] + [JsonProperty("appSettings", Required = Required.AllowNull)] public Dictionary> AppSettings { get; set; } - [JsonProperty("environment")] + [Description("Configures the Mobile.BuildTools with default non-sensitive environment values. If the value does not exist in the System Environment, this value will be used.")] + [JsonProperty("environment", Required = Required.AllowNull)] public EnvironmentSettings Environment { get; set; } - [JsonProperty("google")] + [Description("Configures the Mobile.BuildTools to automatically generate and include the google-services.json or GoogleService-Info.plist from an Environment variable. This can be either a raw string value or file location if using Secure Files.")] + [JsonProperty("google", Required = Required.AllowNull)] public GoogleConfig Google { get; set; } - [JsonProperty("debug")] + [Description("Having issues with the Mobile.BuildTools. Enable the Debug property to help you get some additional debug output in the build logs to help identify configuration issues.")] + [JsonProperty("debug", Required = Required.AllowNull)] public bool Debug { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs b/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs index 0d841fe0..f19bfac0 100644 --- a/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs +++ b/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs @@ -11,10 +11,10 @@ public EnvironmentSettings() Configuration = new Dictionary>(); } - [JsonProperty("defaults")] + [JsonProperty("defaults", Required = Required.AllowNull)] public Dictionary Defaults { get; set; } - [JsonProperty("configuration")] + [JsonProperty("configuration", Required = Required.AllowNull)] public Dictionary> Configuration { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs b/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs index 09d17196..0b40ffbc 100644 --- a/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs @@ -1,9 +1,13 @@ +using Newtonsoft.Json; + namespace Mobile.BuildTools.Models { public class GoogleConfig { + [JsonProperty("servicesJson", Required = Required.AllowNull)] public string ServicesJson { get; set; } - + + [JsonProperty("infoPlist", Required = Required.AllowNull)] public string InfoPlist { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ImageResize.cs b/src/Mobile.BuildTools.Reference/Models/ImageResize.cs index a57a5ace..8e0f1824 100644 --- a/src/Mobile.BuildTools.Reference/Models/ImageResize.cs +++ b/src/Mobile.BuildTools.Reference/Models/ImageResize.cs @@ -12,13 +12,10 @@ public ImageResize() ConditionalDirectories = new Dictionary>(); } - [JsonProperty("directories")] + [JsonProperty("directories", Required = Required.AllowNull)] public List Directories { get; set; } - [JsonProperty("conditionalDirectories")] + [JsonProperty("conditionalDirectories", Required = Required.AllowNull)] public Dictionary> ConditionalDirectories { get; set; } - - [JsonProperty("watermarkOpacity")] - public double? WatermarkOpacity { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs b/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs index 82ecb002..7fba0e7a 100644 --- a/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs +++ b/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs @@ -6,23 +6,23 @@ namespace Mobile.BuildTools.Models public class ReleaseNotesOptions : ToolItem { [DefaultValue(10)] - [JsonProperty("maxDays")] + [JsonProperty("maxDays", Required = Required.AllowNull)] public int MaxDays { get; set; } [DefaultValue(10)] - [JsonProperty("maxCommit")] + [JsonProperty("maxCommit", Required = Required.AllowNull)] public int MaxCommit { get; set; } [DefaultValue(250)] - [JsonProperty("characterLimit")] + [JsonProperty("characterLimit", Required = Required.AllowNull)] public int CharacterLimit { get; set; } [DefaultValue("releasenotes.txt")] - [JsonProperty("filename")] + [JsonProperty("filename", Required = Required.AllowNull)] public string FileName { get; set; } [DefaultValue(true)] - [JsonProperty("createInRoot")] + [JsonProperty("createInRoot", Required = Required.AllowNull)] public bool CreateInRoot { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs index 48853b4a..ff018a45 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs @@ -12,28 +12,28 @@ public SecretsConfig() Properties = new List(); } - [JsonProperty("disable")] + [JsonProperty("disable", Required = Required.AllowNull)] public bool Disable { get; set; } - [JsonProperty("delimiter")] + [JsonProperty("delimiter", Required = Required.AllowNull)] public string Delimiter { get; set; } - [JsonProperty("prefix")] + [JsonProperty("prefix", Required = Required.AllowNull)] public string Prefix { get; set; } - [JsonProperty("className")] + [JsonProperty("className", Required = Required.AllowNull)] public string ClassName { get; set; } - [JsonProperty("accessibility")] + [JsonProperty("accessibility", Required = Required.AllowNull)] public Accessibility Accessibility { get; set; } - [JsonProperty("rootNamespace")] + [JsonProperty("rootNamespace", Required = Required.AllowNull)] public string RootNamespace { get; set; } - [JsonProperty("namespace")] + [JsonProperty("namespace", Required = Required.AllowNull)] public string Namespace { get; set; } - [JsonProperty("properties")] + [JsonProperty("properties", Required = Required.AllowNull)] public List Properties { get; set; } public bool ContainsKey(string key) => Properties != null && Properties.Any(x => x.Name == key); diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/Accessibility.cs b/src/Mobile.BuildTools.Reference/Models/Settings/Accessibility.cs index a504ad0e..d728aa21 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/Accessibility.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/Accessibility.cs @@ -1,6 +1,8 @@ - +using Newtonsoft.Json.Schema.Generation; + namespace Mobile.BuildTools.Models.Settings { + [JSchemaGenerationProvider(typeof(StringEnumGenerationProvider))] public enum Accessibility { Internal, diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/PropertyType.cs b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyType.cs index 53e627c6..72894f7c 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/PropertyType.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/PropertyType.cs @@ -1,8 +1,10 @@ using System; using Mobile.BuildTools.Handlers; +using Newtonsoft.Json.Schema.Generation; namespace Mobile.BuildTools.Models.Settings { + [JSchemaGenerationProvider(typeof(StringEnumGenerationProvider))] public enum PropertyType { [PropertyTypeMapping(typeof(string), "\"{0}\"")] diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs index 05eefbfc..e2c1ff4b 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using Mobile.BuildTools.Models.Settings; using Newtonsoft.Json; @@ -12,25 +13,37 @@ public SettingsConfig() Properties = new List(); } - [JsonProperty("delimiter")] + [DefaultValue(";")] + [Description("The delimiter used for arrays. By default this will use a semi-colon.")] + [JsonProperty("delimiter", Required = Required.AllowNull)] public string Delimiter { get; set; } - [JsonProperty("prefix")] + [DefaultValue("BuildTools_")] + [Description("The prefix the Mobile.BuildTools should use to look for variables. Note if a variable exists with the exact name it will be used if one does not exist with the prefix.")] + [JsonProperty("prefix", Required = Required.AllowNull)] public string Prefix { get; set; } - [JsonProperty("className")] + [DefaultValue("AppSettings")] + [Description("The name of the generated class.")] + [JsonProperty("className", Required = Required.AllowNull)] public string ClassName { get; set; } - [JsonProperty("accessibility")] + [DefaultValue(Accessibility.Internal)] + [Description("The default visibility of the generated class, either 'public' or 'internal'.")] + [JsonProperty("accessibility", Required = Required.AllowNull)] public Accessibility Accessibility { get; set; } - [JsonProperty("rootNamespace")] + [Description("If using a Shared project as is typically the case with an Uno Platform app, be sure to specify the Root Namespace to use as this will change otherwise based on which platform target you are compiling.")] + [JsonProperty("rootNamespace", Required = Required.AllowNull)] public string RootNamespace { get; set; } - [JsonProperty("namespace")] + [DefaultValue("Helpers")] + [Description("The partial relative namespace to generate. By default this will be the Helpers namespace, you may set it to the root namespace by providing a period '.' for the value.")] + [JsonProperty("namespace", Required = Required.AllowNull)] public string Namespace { get; set; } - [JsonProperty("properties")] + [Description("The properties that should be generated in the generated AppSettings class.")] + [JsonProperty("properties", Required = Required.Always)] public List Properties { get; set; } public bool ContainsKey(string key) => Properties != null && Properties.Any(x => x.Name == key); diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs index b32f0076..066d3054 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs @@ -5,16 +5,16 @@ namespace Mobile.BuildTools.Models.Settings { public class ValueConfig { - [JsonProperty("name")] + [JsonProperty("name", Required = Required.AllowNull)] public string Name { get; set; } - [JsonProperty("type")] + [JsonProperty("type", Required = Required.AllowNull)] public PropertyType PropertyType { get; set; } - [JsonProperty("isArray")] + [JsonProperty("isArray", Required = Required.AllowNull)] public bool IsArray { get; set; } - [JsonProperty("defaultValue")] + [JsonProperty("defaultValue", Required = Required.AllowNull)] public string DefaultValue { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs b/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs index c1014547..5617ace5 100644 --- a/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs +++ b/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs @@ -6,15 +6,15 @@ namespace Mobile.BuildTools.Models public class TemplatedManifest : ToolItem { [DefaultValue("$$")] - [JsonProperty("token")] + [JsonProperty("token", Required = Required.AllowNull)] public string Token { get; set; } [DefaultValue("Manifest_")] - [JsonProperty("variablePrefix")] + [JsonProperty("variablePrefix", Required = Required.AllowNull)] public string VariablePrefix { get; set; } [DefaultValue(false)] - [JsonProperty("missingTokensAsErrors")] + [JsonProperty("missingTokensAsErrors", Required = Required.AllowNull)] public bool MissingTokensAsErrors { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ToolItem.cs b/src/Mobile.BuildTools.Reference/Models/ToolItem.cs index 868b259c..dabcfb29 100644 --- a/src/Mobile.BuildTools.Reference/Models/ToolItem.cs +++ b/src/Mobile.BuildTools.Reference/Models/ToolItem.cs @@ -7,7 +7,7 @@ public abstract class ToolItem { [Description("Disables this Mobile.Build.Tools Task")] [DefaultValue(false)] - [JsonProperty("disable")] + [JsonProperty("disable", Required = Required.AllowNull)] public bool Disable { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/VersionBehavior.cs b/src/Mobile.BuildTools.Reference/Models/VersionBehavior.cs index 7b28be4a..820bc0af 100644 --- a/src/Mobile.BuildTools.Reference/Models/VersionBehavior.cs +++ b/src/Mobile.BuildTools.Reference/Models/VersionBehavior.cs @@ -1,5 +1,8 @@ -namespace Mobile.BuildTools.Models +using Newtonsoft.Json.Schema.Generation; + +namespace Mobile.BuildTools.Models { + [JSchemaGenerationProvider(typeof(StringEnumGenerationProvider))] public enum VersionBehavior { Off, diff --git a/src/Mobile.BuildTools.Reference/Models/VersionEnvironment.cs b/src/Mobile.BuildTools.Reference/Models/VersionEnvironment.cs index 8b79a913..56921d9c 100644 --- a/src/Mobile.BuildTools.Reference/Models/VersionEnvironment.cs +++ b/src/Mobile.BuildTools.Reference/Models/VersionEnvironment.cs @@ -1,5 +1,8 @@ -namespace Mobile.BuildTools.Models +using Newtonsoft.Json.Schema.Generation; + +namespace Mobile.BuildTools.Models { + [JSchemaGenerationProvider(typeof(StringEnumGenerationProvider))] public enum VersionEnvironment { All, diff --git a/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs b/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs index 01165bc4..eca6a2dc 100644 --- a/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs +++ b/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs @@ -6,11 +6,11 @@ namespace Mobile.BuildTools.Models public class XamarinCss : ToolItem { [DefaultValue(false)] - [JsonProperty("minify")] + [JsonProperty("minify", Required = Required.AllowNull)] public bool Minify { get; set; } [DefaultValue(false)] - [JsonProperty("bundleScss")] + [JsonProperty("bundleScss", Required = Required.AllowNull)] public bool BundleScss { get; set; } } } diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index c30b0216..89ff162a 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Build.Framework; @@ -7,6 +8,9 @@ using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Newtonsoft.Json.Schema.Generation; namespace Mobile.BuildTools.Tasks { @@ -63,6 +67,7 @@ public override bool Execute() LocateSolution(); BuildToolsConfigFilePath = ConfigHelper.GetConfigurationPath(ProjectDir); MigrateSecretsToSettings(); + ValidateConfigSchema(); var crossTargetingProject = IsCrossTargeting(); var platform = TargetFrameworkIdentifier.GetTargetPlatform(); @@ -85,11 +90,35 @@ public override bool Execute() return true; } + internal void ValidateConfigSchema() + { + try + { + var generator = new JSchemaGenerator(); + var schema = generator.Generate(typeof(BuildToolsConfig)); + var config = JObject.Parse(File.ReadAllText(BuildToolsConfigFilePath)); + if(!config.IsValid(schema, out IList errorMessages)) + { + foreach(var error in errorMessages) + { + Log.LogError("Invalid buildtools.json schema detected.."); + Log.LogError(error); + } + } + } + catch (Exception ex) + { + Log.LogError("There was an unhandled exception while attempting to valid the buildtools.json."); + Log.LogError(ex.Message); + } + } + +#pragma warning disable CS0612 // Project Secrets is obsolete. This converts to the new AppSettings. internal void MigrateSecretsToSettings() { var config = ConfigHelper.GetConfig(BuildToolsConfigFilePath); - if(config.ProjectSecrets is not null && config.ProjectSecrets.Any()) + if (config.ProjectSecrets is not null && config.ProjectSecrets.Any()) { config.ProjectSecrets.ForEach(x => { @@ -98,7 +127,7 @@ internal void MigrateSecretsToSettings() var settings = new SettingsConfig { Accessibility = secretsConfig.Accessibility, - ClassName = secretsConfig.ClassName, + ClassName = secretsConfig.ClassName ?? "Secrets", Delimiter = secretsConfig.Delimiter, Namespace = secretsConfig.Namespace, Prefix = secretsConfig.Prefix, @@ -124,6 +153,7 @@ internal void MigrateSecretsToSettings() ConfigHelper.SaveConfig(config, BuildToolsConfigFilePath); } } +#pragma warning restore CS0612 // Project Secrets is obsolete. This converts to the new AppSettings. public static bool IsEnabled(ToolItem item) { From f25193d87947736e14ba9c2c6af906a1b8526adf Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sat, 3 Jul 2021 21:43:57 -0700 Subject: [PATCH 04/17] fix SettingsConfig namespace --- src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs | 1 - src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs | 3 +-- src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs | 3 +-- .../Models/Settings/SettingsConfig.cs | 3 +-- src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs | 5 ++--- .../Generators/Secrets/SecretsClassGenerator.cs | 1 - src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs | 1 - src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs | 1 - src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs | 2 +- .../Fixtures/Generators/SecretsClassGeneratorFixture.cs | 1 - .../Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs | 1 - 11 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs b/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs index b0d2603c..a4504c82 100644 --- a/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Build/BuildConfiguration.cs @@ -5,7 +5,6 @@ using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; using Mobile.BuildTools.Models.Settings; -using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; namespace Mobile.BuildTools.Build diff --git a/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs b/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs index f4341863..f6007374 100644 --- a/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Build/IBuildConfiguration.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; using Mobile.BuildTools.Logging; -using Mobile.BuildTools.Models.Settings; using Mobile.BuildTools.Models; +using Mobile.BuildTools.Models.Settings; using Mobile.BuildTools.Utils; -using Mobile.BuildTools.Reference.Models.Settings; namespace Mobile.BuildTools.Build { diff --git a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs index 26857f74..be7edcdd 100644 --- a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs @@ -3,7 +3,6 @@ using System.ComponentModel; using Mobile.BuildTools.Models.Secrets; using Mobile.BuildTools.Models.Settings; -using Mobile.BuildTools.Reference.Models.Settings; using Newtonsoft.Json; namespace Mobile.BuildTools.Models @@ -22,7 +21,7 @@ public string Schema [JsonProperty("appConfig", Required = Required.AllowNull)] public AppConfig AppConfig { get; set; } - [Description("Confgures the Mobile.BuildTools to copy the generated APK/AAB or IPA & dSYM to the root directory making it easier to locate and stage the build artifacts.")] + [Description("Configures the Mobile.BuildTools to copy the generated APK/AAB or IPA & dSYM to the root directory making it easier to locate and stage the build artifacts.")] [JsonProperty("artifactCopy", Required = Required.AllowNull)] public ArtifactCopy ArtifactCopy { get; set; } diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs index e2c1ff4b..25cbad1c 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using Mobile.BuildTools.Models.Settings; using Newtonsoft.Json; -namespace Mobile.BuildTools.Reference.Models.Settings +namespace Mobile.BuildTools.Models.Settings { public class SettingsConfig { diff --git a/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs b/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs index 9ee70503..9cd419fa 100644 --- a/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs +++ b/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.cs @@ -1,12 +1,11 @@ -using System.Collections.Generic; using System; +using System.Collections.Generic; using System.IO; -using Mobile.BuildTools.Build; using System.Linq; +using Mobile.BuildTools.Build; using Mobile.BuildTools.Models; using Mobile.BuildTools.Models.Settings; using Newtonsoft.Json; -using Mobile.BuildTools.Reference.Models.Settings; namespace Mobile.BuildTools.Utils { diff --git a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs index b88b6b24..c306788c 100644 --- a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs +++ b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs @@ -9,7 +9,6 @@ using Mobile.BuildTools.Build; using Mobile.BuildTools.Handlers; using Mobile.BuildTools.Models.Settings; -using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; diff --git a/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs b/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs index 1f56e818..b8e2c8ae 100644 --- a/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs +++ b/src/Mobile.BuildTools/Tasks/BuildEnvironmentDumpTask.cs @@ -4,7 +4,6 @@ using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; using Mobile.BuildTools.Models.Settings; -using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; namespace Mobile.BuildTools.Tasks diff --git a/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs b/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs index 2c288d85..7eb25ed8 100644 --- a/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs +++ b/src/Mobile.BuildTools/Tasks/BuildToolsTaskBase.cs @@ -11,7 +11,6 @@ using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; using Mobile.BuildTools.Models.Settings; -using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; using Newtonsoft.Json; diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index 89ff162a..e8bbff56 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -5,7 +5,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Mobile.BuildTools.Models; -using Mobile.BuildTools.Reference.Models.Settings; +using Mobile.BuildTools.Models.Settings; using Mobile.BuildTools.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs index 7bfce786..7fd9e1b8 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs @@ -10,7 +10,6 @@ using Mobile.BuildTools.Build; using Mobile.BuildTools.Generators.Secrets; using Mobile.BuildTools.Models.Settings; -using Mobile.BuildTools.Reference.Models.Settings; using Xunit; using Xunit.Abstractions; diff --git a/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs b/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs index c7865575..976dc5cd 100644 --- a/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs +++ b/tests/Mobile.BuildTools.Tests/Mocks/TestBuildConfiguration.cs @@ -3,7 +3,6 @@ using Mobile.BuildTools.Logging; using Mobile.BuildTools.Models; using Mobile.BuildTools.Models.Settings; -using Mobile.BuildTools.Reference.Models.Settings; using Mobile.BuildTools.Utils; namespace Mobile.BuildTools.Tests.Mocks From d65157efa0b6bb3d155b09c43511a3a5794d70e9 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sat, 3 Jul 2021 22:28:14 -0700 Subject: [PATCH 05/17] schema fixes --- Packages.props | 2 ++ .../Generators/ReleaseNotesGenerator.cs | 2 +- .../Mobile.BuildTools.Reference.csproj | 3 ++- .../Models/AppConfig.cs | 3 ++- .../Models/AppIcons/AppleIconSetImage.cs | 8 +++--- .../Models/AppIcons/BaseImageConfiguration.cs | 6 ++--- .../Models/AppIcons/ResourceDefinition.cs | 2 +- .../Models/AppIcons/WatermarkConfiguration.cs | 16 +++++------ .../Models/AutomaticVersioning.cs | 6 ++--- .../Models/BuildToolsConfig.cs | 27 ++++++++++--------- .../Models/EnvironmentSettings.cs | 4 +-- .../Models/GoogleConfig.cs | 4 +-- .../Models/ImageResize.cs | 4 +-- .../Models/ReleaseNotesOptions.cs | 14 ++++++---- .../Models/Secrets/SecretsConfig.cs | 16 +++++------ .../Models/Settings/SettingsConfig.cs | 12 ++++----- .../Models/Settings/ValueConfig.cs | 8 +++--- .../Models/TemplatedManifest.cs | 8 +++--- .../Models/ToolItem.cs | 6 ++--- .../Models/XamarinCss.cs | 4 +-- .../Mobile.BuildTools.csproj | 1 + .../Tasks/LocateBuildToolsConfigTask.cs | 2 +- 22 files changed, 85 insertions(+), 73 deletions(-) diff --git a/Packages.props b/Packages.props index f033c61e..b4964b8d 100644 --- a/Packages.props +++ b/Packages.props @@ -18,12 +18,14 @@ + + diff --git a/src/Mobile.BuildTools.Reference/Generators/ReleaseNotesGenerator.cs b/src/Mobile.BuildTools.Reference/Generators/ReleaseNotesGenerator.cs index ca4829a6..3c074555 100644 --- a/src/Mobile.BuildTools.Reference/Generators/ReleaseNotesGenerator.cs +++ b/src/Mobile.BuildTools.Reference/Generators/ReleaseNotesGenerator.cs @@ -23,7 +23,7 @@ protected override void ExecuteInternal() { var releaseNotesOptions = Build.Configuration.ReleaseNotes; - if (releaseNotesOptions.Disable) return; + if (releaseNotesOptions.Disable ?? false) return; var rootDirectory = ConfigHelper.GetConfigurationPath(Build.ProjectDirectory, Build.SolutionDirectory); var releaseNotesFilePath = new FileInfo(Path.Combine(releaseNotesOptions.CreateInRoot ? rootDirectory : OutputPath, releaseNotesOptions.FileName ?? "ReleaseNotes.txt")); diff --git a/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj b/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj index 67bda900..6a323192 100644 --- a/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj +++ b/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj @@ -11,9 +11,10 @@ - + + diff --git a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs index 64d613b9..0e9dfa62 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; namespace Mobile.BuildTools.Models @@ -7,7 +8,7 @@ public class AppConfig : ToolItem { [Description("Configures the bundling strategy for advanced scenarios. By default it will only transform the app.config with the app.{BuildConfiguration}.config. You may optionally bundle all app.*.config or those that do not include Debug, Release, Store.")] [DefaultValue(AppConfigStrategy.TransformOnly)] - [JsonProperty("strategy", Required = Required.AllowNull)] + [JsonProperty("strategy", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public AppConfigStrategy Strategy { get; set; } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs index 511f0b3a..ad9d957a 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs @@ -4,16 +4,16 @@ namespace Mobile.BuildTools.Models.AppIcons { public class AppleIconSetImage { - [JsonProperty("scale", Required = Required.AllowNull)] + [JsonProperty("scale", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Scale { get; set; } - [JsonProperty("size", Required = Required.AllowNull)] + [JsonProperty("size", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Size { get; set; } - [JsonProperty("idiom", Required = Required.AllowNull)] + [JsonProperty("idiom", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Idiom { get; set; } - [JsonProperty("filename", Required = Required.AllowNull)] + [JsonProperty("filename", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string FileName { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs index d3bbb42a..6a1d0ba8 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs @@ -10,8 +10,8 @@ public class BaseImageConfiguration : IImageResource [JsonProperty("ignore", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public bool Ignore { get; set; } - [Description("The name of the output image.")] - [JsonProperty("name", Required = Required.AllowNull)] + [Description("The name of the output image. This will default to the source image name.")] + [JsonProperty("name", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } [DefaultValue(1.0)] @@ -31,7 +31,7 @@ public class BaseImageConfiguration : IImageResource [JsonProperty("height", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public int? Height { get; set; } - [Description("The padding factor will resize the canvas size by the specified factor. For example a facor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.")] + [Description("The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.")] [JsonProperty("padFactor", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] public double? PaddingFactor { get; set; } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs index 88ff490b..151223b0 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs @@ -11,7 +11,7 @@ namespace Mobile.BuildTools.Models.AppIcons { public class ResourceDefinition : PlatformConfiguration { - [JsonProperty("$schema")] + [JsonProperty("$schema", NullValueHandling = NullValueHandling.Ignore)] public string Schema { get => "http://mobilebuildtools.com/schemas/v2/resourceDefinition.schema.json"; diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs index 2c6bc6d7..1d632ca2 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs @@ -7,35 +7,35 @@ namespace Mobile.BuildTools.Models.AppIcons public class WatermarkConfiguration { [Description("Specifies the watermark source file name.")] - [JsonProperty("sourceFile", Required = Required.AllowNull)] + [JsonProperty("sourceFile", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string SourceFile { get; set; } [Description("Specifies the color or colors (for gradients) that should be used for the automatically generated Banner style watermark.")] - [JsonProperty("colors", Required = Required.AllowNull)] + [JsonProperty("colors", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public IEnumerable Colors { get; set; } [Description("Specifies the watermark position.")] - [JsonProperty("position", Required = Required.AllowNull)] + [JsonProperty("position", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public WatermarkPosition? Position { get; set; } [Description("Specifies the text for the watermark. For example you may wish to have a banner such as 'Dev' or 'Stage' to signify which environment the app was built for. Other scenarios could include 'Free', 'Pro', 'Lite', etc.")] - [JsonProperty("text", Required = Required.AllowNull)] + [JsonProperty("text", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Text { get; set; } [Description("The hex or name of the color to use for the text.")] - [JsonProperty("textColor", Required = Required.AllowNull)] + [JsonProperty("textColor", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string TextColor { get; set; } [Description("The Font Family name if you want to use a custom font.")] - [JsonProperty("fontFamily", Required = Required.AllowNull)] + [JsonProperty("fontFamily", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string FontFamily { get; set; } [Description("Have a custom font you want to use, you can provide the file path to the Font File.")] - [JsonProperty("fontFile", Required = Required.AllowNull)] + [JsonProperty("fontFile", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string FontFile { get; set; } [Description("The opacity to use for the Watermark or Banner.")] - [JsonProperty("opacity", Required = Required.AllowNull)] + [JsonProperty("opacity", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public double? Opacity { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs index b798bd63..867c55e6 100644 --- a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs +++ b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs @@ -7,16 +7,16 @@ public class AutomaticVersioning : ToolItem { [DefaultValue(VersionBehavior.PreferBuildNumber)] [Description("Sets the default behavior for versioning the app. By default the Mobile.BuildTools will attempt to use a Build number and will fallback to a timestamp.")] - [JsonProperty("behavior", Required = Required.AllowNull)] + [JsonProperty("behavior", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public VersionBehavior Behavior { get; set; } [DefaultValue(VersionEnvironment.BuildHost)] [Description("Sets the default versioning environment. You can use this locally only, on build hosts only, or everywhere for a unique build number every time.")] - [JsonProperty("environment", Required = Required.Always)] + [JsonProperty("environment", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public VersionEnvironment Environment { get; set; } [Description("If you need to offset from your build number, you may want to set the Version Offset to get a version that will work for you.")] - [JsonProperty("versionOffset", Required = Required.AllowNull)] + [JsonProperty("versionOffset", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public int VersionOffset { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs index be7edcdd..034995cc 100644 --- a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs @@ -7,10 +7,11 @@ namespace Mobile.BuildTools.Models { + [Description("Configures the Mobile.BuildTools. This file should be located in the solution root directory next to the solution file.")] public class BuildToolsConfig { private string _schema = "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json"; - [JsonProperty("$schema")] + [JsonProperty("$schema", NullValueHandling = NullValueHandling.Ignore)] public string Schema { get => _schema; @@ -18,52 +19,52 @@ public string Schema } [Description("Configures the settings for bundling and compiling the app.config for use with the Mobile.BuildTools.Configuration package.")] - [JsonProperty("appConfig", Required = Required.AllowNull)] + [JsonProperty("appConfig", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public AppConfig AppConfig { get; set; } [Description("Configures the Mobile.BuildTools to copy the generated APK/AAB or IPA & dSYM to the root directory making it easier to locate and stage the build artifacts.")] - [JsonProperty("artifactCopy", Required = Required.AllowNull)] + [JsonProperty("artifactCopy", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public ArtifactCopy ArtifactCopy { get; set; } [Description("Configures the Mobile.BuildTools to automatically version the build for Android and iOS targets.")] - [JsonProperty("automaticVersioning", Required = Required.AllowNull)] + [JsonProperty("automaticVersioning", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public AutomaticVersioning AutomaticVersioning { get; set; } [Description("Configures the Mobile.BuildTools to compile SCSS files into Xamarin.Forms compliant CSS for styling your Xamarin.Forms application with CSS.")] - [JsonProperty("css", Required = Required.AllowNull)] + [JsonProperty("css", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public XamarinCss Css { get; set; } [Description("Configures the Mobile.BuildTools to intelligently process image sources to be bundled into your Android and iOS application.")] - [JsonProperty("images", Required = Required.AllowNull)] + [JsonProperty("images", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public ImageResize Images { get; set; } [Description("Configures the Mobile.BuildTools to process Tokens within the AndroidManifest.xml and Info.plist, replacing values like $AppName$ with a variable named AppName.")] - [JsonProperty("manifests", Required = Required.AllowNull)] + [JsonProperty("manifests", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public TemplatedManifest Manifests { get; set; } [Description("Configures the Mobile.BuildTools to generate Release Notes for your build, based on the Git commit messages.")] - [JsonProperty("releaseNotes", Required = Required.AllowNull)] + [JsonProperty("releaseNotes", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public ReleaseNotesOptions ReleaseNotes { get; set; } [Obsolete] [Description("Note: This is obsolete, please use `appSettings`.")] - [JsonProperty("projectSecrets", Required = Required.AllowNull)] + [JsonProperty("projectSecrets", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public Dictionary ProjectSecrets { get; set; } [Description("Replaces the former 'Secrets' API, with a newly generated AppSettings class. This will allow you to generate one or more configuration classes.")] - [JsonProperty("appSettings", Required = Required.AllowNull)] + [JsonProperty("appSettings", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public Dictionary> AppSettings { get; set; } [Description("Configures the Mobile.BuildTools with default non-sensitive environment values. If the value does not exist in the System Environment, this value will be used.")] - [JsonProperty("environment", Required = Required.AllowNull)] + [JsonProperty("environment", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public EnvironmentSettings Environment { get; set; } [Description("Configures the Mobile.BuildTools to automatically generate and include the google-services.json or GoogleService-Info.plist from an Environment variable. This can be either a raw string value or file location if using Secure Files.")] - [JsonProperty("google", Required = Required.AllowNull)] + [JsonProperty("google", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public GoogleConfig Google { get; set; } [Description("Having issues with the Mobile.BuildTools. Enable the Debug property to help you get some additional debug output in the build logs to help identify configuration issues.")] - [JsonProperty("debug", Required = Required.AllowNull)] + [JsonProperty("debug", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public bool Debug { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs b/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs index f19bfac0..5a1fa9b5 100644 --- a/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs +++ b/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs @@ -11,10 +11,10 @@ public EnvironmentSettings() Configuration = new Dictionary>(); } - [JsonProperty("defaults", Required = Required.AllowNull)] + [JsonProperty("defaults", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public Dictionary Defaults { get; set; } - [JsonProperty("configuration", Required = Required.AllowNull)] + [JsonProperty("configuration", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public Dictionary> Configuration { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs b/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs index 0b40ffbc..187b74a4 100644 --- a/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs @@ -4,10 +4,10 @@ namespace Mobile.BuildTools.Models { public class GoogleConfig { - [JsonProperty("servicesJson", Required = Required.AllowNull)] + [JsonProperty("servicesJson", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string ServicesJson { get; set; } - [JsonProperty("infoPlist", Required = Required.AllowNull)] + [JsonProperty("infoPlist", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string InfoPlist { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ImageResize.cs b/src/Mobile.BuildTools.Reference/Models/ImageResize.cs index 8e0f1824..33bb801a 100644 --- a/src/Mobile.BuildTools.Reference/Models/ImageResize.cs +++ b/src/Mobile.BuildTools.Reference/Models/ImageResize.cs @@ -12,10 +12,10 @@ public ImageResize() ConditionalDirectories = new Dictionary>(); } - [JsonProperty("directories", Required = Required.AllowNull)] + [JsonProperty("directories", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public List Directories { get; set; } - [JsonProperty("conditionalDirectories", Required = Required.AllowNull)] + [JsonProperty("conditionalDirectories", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public Dictionary> ConditionalDirectories { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs b/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs index 7fba0e7a..e6b5f170 100644 --- a/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs +++ b/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs @@ -6,23 +6,27 @@ namespace Mobile.BuildTools.Models public class ReleaseNotesOptions : ToolItem { [DefaultValue(10)] - [JsonProperty("maxDays", Required = Required.AllowNull)] + [Description("The number of days back to look when generating the Release Notes")] + [JsonProperty("maxDays", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public int MaxDays { get; set; } [DefaultValue(10)] - [JsonProperty("maxCommit", Required = Required.AllowNull)] + [Description("The maximum number of commits to lookup")] + [JsonProperty("maxCommit", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public int MaxCommit { get; set; } [DefaultValue(250)] - [JsonProperty("characterLimit", Required = Required.AllowNull)] + [Description("The maximum character limit for generated Release Notes")] + [JsonProperty("characterLimit", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public int CharacterLimit { get; set; } [DefaultValue("releasenotes.txt")] - [JsonProperty("filename", Required = Required.AllowNull)] + [Description("The output filename such as 'ReleaseNotes.md' or 'ReleaseNotes.txt'")] + [JsonProperty("filename", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string FileName { get; set; } [DefaultValue(true)] - [JsonProperty("createInRoot", Required = Required.AllowNull)] + [JsonProperty("createInRoot", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public bool CreateInRoot { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs index ff018a45..6389105f 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs @@ -12,28 +12,28 @@ public SecretsConfig() Properties = new List(); } - [JsonProperty("disable", Required = Required.AllowNull)] + [JsonProperty("disable", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public bool Disable { get; set; } - [JsonProperty("delimiter", Required = Required.AllowNull)] + [JsonProperty("delimiter", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Delimiter { get; set; } - [JsonProperty("prefix", Required = Required.AllowNull)] + [JsonProperty("prefix", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Prefix { get; set; } - [JsonProperty("className", Required = Required.AllowNull)] + [JsonProperty("className", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string ClassName { get; set; } - [JsonProperty("accessibility", Required = Required.AllowNull)] + [JsonProperty("accessibility", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public Accessibility Accessibility { get; set; } - [JsonProperty("rootNamespace", Required = Required.AllowNull)] + [JsonProperty("rootNamespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string RootNamespace { get; set; } - [JsonProperty("namespace", Required = Required.AllowNull)] + [JsonProperty("namespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Namespace { get; set; } - [JsonProperty("properties", Required = Required.AllowNull)] + [JsonProperty("properties", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public List Properties { get; set; } public bool ContainsKey(string key) => Properties != null && Properties.Any(x => x.Name == key); diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs index 25cbad1c..2cce195b 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs @@ -14,31 +14,31 @@ public SettingsConfig() [DefaultValue(";")] [Description("The delimiter used for arrays. By default this will use a semi-colon.")] - [JsonProperty("delimiter", Required = Required.AllowNull)] + [JsonProperty("delimiter", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Delimiter { get; set; } [DefaultValue("BuildTools_")] [Description("The prefix the Mobile.BuildTools should use to look for variables. Note if a variable exists with the exact name it will be used if one does not exist with the prefix.")] - [JsonProperty("prefix", Required = Required.AllowNull)] + [JsonProperty("prefix", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Prefix { get; set; } [DefaultValue("AppSettings")] [Description("The name of the generated class.")] - [JsonProperty("className", Required = Required.AllowNull)] + [JsonProperty("className", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string ClassName { get; set; } [DefaultValue(Accessibility.Internal)] [Description("The default visibility of the generated class, either 'public' or 'internal'.")] - [JsonProperty("accessibility", Required = Required.AllowNull)] + [JsonProperty("accessibility", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public Accessibility Accessibility { get; set; } [Description("If using a Shared project as is typically the case with an Uno Platform app, be sure to specify the Root Namespace to use as this will change otherwise based on which platform target you are compiling.")] - [JsonProperty("rootNamespace", Required = Required.AllowNull)] + [JsonProperty("rootNamespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string RootNamespace { get; set; } [DefaultValue("Helpers")] [Description("The partial relative namespace to generate. By default this will be the Helpers namespace, you may set it to the root namespace by providing a period '.' for the value.")] - [JsonProperty("namespace", Required = Required.AllowNull)] + [JsonProperty("namespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Namespace { get; set; } [Description("The properties that should be generated in the generated AppSettings class.")] diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs index 066d3054..37613f3b 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs @@ -5,16 +5,16 @@ namespace Mobile.BuildTools.Models.Settings { public class ValueConfig { - [JsonProperty("name", Required = Required.AllowNull)] + [JsonProperty("name", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } - [JsonProperty("type", Required = Required.AllowNull)] + [JsonProperty("type", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public PropertyType PropertyType { get; set; } - [JsonProperty("isArray", Required = Required.AllowNull)] + [JsonProperty("isArray", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public bool IsArray { get; set; } - [JsonProperty("defaultValue", Required = Required.AllowNull)] + [JsonProperty("defaultValue", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string DefaultValue { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs b/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs index 5617ace5..19122e99 100644 --- a/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs +++ b/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs @@ -5,16 +5,18 @@ namespace Mobile.BuildTools.Models { public class TemplatedManifest : ToolItem { + [Description("The Regex escaped value of the Token. '$$' is used by default to look for token matching the pattern $TokenName$.")] [DefaultValue("$$")] - [JsonProperty("token", Required = Required.AllowNull)] + [JsonProperty("token", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string Token { get; set; } [DefaultValue("Manifest_")] - [JsonProperty("variablePrefix", Required = Required.AllowNull)] + [JsonProperty("variablePrefix", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public string VariablePrefix { get; set; } + [Description("If set to true, this will generate a build time error if a token is found which does not have a value in the environment or secrets.json.")] [DefaultValue(false)] - [JsonProperty("missingTokensAsErrors", Required = Required.AllowNull)] + [JsonProperty("missingTokensAsErrors", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public bool MissingTokensAsErrors { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ToolItem.cs b/src/Mobile.BuildTools.Reference/Models/ToolItem.cs index dabcfb29..967dfaa2 100644 --- a/src/Mobile.BuildTools.Reference/Models/ToolItem.cs +++ b/src/Mobile.BuildTools.Reference/Models/ToolItem.cs @@ -5,9 +5,9 @@ namespace Mobile.BuildTools.Models { public abstract class ToolItem { - [Description("Disables this Mobile.Build.Tools Task")] + [Description("Disables this Mobile.BuildTools Task")] [DefaultValue(false)] - [JsonProperty("disable", Required = Required.AllowNull)] - public bool Disable { get; set; } + [JsonProperty("disable", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + public bool? Disable { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs b/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs index eca6a2dc..0c5ad0bb 100644 --- a/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs +++ b/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs @@ -6,11 +6,11 @@ namespace Mobile.BuildTools.Models public class XamarinCss : ToolItem { [DefaultValue(false)] - [JsonProperty("minify", Required = Required.AllowNull)] + [JsonProperty("minify", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public bool Minify { get; set; } [DefaultValue(false)] - [JsonProperty("bundleScss", Required = Required.AllowNull)] + [JsonProperty("bundleScss", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] public bool BundleScss { get; set; } } } diff --git a/src/Mobile.BuildTools/Mobile.BuildTools.csproj b/src/Mobile.BuildTools/Mobile.BuildTools.csproj index cf15e3a5..469981aa 100644 --- a/src/Mobile.BuildTools/Mobile.BuildTools.csproj +++ b/src/Mobile.BuildTools/Mobile.BuildTools.csproj @@ -34,6 +34,7 @@ + diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index e8bbff56..2adaad93 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -159,7 +159,7 @@ public static bool IsEnabled(ToolItem item) { if (item is null) return true; - return !item.Disable; + return !(item.Disable ?? false); } private bool IsCrossTargeting() From 9f5ca0500e75d8293b7415fbbe5e7dd2822bc7c3 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sat, 3 Jul 2021 23:40:06 -0700 Subject: [PATCH 06/17] add schema generator --- Mobile.BuildTools.sln | 17 + Update-Schemas.ps1 | 1 + docs/schemas/v2/buildtools.schema.json | 286 ++++-- .../schemas/v2/resourceDefinition.schema.json | 881 +++++++++++++++--- .../Mobile.BuildTools.SchemaGenerator.csproj | 16 + .../Program.cs | 47 + 6 files changed, 1040 insertions(+), 208 deletions(-) create mode 100644 Update-Schemas.ps1 create mode 100644 tools/Mobile.BuildTools.SchemaGenerator/Mobile.BuildTools.SchemaGenerator.csproj create mode 100644 tools/Mobile.BuildTools.SchemaGenerator/Program.cs diff --git a/Mobile.BuildTools.sln b/Mobile.BuildTools.sln index 240c34a6..0466d206 100644 --- a/Mobile.BuildTools.sln +++ b/Mobile.BuildTools.sln @@ -28,6 +28,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mobile.BuildTools.Reference EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mobile.BuildTools.XamarinSdk", "src\Mobile.BuildTools.XamarinSdk\Mobile.BuildTools.XamarinSdk.csproj", "{2367403E-8614-4F4A-8627-7FC41995F96D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{D096610B-57C6-476F-99CE-FA949E35F2BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mobile.BuildTools.SchemaGenerator", "tools\Mobile.BuildTools.SchemaGenerator\Mobile.BuildTools.SchemaGenerator.csproj", "{91F7409A-B8C5-488B-8E45-2CD368F193C3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -110,6 +114,18 @@ Global {2367403E-8614-4F4A-8627-7FC41995F96D}.Release|x64.Build.0 = Release|Any CPU {2367403E-8614-4F4A-8627-7FC41995F96D}.Release|x86.ActiveCfg = Release|Any CPU {2367403E-8614-4F4A-8627-7FC41995F96D}.Release|x86.Build.0 = Release|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Debug|x64.ActiveCfg = Debug|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Debug|x64.Build.0 = Debug|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Debug|x86.ActiveCfg = Debug|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Debug|x86.Build.0 = Debug|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Release|Any CPU.Build.0 = Release|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Release|x64.ActiveCfg = Release|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Release|x64.Build.0 = Release|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Release|x86.ActiveCfg = Release|Any CPU + {91F7409A-B8C5-488B-8E45-2CD368F193C3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -121,6 +137,7 @@ Global {82EF42A8-49A3-4086-A045-5E2C2F094C04} = {2510D213-21AF-4B25-888D-E63163356A4F} {20FB88AB-5FE0-41DC-97B7-4C8772A0D059} = {C7456F1C-2B86-4C40-A228-72209A7355A0} {2367403E-8614-4F4A-8627-7FC41995F96D} = {C7456F1C-2B86-4C40-A228-72209A7355A0} + {91F7409A-B8C5-488B-8E45-2CD368F193C3} = {D096610B-57C6-476F-99CE-FA949E35F2BE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {429C8CA0-DB8C-4CF5-9627-187B9CC47A99} diff --git a/Update-Schemas.ps1 b/Update-Schemas.ps1 new file mode 100644 index 00000000..bb3fe9fc --- /dev/null +++ b/Update-Schemas.ps1 @@ -0,0 +1 @@ +dotnet run --project .\tools\Mobile.BuildTools.SchemaGenerator\Mobile.BuildTools.SchemaGenerator.csproj -- --output-directory .\docs\schemas\v2 diff --git a/docs/schemas/v2/buildtools.schema.json b/docs/schemas/v2/buildtools.schema.json index 58bcf0a1..6df75cf3 100644 --- a/docs/schemas/v2/buildtools.schema.json +++ b/docs/schemas/v2/buildtools.schema.json @@ -1,32 +1,35 @@ { - "$schema": "http://json-schema.org/draft-04/schema", - "id": "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json", - "title": "Mobile.BuildTools Project Configuration", - "description": "Configures the Mobile.BuildTools. This file should generally be located next to the Solution file and be a central configuration for the entire solution.", + "description": "Configures the Mobile.BuildTools. This file should be located in the solution root directory next to the solution file.", "definitions": { "AppConfig": { + "description": "Configures the settings for bundling and compiling the app.config for use with the Mobile.BuildTools.Configuration package.", "type": [ "object", "null" ], "properties": { - "disable": { - "description": "Disables this Mobile.BuildTools Task", - "type": [ "boolean", "null" ], - "default": false - }, "strategy": { + "description": "Configures the bundling strategy for advanced scenarios. By default it will only transform the app.config with the app.{BuildConfiguration}.config. You may optionally bundle all app.*.config or those that do not include Debug, Release, Store.", + "type": "string", + "default": "TransformOnly", "enum": [ "TransformOnly", "BundleAll", "BundleNonStandard" + ] + }, + "disable": { + "description": "Disables this Mobile.BuildTools Task", + "type": [ + "boolean", + "null" ], - "default": "TransformOnly", - "description": "Defines how we should determine which app.config's to include. By default we only bundle the transformed app.config" + "default": false } } }, "ArtifactCopy": { + "description": "Configures the Mobile.BuildTools to copy the generated APK/AAB or IPA & dSYM to the root directory making it easier to locate and stage the build artifacts.", "type": [ "object", "null" @@ -34,28 +37,35 @@ "properties": { "disable": { "description": "Disables this Mobile.BuildTools Task", - "type": [ "boolean", "null" ], + "type": [ + "boolean", + "null" + ], "default": false } } }, "AutomaticVersioning": { + "description": "Configures the Mobile.BuildTools to automatically version the build for Android and iOS targets.", "type": [ "object", "null" ], "properties": { "behavior": { - "description": "Configures whether the Versioning should prefer to use a Build Number or a Timestamp.", + "description": "Sets the default behavior for versioning the app. By default the Mobile.BuildTools will attempt to use a Build number and will fallback to a timestamp.", + "type": "string", "default": "PreferBuildNumber", "enum": [ + "Off", "PreferBuildNumber", "Timestamp" ] }, "environment": { - "description": "Configures whether Versioning should occur only on a CI Host, Local Builds or anywhere.", - "default": "All", + "description": "Sets the default versioning environment. You can use this locally only, on build hosts only, or everywhere for a unique build number every time.", + "type": "string", + "default": "BuildHost", "enum": [ "All", "BuildHost", @@ -63,71 +73,87 @@ ] }, "versionOffset": { - "type": [ "integer", "null" ], - "default": 0 + "description": "If you need to offset from your build number, you may want to set the Version Offset to get a version that will work for you.", + "type": "integer" }, "disable": { "description": "Disables this Mobile.BuildTools Task", - "type": [ "boolean", "null" ], + "type": [ + "boolean", + "null" + ], "default": false } } }, "EnvironmentSettings": { + "description": "Configures the Mobile.BuildTools with default non-sensitive environment values. If the value does not exist in the System Environment, this value will be used.", "type": [ "object", "null" ], "properties": { - "default": { + "defaults": { "type": [ "object", "null" ], - "default": { } + "additionalProperties": { + "type": [ + "string", + "null" + ] + } }, "configuration": { "type": [ "object", "null" ], - "default": { "Debug": { } } + "additionalProperties": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": [ + "string", + "null" + ] + } + } } } }, - "GoogleServices": { + "GoogleConfig": { + "description": "Configures the Mobile.BuildTools to automatically generate and include the google-services.json or GoogleService-Info.plist from an Environment variable. This can be either a raw string value or file location if using Secure Files.", "type": [ "object", "null" ], - "description": "GoogleServices location. This can be a file location or the raw contents.", "properties": { "servicesJson": { "type": [ "string", "null" - ], - "default": "Environment Variable name", - "description": "Environment Variable name containing the file contents or file path to the google-services.json" + ] }, "infoPlist": { "type": [ "string", "null" - ], - "default": "Environment Variable name", - "description": "Environment Variable name containing the file contents or file path to the GoogleService-Info.plist" + ] } } }, "ImageResize": { + "description": "Configures the Mobile.BuildTools to intelligently process image sources to be bundled into your Android and iOS application.", "type": [ "object", "null" ], "properties": { "directories": { - "description": "A list of search directories relative to this file", "type": [ "array", "null" @@ -137,13 +163,9 @@ "string", "null" ] - }, - "default": [ - "Images" - ] + } }, "conditionalDirectories": { - "description": "A list of conditional search directories relative to this file. Supports build Configuration, monoandroid, or xamarin.ios", "type": [ "object", "null" @@ -159,29 +181,20 @@ "null" ] } - }, - "default": { - "Xamarin.iOS": [ "Images/iOS" ], - "MonoAndroid": [ "Images/Android" ], - "Debug": [ "Images/Dev" ] } }, - "watermarkOpacity": { - "description": "Configures the opacity to use for any watermark files (0 - 1)", + "disable": { + "description": "Disables this Mobile.BuildTools Task", "type": [ - "number", + "boolean", "null" ], - "default": 0.95 - }, - "disable": { - "description": "Disables this Mobile.BuildTools Task", - "type": [ "boolean", "null" ], "default": false } } }, "ReleaseNotesOptions": { + "description": "Configures the Mobile.BuildTools to generate Release Notes for your build, based on the Git commit messages.", "type": [ "object", "null" @@ -203,7 +216,7 @@ "default": 250 }, "filename": { - "description": "The output filename such as `ReleaseNotes.md` or `ReleaseNotes.txt`", + "description": "The output filename such as 'ReleaseNotes.md' or 'ReleaseNotes.txt'", "type": [ "string", "null" @@ -212,12 +225,14 @@ }, "createInRoot": { "type": "boolean", - "description": "If true it will create the Release Notes in the Solution Directory. Otherwise it will be created in the Project Directory.", "default": true }, "disable": { "description": "Disables this Mobile.BuildTools Task", - "type": [ "boolean", "null" ], + "type": [ + "boolean", + "null" + ], "default": false } } @@ -229,24 +244,64 @@ ], "properties": { "disable": { - "description": "Disables this Mobile.BuildTools Task", - "type": "boolean", - "default": false + "type": "boolean" }, - "accessibility": { - "description": "Specifies the default class Accessibility.", + "delimiter": { "type": [ "string", "null" - ], + ] + }, + "prefix": { + "type": [ + "string", + "null" + ] + }, + "className": { + "type": [ + "string", + "null" + ] + }, + "accessibility": { + "type": "string", "enum": [ - "public", - "internal" - ], - "default": "internal" + "Internal", + "Public" + ] }, + "rootNamespace": { + "type": [ + "string", + "null" + ] + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "properties": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/ValueConfig" + } + } + } + }, + "SettingsConfig": { + "type": [ + "object", + "null" + ], + "properties": { "delimiter": { - "description": "The delimiter used to separate values into an array. (i.e. ';' value1;value2)", + "description": "The delimiter used for arrays. By default this will use a semi-colon.", "type": [ "string", "null" @@ -254,6 +309,7 @@ "default": ";" }, "prefix": { + "description": "The prefix the Mobile.BuildTools should use to look for variables. Note if a variable exists with the exact name it will be used if one does not exist with the prefix.", "type": [ "string", "null" @@ -261,31 +317,40 @@ "default": "BuildTools_" }, "className": { + "description": "The name of the generated class.", "type": [ "string", "null" ], - "default": "Secrets" + "default": "AppSettings" }, - "namespace": { + "accessibility": { + "description": "The default visibility of the generated class, either 'public' or 'internal'.", + "type": "string", + "default": "Internal", + "enum": [ + "Internal", + "Public" + ] + }, + "rootNamespace": { + "description": "If using a Shared project as is typically the case with an Uno Platform app, be sure to specify the Root Namespace to use as this will change otherwise based on which platform target you are compiling.", "type": [ "string", "null" - ], - "default": "Helpers" + ] }, - "rootNamespace": { + "namespace": { + "description": "The partial relative namespace to generate. By default this will be the Helpers namespace, you may set it to the root namespace by providing a period '.' for the value.", "type": [ "string", "null" ], - "description": "An override to the project root namespace. This may be useful if using a Shared project." + "default": "Helpers" }, "properties": { - "type": [ - "array", - "null" - ], + "description": "The properties that should be generated in the generated AppSettings class.", + "type": "array", "items": { "$ref": "#/definitions/ValueConfig" } @@ -296,12 +361,14 @@ ] }, "TemplatedManifest": { + "description": "Configures the Mobile.BuildTools to process Tokens within the AndroidManifest.xml and Info.plist, replacing values like $AppName$ with a variable named AppName.", "type": [ "object", "null" ], "properties": { "token": { + "description": "The Regex escaped value of the Token. '$$' is used by default to look for token matching the pattern $TokenName$.", "type": [ "string", "null" @@ -316,12 +383,16 @@ "default": "Manifest_" }, "missingTokensAsErrors": { - "type": [ "boolean", "null" ], + "description": "If set to true, this will generate a build time error if a token is found which does not have a value in the environment or secrets.json.", + "type": "boolean", "default": false }, "disable": { "description": "Disables this Mobile.BuildTools Task", - "type": [ "boolean", "null" ], + "type": [ + "boolean", + "null" + ], "default": false } } @@ -333,14 +404,13 @@ ], "properties": { "name": { - "description": "The name of the Property that will be generated", "type": [ "string", "null" ] }, "type": { - "description": "The property type (i.e. string, int, DateTime)", + "type": "string", "enum": [ "String", "Bool", @@ -361,36 +431,40 @@ "Guid", "Uri", "TimeSpan" - ], - "default": "String" + ] + }, + "isArray": { + "type": "boolean" }, "defaultValue": { - "description": "Provides a default value, usually for safe for source control values such as a Dev API or the null/default keywords", "type": [ "string", "null" ] - }, - "isArray": { - "description": "Flag to indicate whether to generate the property as an Array", - "type": "boolean", - "default": false } - }, - "required": [ - "name", - "type" - ] + } }, "XamarinCss": { + "description": "Configures the Mobile.BuildTools to compile SCSS files into Xamarin.Forms compliant CSS for styling your Xamarin.Forms application with CSS.", "type": [ "object", "null" ], "properties": { + "minify": { + "type": "boolean", + "default": false + }, + "bundleScss": { + "type": "boolean", + "default": false + }, "disable": { "description": "Disables this Mobile.BuildTools Task", - "type": [ "boolean", "null" ], + "type": [ + "boolean", + "null" + ], "default": false } } @@ -398,6 +472,12 @@ }, "type": "object", "properties": { + "$schema": { + "type": [ + "string", + "null" + ] + }, "appConfig": { "$ref": "#/definitions/AppConfig" }, @@ -410,9 +490,6 @@ "css": { "$ref": "#/definitions/XamarinCss" }, - "google": { - "$ref": "#/definitions/GoogleServices" - }, "images": { "$ref": "#/definitions/ImageResize" }, @@ -423,6 +500,7 @@ "$ref": "#/definitions/ReleaseNotesOptions" }, "projectSecrets": { + "description": "Note: This is obsolete, please use `appSettings`.", "type": [ "object", "null" @@ -431,11 +509,31 @@ "$ref": "#/definitions/SecretsConfig" } }, + "appSettings": { + "description": "Replaces the former 'Secrets' API, with a newly generated AppSettings class. This will allow you to generate one or more configuration classes.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/SettingsConfig" + } + } + }, "environment": { "$ref": "#/definitions/EnvironmentSettings" }, + "google": { + "$ref": "#/definitions/GoogleConfig" + }, "debug": { + "description": "Having issues with the Mobile.BuildTools. Enable the Debug property to help you get some additional debug output in the build logs to help identify configuration issues.", "type": "boolean" } } -} +} \ No newline at end of file diff --git a/docs/schemas/v2/resourceDefinition.schema.json b/docs/schemas/v2/resourceDefinition.schema.json index 468e3c26..cf49df78 100644 --- a/docs/schemas/v2/resourceDefinition.schema.json +++ b/docs/schemas/v2/resourceDefinition.schema.json @@ -1,158 +1,782 @@ { "definitions": { - "WatermarkConfiguration": { - "type": [ "object", "null" ], + "BaseImageConfiguration": { + "type": [ + "object", + "null" + ], "properties": { - "sourceFile": { - "type": [ "string", "null" ], - "description": "The name of the source file you want to overlay without an extension or folder path" + "ignore": { + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", + "default": false }, - "colors": { - "type": [ "array", "null" ], + "name": { + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] + }, + "scale": { + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 + }, + "backgroundColor": { + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] + }, + "width": { + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "height": { + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "padFactor": { + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] + }, + "padColor": { + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] + }, + "watermark": { + "$ref": "#/definitions/WatermarkConfiguration" + }, + "resourceType": { + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] + } + } + }, + "PlatformConfiguration": { + "description": "Specifies the Android specific configuration for the source image.", + "type": [ + "object", + "null" + ], + "properties": { + "additionalOutputs": { + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], "items": { - "type": "string", - "description": "A Color Name such as Red, or OrangeRed... or a Hex String" + "$ref": "#/definitions/BaseImageConfiguration" } }, - "position": { - "type": [ "string", "null" ], - "enum": [ "Top", "Bottom", "TopLeft", "TopRight", "BottomLeft", "BottomRight" ], - "default": "BottomRight" + "ignore": { + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", + "default": false }, - "text": { - "type": [ "string", "null" ], - "description": "The text to be added to the banner. (i.e. Dev, Stage, Lite, Pro)" + "name": { + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] }, - "textColor": { - "type": [ "string", "null" ], - "description": "The color of the text" + "scale": { + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 }, - "fontFamily": { - "type": [ "string", "null" ], - "description": "The name of a Font Family installed on the machine. This defaults to Arial" + "backgroundColor": { + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] }, - "fontFile": { - "type": [ "string", "null" ], - "description": "The relative path to a font file to use." + "width": { + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] }, - "opacity": { - "type": [ "number", "null" ], - "description": "The opacity of the banner over your icon. This is 0.95 by default" + "height": { + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "padFactor": { + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] + }, + "padColor": { + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] + }, + "watermark": { + "$ref": "#/definitions/WatermarkConfiguration" + }, + "resourceType": { + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] + } + } + }, + "PlatformConfiguration-1": { + "description": "Specifies the configuration for all Apple targets if building for iOS, macOS, TVOS, WatchOS.", + "type": [ + "object", + "null" + ], + "properties": { + "additionalOutputs": { + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/BaseImageConfiguration" + } + }, + "ignore": { + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", + "default": false + }, + "name": { + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] + }, + "scale": { + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 + }, + "backgroundColor": { + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] + }, + "width": { + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "height": { + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "padFactor": { + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] + }, + "padColor": { + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] + }, + "watermark": { + "$ref": "#/definitions/WatermarkConfiguration" + }, + "resourceType": { + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] } } }, - "ImageConfiguration": { + "PlatformConfiguration-2": { + "description": "Specifies the iOS specific configuration for the source image.", "type": [ "object", "null" ], "properties": { + "additionalOutputs": { + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/BaseImageConfiguration" + } + }, "ignore": { - "type": [ "boolean", "null" ], + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", "default": false }, "name": { - "type": [ "string", "null" ], - "description": "Defines the name of the output image or iconset without an extension" + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] }, "scale": { - "type": [ "number", "null" ], - "default": 1, - "description": "Sets the scale from size of the source image to the largest output image" + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 }, "backgroundColor": { - "type": [ "string", "null" ], - "default": "#FFFFFF", - "description": "Sets a background color to use for a transparent image if desired" + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] }, "width": { - "type": [ "integer", "null" ], - "description": "Sets the desired width of the largest output image" + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] }, "height": { - "type": [ "integer", "null" ], - "description": "Sets the desired height of the largest output image" + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] }, "padFactor": { - "type": [ "number", "null" ], - "description": "If set will change the largest image output size by changing the canvas size by the set factor. This should be greater than 1, like 1.5." + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] }, "padColor": { - "type": [ "string", "null" ], - "description": "If set this will set a color to use on the background when adding a pad to your image" + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] + }, + "watermark": { + "$ref": "#/definitions/WatermarkConfiguration" }, "resourceType": { - "type": [ "string", "null" ], - "enum": [ "Default", "Drawable", "Mipmap", "AllSquareTiles", "SquareTile", "SmallTile", "WideTile", "SplashScreen" ], - "description": "This is used by Android and UWP. This will be Drawable by default on Android. On UWP this will by default generate a standard image asset, or you can specify other app resource types." + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] + } + } + }, + "PlatformConfiguration-3": { + "description": "Specifies the TVOS specific configuration for the source image.", + "type": [ + "object", + "null" + ], + "properties": { + "additionalOutputs": { + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/BaseImageConfiguration" + } + }, + "ignore": { + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", + "default": false + }, + "name": { + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] + }, + "scale": { + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 + }, + "backgroundColor": { + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] + }, + "width": { + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "height": { + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "padFactor": { + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] + }, + "padColor": { + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] }, "watermark": { "$ref": "#/definitions/WatermarkConfiguration" + }, + "resourceType": { + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] } } }, - "PlatformConfiguration": { + "PlatformConfiguration-4": { + "description": "Specifies the macOS specific configuration for the source image.", "type": [ "object", "null" ], "properties": { + "additionalOutputs": { + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/BaseImageConfiguration" + } + }, "ignore": { - "type": [ "boolean", "null" ], + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", "default": false }, "name": { - "type": [ "string", "null" ], - "description": "Defines the name of the output image or iconset without an extension" + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] }, "scale": { - "type": [ "number", "null" ], - "default": 1, - "description": "Sets the scale from size of the source image to the largest output image" + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 }, "backgroundColor": { - "type": [ "string", "null" ], - "default": "#FFFFFF", - "description": "Sets a background color to use for a transparent image if desired" + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] }, "width": { - "type": [ "integer", "null" ], - "description": "Sets the desired width of the largest output image" + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] }, "height": { - "type": [ "integer", "null" ], - "description": "Sets the desired height of the largest output image" + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] }, "padFactor": { - "type": [ "number", "null" ], - "description": "If set will change the largest image output size by changing the canvas size by the set factor. This should be greater than 1, like 1.5." + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] }, "padColor": { - "type": [ "string", "null" ], - "description": "If set this will set a color to use on the background when adding a pad to your image" + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] + }, + "watermark": { + "$ref": "#/definitions/WatermarkConfiguration" }, "resourceType": { - "type": [ "string", "null" ], - "enum": [ "Default", "Drawable", "Mipmap", "AllSquareTiles", "SquareTile", "SmallTile", "WideTile", "SplashScreen" ], - "description": "This is used by Android and UWP. This will be Drawable by default on Android. On UWP this will by default generate a standard image asset, or you can specify other app resource types." + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] + } + } + }, + "PlatformConfiguration-5": { + "description": "Specifies the Tizen specific configuration for the source image.", + "type": [ + "object", + "null" + ], + "properties": { + "additionalOutputs": { + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/BaseImageConfiguration" + } + }, + "ignore": { + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", + "default": false + }, + "name": { + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] + }, + "scale": { + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 + }, + "backgroundColor": { + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] + }, + "width": { + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "height": { + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "padFactor": { + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] + }, + "padColor": { + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] }, "watermark": { - "$ref": "#/definitions/WatermarkConfiguration", - "description": "A watermark to overlay on the output image" + "$ref": "#/definitions/WatermarkConfiguration" }, + "resourceType": { + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] + } + } + }, + "PlatformConfiguration-6": { + "description": "Specifies the UWP specific configuration for the source image.", + "type": [ + "object", + "null" + ], + "properties": { "additionalOutputs": { - "type": [ "array", "null" ], + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/BaseImageConfiguration" + } + }, + "ignore": { + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", + "default": false + }, + "name": { + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] + }, + "scale": { + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 + }, + "backgroundColor": { + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] + }, + "width": { + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "height": { + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] + }, + "padFactor": { + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] + }, + "padColor": { + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] + }, + "watermark": { + "$ref": "#/definitions/WatermarkConfiguration" + }, + "resourceType": { + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] + } + } + }, + "WatermarkConfiguration": { + "description": "Specifies a configuration for adding a watermark or banner on a specified image.", + "type": [ + "object", + "null" + ], + "properties": { + "sourceFile": { + "description": "Specifies the watermark source file name.", + "type": [ + "string", + "null" + ] + }, + "colors": { + "description": "Specifies the color or colors (for gradients) that should be used for the automatically generated Banner style watermark.", + "type": [ + "array", + "null" + ], "items": { - "$ref": "#/definitions/ImageConfiguration" - }, - "description": "Optional additional copies of the input image" + "type": [ + "string", + "null" + ] + } + }, + "position": { + "description": "Specifies the watermark position.", + "type": [ + "string", + "null" + ], + "enum": [ + null, + "Top", + "Bottom", + "TopLeft", + "TopRight", + "BottomLeft", + "BottomRight" + ] + }, + "text": { + "description": "Specifies the text for the watermark. For example you may wish to have a banner such as 'Dev' or 'Stage' to signify which environment the app was built for. Other scenarios could include 'Free', 'Pro', 'Lite', etc.", + "type": [ + "string", + "null" + ] + }, + "textColor": { + "description": "The hex or name of the color to use for the text.", + "type": [ + "string", + "null" + ] + }, + "fontFamily": { + "description": "The Font Family name if you want to use a custom font.", + "type": [ + "string", + "null" + ] + }, + "fontFile": { + "description": "Have a custom font you want to use, you can provide the file path to the Font File.", + "type": [ + "string", + "null" + ] + }, + "opacity": { + "description": "The opacity to use for the Watermark or Banner.", + "type": [ + "number", + "null" + ] } } } }, "type": "object", "properties": { - "watermarkFile": { + "$schema": { "type": [ "string", "null" @@ -162,72 +786,101 @@ "$ref": "#/definitions/PlatformConfiguration" }, "apple": { - "$ref": "#/definitions/PlatformConfiguration" + "$ref": "#/definitions/PlatformConfiguration-1" }, "ios": { - "$ref": "#/definitions/PlatformConfiguration" + "$ref": "#/definitions/PlatformConfiguration-2" }, "tvos": { - "$ref": "#/definitions/PlatformConfiguration" + "$ref": "#/definitions/PlatformConfiguration-3" }, "macos": { - "$ref": "#/definitions/PlatformConfiguration" + "$ref": "#/definitions/PlatformConfiguration-4" }, "tizen": { - "$ref": "#/definitions/PlatformConfiguration" + "$ref": "#/definitions/PlatformConfiguration-5" }, "uwp": { - "$ref": "#/definitions/PlatformConfiguration" + "$ref": "#/definitions/PlatformConfiguration-6" + }, + "additionalOutputs": { + "description": "Provides additional resource outputs for the specified source image.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/BaseImageConfiguration" + } }, "ignore": { - "type": [ "boolean", "null" ], + "description": "Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.", + "type": "boolean", "default": false }, "name": { - "type": [ "string", "null" ], - "description": "Defines the name of the output image or iconset without an extension" + "description": "The name of the output image. This will default to the source image name.", + "type": [ + "string", + "null" + ] }, "scale": { - "type": [ "number", "null" ], - "default": 1, - "description": "Sets the scale from size of the source image to the largest output image" + "description": "The scale factor to scale the largest output image based on the size of the input image.", + "type": "number", + "default": 1.0 }, "backgroundColor": { - "type": [ "string", "null" ], - "default": "#FFFFFF", - "description": "Sets a background color to use for a transparent image if desired" + "description": "A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.", + "type": [ + "string", + "null" + ] }, "width": { - "type": [ "integer", "null" ], - "description": "Sets the desired width of the largest output image" + "description": "A specific override for the width of the largest output image.", + "type": [ + "integer", + "null" + ] }, "height": { - "type": [ "integer", "null" ], - "description": "Sets the desired height of the largest output image" + "description": "A specific override for the height of the largest output image.", + "type": [ + "integer", + "null" + ] }, "padFactor": { - "type": [ "number", "null" ], - "description": "If set will change the largest image output size by changing the canvas size by the set factor. This should be greater than 1, like 1.5." + "description": "The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.", + "type": [ + "number", + "null" + ] }, "padColor": { - "type": [ "string", "null" ], - "description": "If set this will set a color to use on the background when adding a pad to your image" - }, - "resourceType": { - "type": [ "string", "null" ], - "enum": [ "Default", "Drawable", "Mipmap", "AllSquareTiles", "SquareTile", "SmallTile", "WideTile", "SplashScreen" ], - "description": "This is used by Android and UWP. This will be Drawable by default on Android. On UWP this will by default generate a standard image asset, or you can specify other app resource types." + "description": "This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.", + "type": [ + "string", + "null" + ] }, "watermark": { - "$ref": "#/definitions/WatermarkConfiguration", - "description": "A watermark to overlay on the output image" + "$ref": "#/definitions/WatermarkConfiguration" }, - "additionalOutputs": { - "type": [ "array", "null" ], - "items": { - "$ref": "#/definitions/ImageConfiguration" - }, - "description": "Optional additional copies of the input image" + "resourceType": { + "description": "Specifies the output resource type for the image. This typically should be used within a platform specific configuration.", + "type": "string", + "enum": [ + "Default", + "Drawable", + "Mipmap", + "AllSquareTiles", + "SquareTile", + "SmallTile", + "SplashScreen", + "WideTile" + ] } } -} +} \ No newline at end of file diff --git a/tools/Mobile.BuildTools.SchemaGenerator/Mobile.BuildTools.SchemaGenerator.csproj b/tools/Mobile.BuildTools.SchemaGenerator/Mobile.BuildTools.SchemaGenerator.csproj new file mode 100644 index 00000000..464c45a0 --- /dev/null +++ b/tools/Mobile.BuildTools.SchemaGenerator/Mobile.BuildTools.SchemaGenerator.csproj @@ -0,0 +1,16 @@ + + + + Exe + net5.0 + false + + + + + + + + + + diff --git a/tools/Mobile.BuildTools.SchemaGenerator/Program.cs b/tools/Mobile.BuildTools.SchemaGenerator/Program.cs new file mode 100644 index 00000000..54b86aca --- /dev/null +++ b/tools/Mobile.BuildTools.SchemaGenerator/Program.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.IO; +using McMaster.Extensions.CommandLineUtils; +using Mobile.BuildTools.Models; +using Mobile.BuildTools.Models.AppIcons; +using Newtonsoft.Json.Schema.Generation; + +namespace Mobile.BuildTools.SchemaGenerator +{ + public class Program + { + public static int Main(string[] args) => + CommandLineApplication.Execute(args); + + [Required] + [Option(Description = "Output Directory")] + public string OutputDirectory { get; set; } + + private int OnExecute() + { + if (!Directory.Exists(OutputDirectory)) + { + Console.Error.WriteLine($"Output directory does not exist: {OutputDirectory}"); + return 1; + } + + GenerateSchema(typeof(BuildToolsConfig), "buildtools.schema.json"); + GenerateSchema(typeof(ResourceDefinition), "resourceDefinition.schema.json"); + + return 0; + } + + private void GenerateSchema(Type type, string fileName) + { + Console.WriteLine($"Generating schema for {type.Name} - {fileName}"); + var schemaGenerator = new JSchemaGenerator + { + DefaultRequired = Newtonsoft.Json.Required.AllowNull, + }; + schemaGenerator.GenerationProviders.Add(new StringEnumGenerationProvider()); + + var schema = schemaGenerator.Generate(type); + File.WriteAllText(Path.Combine(OutputDirectory, fileName), schema.ToString()); + } + } +} From 4578952e1aea39a0ca98d669f0a612913b09dca9 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sun, 4 Jul 2021 07:40:13 -0700 Subject: [PATCH 07/17] add support for appsettings.json --- src/Mobile.BuildTools.Reference/Constants.cs | 4 ++ .../Utils/EnvironmentAnalyzer.cs | 58 ++++++++++++++++--- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/Mobile.BuildTools.Reference/Constants.cs b/src/Mobile.BuildTools.Reference/Constants.cs index 89c90ad0..b52d3a1b 100644 --- a/src/Mobile.BuildTools.Reference/Constants.cs +++ b/src/Mobile.BuildTools.Reference/Constants.cs @@ -2,6 +2,10 @@ { public static class Constants { + public const string AppSettingsJsonFileName = "appsettings.json"; + + public const string AppSettingsJsonConfigurationFileFormat = "appsettings.{0}.json"; + public const string SecretsJsonFileName = "secrets.json"; public const string SecretsJsonConfigurationFileFormat = "secrets.{0}.json"; diff --git a/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs b/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs index eb87eff6..3f1bc918 100644 --- a/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs +++ b/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,21 +18,45 @@ public static class EnvironmentAnalyzer public static IDictionary GatherEnvironmentVariables(IBuildConfiguration buildConfiguration = null, bool includeManifest = false) { var env = new Dictionary(); - foreach(var key in Environment.GetEnvironmentVariables().Keys) + if(buildConfiguration is null) { - env.Add(key.ToString(), Environment.GetEnvironmentVariable(key.ToString())); + foreach (var key in Environment.GetEnvironmentVariables().Keys) + { + env[key.ToString()] = Environment.GetEnvironmentVariable(key.ToString()); + } + + return env; } - if (buildConfiguration is null) - return env; + LoadConfigurationEnvironment(buildConfiguration, ref env); + + foreach(var key in Environment.GetEnvironmentVariables().Keys) + { + env[key.ToString()] = Environment.GetEnvironmentVariable(key.ToString()); + } var projectDirectory = buildConfiguration.ProjectDirectory; var solutionDirectory = buildConfiguration.SolutionDirectory; var configuration = buildConfiguration.BuildConfiguration; - LoadSecrets(Path.Combine(projectDirectory, Constants.SecretsJsonFileName), ref env); - LoadSecrets(Path.Combine(projectDirectory, string.Format(Constants.SecretsJsonConfigurationFileFormat, configuration)), ref env); - LoadSecrets(Path.Combine(solutionDirectory, Constants.SecretsJsonFileName), ref env); - LoadSecrets(Path.Combine(solutionDirectory, string.Format(Constants.SecretsJsonConfigurationFileFormat, configuration)), ref env); + new[] + { + Path.Combine(projectDirectory, Constants.AppSettingsJsonFileName), + Path.Combine(projectDirectory, string.Format(Constants.AppSettingsJsonConfigurationFileFormat, configuration)), + Path.Combine(solutionDirectory, Constants.AppSettingsJsonFileName), + Path.Combine(solutionDirectory, string.Format(Constants.AppSettingsJsonConfigurationFileFormat, configuration)), + // Legacy Support + Path.Combine(projectDirectory, Constants.SecretsJsonFileName), + Path.Combine(projectDirectory, string.Format(Constants.SecretsJsonConfigurationFileFormat, configuration)), + Path.Combine(solutionDirectory, Constants.SecretsJsonFileName), + Path.Combine(solutionDirectory, string.Format(Constants.SecretsJsonConfigurationFileFormat, configuration)) + }.Distinct() + .ForEach(x => + { + if (Path.GetFileName(x).StartsWith("secrets") && File.Exists(x)) + buildConfiguration.Logger.LogWarning("The secrets.json has been deprecated and will no longer be supported in a future version. Please migrate to appsettings.json"); + + LoadSecrets(x, ref env); + }); if (includeManifest) { @@ -55,6 +80,23 @@ public static IDictionary GatherEnvironmentVariables(IBuildConfi return env; } + private static void LoadConfigurationEnvironment(IBuildConfiguration buildConfiguration, ref Dictionary env) + { + var config = buildConfiguration.Configuration; + foreach ((var key, var value) in config.Environment.Defaults) + { + env[key] = value; + } + + if(config.Environment.Configuration.ContainsKey(buildConfiguration.BuildConfiguration)) + { + foreach ((var key, var value) in config.Environment.Configuration[buildConfiguration.BuildConfiguration]) + { + env[key] = value; + } + } + } + internal static void UpdateVariables(IDictionary settings, ref Dictionary output) { if (settings is null || settings.Count < 1) From 36ce56f9677386499786c0e63f18ac1abfec3fb4 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sun, 4 Jul 2021 07:43:27 -0700 Subject: [PATCH 08/17] fixing serialization tests --- .../Models/AppConfig.cs | 2 +- .../Models/AppIcons/AppleIconSet.cs | 2 +- .../Models/AppIcons/AppleIconSetImage.cs | 8 +- .../Models/AppIcons/BaseImageConfiguration.cs | 20 +- .../Models/AppIcons/PlatformConfiguration.cs | 2 +- .../Models/AppIcons/ResourceDefinition.cs | 14 +- .../Models/AppIcons/WatermarkConfiguration.cs | 16 +- .../Models/AutomaticVersioning.cs | 6 +- .../Models/BuildToolsConfig.cs | 24 +- .../Models/EnvironmentSettings.cs | 4 +- .../Models/GoogleConfig.cs | 4 +- .../Models/ImageResize.cs | 4 +- .../Models/ReleaseNotesOptions.cs | 10 +- .../Models/Secrets/SecretsConfig.cs | 16 +- .../Models/Settings/SettingsConfig.cs | 12 +- .../Models/Settings/ValueConfig.cs | 8 +- .../Models/TemplatedManifest.cs | 6 +- .../Models/ToolItem.cs | 2 +- .../Models/XamarinCss.cs | 4 +- .../Secrets/SecretsClassGenerator.cs | 120 ++-------- .../Tasks/SecretsJsonTask.cs | 30 +-- .../SecretsClassGeneratorFixture.cs | 220 +++++++++--------- 22 files changed, 232 insertions(+), 302 deletions(-) diff --git a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs index 0e9dfa62..8e93b674 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs @@ -8,7 +8,7 @@ public class AppConfig : ToolItem { [Description("Configures the bundling strategy for advanced scenarios. By default it will only transform the app.config with the app.{BuildConfiguration}.config. You may optionally bundle all app.*.config or those that do not include Debug, Release, Store.")] [DefaultValue(AppConfigStrategy.TransformOnly)] - [JsonProperty("strategy", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("strategy", NullValueHandling = NullValueHandling.Ignore)] public AppConfigStrategy Strategy { get; set; } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs index b0d702fc..3978e2f9 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSet.cs @@ -5,7 +5,7 @@ namespace Mobile.BuildTools.Models.AppIcons { public class AppleIconSet { - [JsonProperty("images", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Include)] + [JsonProperty("images", NullValueHandling = NullValueHandling.Include)] public List Images { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs index ad9d957a..7782ece4 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/AppleIconSetImage.cs @@ -4,16 +4,16 @@ namespace Mobile.BuildTools.Models.AppIcons { public class AppleIconSetImage { - [JsonProperty("scale", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("scale", NullValueHandling = NullValueHandling.Ignore)] public string Scale { get; set; } - [JsonProperty("size", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)] public string Size { get; set; } - [JsonProperty("idiom", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("idiom", NullValueHandling = NullValueHandling.Ignore)] public string Idiom { get; set; } - [JsonProperty("filename", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)] public string FileName { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs index 6a1d0ba8..30fe32ad 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/BaseImageConfiguration.cs @@ -7,44 +7,44 @@ public class BaseImageConfiguration : IImageResource { [DefaultValue(false)] [Description("Will ignore this image asset. This may be used to ignore an image for a specific platform or in general if using the image as a watermark for other images.")] - [JsonProperty("ignore", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("ignore", DefaultValueHandling = DefaultValueHandling.Ignore)] public bool Ignore { get; set; } [Description("The name of the output image. This will default to the source image name.")] - [JsonProperty("name", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } [DefaultValue(1.0)] [Description("The scale factor to scale the largest output image based on the size of the input image.")] - [JsonProperty("scale", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("scale", DefaultValueHandling = DefaultValueHandling.Ignore)] public double Scale { get; set; } [Description("A color that should be used to apply as a background for a transparent source image. This is particularly useful for app icons on iOS. By default the AppIcon on iOS will get a White background if the source is transparent and no value is specified here. This can be a hex value or name of a standard color.")] - [JsonProperty("backgroundColor", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("backgroundColor", DefaultValueHandling = DefaultValueHandling.Ignore)] public string BackgroundColor { get; set; } [Description("A specific override for the width of the largest output image.")] - [JsonProperty("width", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("width", DefaultValueHandling = DefaultValueHandling.Ignore)] public int? Width { get; set; } [Description("A specific override for the height of the largest output image.")] - [JsonProperty("height", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("height", DefaultValueHandling = DefaultValueHandling.Ignore)] public int? Height { get; set; } [Description("The padding factor will resize the canvas size by the specified factor. For example a factor of 1.5 will increase the overall size of the canvas. This will then be resized back to the original source image size and scaled appropriately. This is useful for Round Icons on Android.")] - [JsonProperty("padFactor", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("padFactor", DefaultValueHandling = DefaultValueHandling.Ignore)] public double? PaddingFactor { get; set; } [Description("This will set the background color for the padded area to the specified color. This can be a hex value or name of standard colors.")] - [JsonProperty("padColor", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("padColor", DefaultValueHandling = DefaultValueHandling.Ignore)] public string PaddingColor { get; set; } [Description("Specifies a configuration for adding a watermark or banner on a specified image.")] - [JsonProperty("watermark", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("watermark", DefaultValueHandling = DefaultValueHandling.Ignore)] public WatermarkConfiguration Watermark { get; set; } [Description("Specifies the output resource type for the image. This typically should be used within a platform specific configuration.")] - [JsonProperty("resourceType", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("resourceType", DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformResourceType ResourceType { get; set; } [JsonIgnore] diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs index 29fa67cb..25091f44 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/PlatformConfiguration.cs @@ -7,7 +7,7 @@ namespace Mobile.BuildTools.Models.AppIcons public class PlatformConfiguration : BaseImageConfiguration { [Description("Provides additional resource outputs for the specified source image.")] - [JsonProperty("additionalOutputs", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("additionalOutputs", DefaultValueHandling = DefaultValueHandling.Ignore)] public List AdditionalOutputs { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs index 151223b0..056ba2f0 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/ResourceDefinition.cs @@ -19,32 +19,32 @@ public string Schema } [Description("Specifies the Android specific configuration for the source image.")] - [JsonProperty("android", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("android", DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration Android { get; set; } [Description("Specifies the configuration for all Apple targets if building for iOS, macOS, TVOS, WatchOS.")] - [JsonProperty("apple", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("apple", DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration Apple { get; set; } [Description("Specifies the iOS specific configuration for the source image.")] - [JsonProperty("ios", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("ios", DefaultValueHandling = DefaultValueHandling.Ignore)] [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Properly named iOS")] public PlatformConfiguration iOS { get; set; } [Description("Specifies the TVOS specific configuration for the source image.")] - [JsonProperty("tvos", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("tvos", DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration TVOS { get; set; } [Description("Specifies the macOS specific configuration for the source image.")] - [JsonProperty("macos", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("macos", DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration MacOS { get; set; } [Description("Specifies the Tizen specific configuration for the source image.")] - [JsonProperty("tizen", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("tizen", DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration Tizen { get; set; } [Description("Specifies the UWP specific configuration for the source image.")] - [JsonProperty("uwp", Required = Required.AllowNull, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("uwp", DefaultValueHandling = DefaultValueHandling.Ignore)] public PlatformConfiguration UWP { get; set; } public IEnumerable GetConfigurations(Platform platform) diff --git a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs index 1d632ca2..a695d35f 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppIcons/WatermarkConfiguration.cs @@ -7,35 +7,35 @@ namespace Mobile.BuildTools.Models.AppIcons public class WatermarkConfiguration { [Description("Specifies the watermark source file name.")] - [JsonProperty("sourceFile", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("sourceFile", NullValueHandling = NullValueHandling.Ignore)] public string SourceFile { get; set; } [Description("Specifies the color or colors (for gradients) that should be used for the automatically generated Banner style watermark.")] - [JsonProperty("colors", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("colors", NullValueHandling = NullValueHandling.Ignore)] public IEnumerable Colors { get; set; } [Description("Specifies the watermark position.")] - [JsonProperty("position", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)] public WatermarkPosition? Position { get; set; } [Description("Specifies the text for the watermark. For example you may wish to have a banner such as 'Dev' or 'Stage' to signify which environment the app was built for. Other scenarios could include 'Free', 'Pro', 'Lite', etc.")] - [JsonProperty("text", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("text", NullValueHandling = NullValueHandling.Ignore)] public string Text { get; set; } [Description("The hex or name of the color to use for the text.")] - [JsonProperty("textColor", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("textColor", NullValueHandling = NullValueHandling.Ignore)] public string TextColor { get; set; } [Description("The Font Family name if you want to use a custom font.")] - [JsonProperty("fontFamily", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("fontFamily", NullValueHandling = NullValueHandling.Ignore)] public string FontFamily { get; set; } [Description("Have a custom font you want to use, you can provide the file path to the Font File.")] - [JsonProperty("fontFile", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("fontFile", NullValueHandling = NullValueHandling.Ignore)] public string FontFile { get; set; } [Description("The opacity to use for the Watermark or Banner.")] - [JsonProperty("opacity", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("opacity", NullValueHandling = NullValueHandling.Ignore)] public double? Opacity { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs index 867c55e6..c78f9042 100644 --- a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs +++ b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs @@ -7,16 +7,16 @@ public class AutomaticVersioning : ToolItem { [DefaultValue(VersionBehavior.PreferBuildNumber)] [Description("Sets the default behavior for versioning the app. By default the Mobile.BuildTools will attempt to use a Build number and will fallback to a timestamp.")] - [JsonProperty("behavior", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("behavior", NullValueHandling = NullValueHandling.Ignore)] public VersionBehavior Behavior { get; set; } [DefaultValue(VersionEnvironment.BuildHost)] [Description("Sets the default versioning environment. You can use this locally only, on build hosts only, or everywhere for a unique build number every time.")] - [JsonProperty("environment", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("environment", NullValueHandling = NullValueHandling.Ignore)] public VersionEnvironment Environment { get; set; } [Description("If you need to offset from your build number, you may want to set the Version Offset to get a version that will work for you.")] - [JsonProperty("versionOffset", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("versionOffset", NullValueHandling = NullValueHandling.Ignore)] public int VersionOffset { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs index 034995cc..5bdc6311 100644 --- a/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/BuildToolsConfig.cs @@ -19,52 +19,52 @@ public string Schema } [Description("Configures the settings for bundling and compiling the app.config for use with the Mobile.BuildTools.Configuration package.")] - [JsonProperty("appConfig", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("appConfig", NullValueHandling = NullValueHandling.Ignore)] public AppConfig AppConfig { get; set; } [Description("Configures the Mobile.BuildTools to copy the generated APK/AAB or IPA & dSYM to the root directory making it easier to locate and stage the build artifacts.")] - [JsonProperty("artifactCopy", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("artifactCopy", NullValueHandling = NullValueHandling.Ignore)] public ArtifactCopy ArtifactCopy { get; set; } [Description("Configures the Mobile.BuildTools to automatically version the build for Android and iOS targets.")] - [JsonProperty("automaticVersioning", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("automaticVersioning", NullValueHandling = NullValueHandling.Ignore)] public AutomaticVersioning AutomaticVersioning { get; set; } [Description("Configures the Mobile.BuildTools to compile SCSS files into Xamarin.Forms compliant CSS for styling your Xamarin.Forms application with CSS.")] - [JsonProperty("css", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("css", NullValueHandling = NullValueHandling.Ignore)] public XamarinCss Css { get; set; } [Description("Configures the Mobile.BuildTools to intelligently process image sources to be bundled into your Android and iOS application.")] - [JsonProperty("images", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("images", NullValueHandling = NullValueHandling.Ignore)] public ImageResize Images { get; set; } [Description("Configures the Mobile.BuildTools to process Tokens within the AndroidManifest.xml and Info.plist, replacing values like $AppName$ with a variable named AppName.")] - [JsonProperty("manifests", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("manifests", NullValueHandling = NullValueHandling.Ignore)] public TemplatedManifest Manifests { get; set; } [Description("Configures the Mobile.BuildTools to generate Release Notes for your build, based on the Git commit messages.")] - [JsonProperty("releaseNotes", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("releaseNotes", NullValueHandling = NullValueHandling.Ignore)] public ReleaseNotesOptions ReleaseNotes { get; set; } [Obsolete] [Description("Note: This is obsolete, please use `appSettings`.")] - [JsonProperty("projectSecrets", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("projectSecrets", NullValueHandling = NullValueHandling.Ignore)] public Dictionary ProjectSecrets { get; set; } [Description("Replaces the former 'Secrets' API, with a newly generated AppSettings class. This will allow you to generate one or more configuration classes.")] - [JsonProperty("appSettings", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("appSettings", NullValueHandling = NullValueHandling.Ignore)] public Dictionary> AppSettings { get; set; } [Description("Configures the Mobile.BuildTools with default non-sensitive environment values. If the value does not exist in the System Environment, this value will be used.")] - [JsonProperty("environment", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("environment", NullValueHandling = NullValueHandling.Ignore)] public EnvironmentSettings Environment { get; set; } [Description("Configures the Mobile.BuildTools to automatically generate and include the google-services.json or GoogleService-Info.plist from an Environment variable. This can be either a raw string value or file location if using Secure Files.")] - [JsonProperty("google", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("google", NullValueHandling = NullValueHandling.Ignore)] public GoogleConfig Google { get; set; } [Description("Having issues with the Mobile.BuildTools. Enable the Debug property to help you get some additional debug output in the build logs to help identify configuration issues.")] - [JsonProperty("debug", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("debug", NullValueHandling = NullValueHandling.Ignore)] public bool Debug { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs b/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs index 5a1fa9b5..e1091759 100644 --- a/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs +++ b/src/Mobile.BuildTools.Reference/Models/EnvironmentSettings.cs @@ -11,10 +11,10 @@ public EnvironmentSettings() Configuration = new Dictionary>(); } - [JsonProperty("defaults", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("defaults", NullValueHandling = NullValueHandling.Ignore)] public Dictionary Defaults { get; set; } - [JsonProperty("configuration", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("configuration", NullValueHandling = NullValueHandling.Ignore)] public Dictionary> Configuration { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs b/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs index 187b74a4..0eb87efa 100644 --- a/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/GoogleConfig.cs @@ -4,10 +4,10 @@ namespace Mobile.BuildTools.Models { public class GoogleConfig { - [JsonProperty("servicesJson", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("servicesJson", NullValueHandling = NullValueHandling.Ignore)] public string ServicesJson { get; set; } - [JsonProperty("infoPlist", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("infoPlist", NullValueHandling = NullValueHandling.Ignore)] public string InfoPlist { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ImageResize.cs b/src/Mobile.BuildTools.Reference/Models/ImageResize.cs index 33bb801a..9642c732 100644 --- a/src/Mobile.BuildTools.Reference/Models/ImageResize.cs +++ b/src/Mobile.BuildTools.Reference/Models/ImageResize.cs @@ -12,10 +12,10 @@ public ImageResize() ConditionalDirectories = new Dictionary>(); } - [JsonProperty("directories", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("directories", NullValueHandling = NullValueHandling.Ignore)] public List Directories { get; set; } - [JsonProperty("conditionalDirectories", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("conditionalDirectories", NullValueHandling = NullValueHandling.Ignore)] public Dictionary> ConditionalDirectories { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs b/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs index e6b5f170..66cfd82f 100644 --- a/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs +++ b/src/Mobile.BuildTools.Reference/Models/ReleaseNotesOptions.cs @@ -7,26 +7,26 @@ public class ReleaseNotesOptions : ToolItem { [DefaultValue(10)] [Description("The number of days back to look when generating the Release Notes")] - [JsonProperty("maxDays", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("maxDays", NullValueHandling = NullValueHandling.Ignore)] public int MaxDays { get; set; } [DefaultValue(10)] [Description("The maximum number of commits to lookup")] - [JsonProperty("maxCommit", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("maxCommit", NullValueHandling = NullValueHandling.Ignore)] public int MaxCommit { get; set; } [DefaultValue(250)] [Description("The maximum character limit for generated Release Notes")] - [JsonProperty("characterLimit", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("characterLimit", NullValueHandling = NullValueHandling.Ignore)] public int CharacterLimit { get; set; } [DefaultValue("releasenotes.txt")] [Description("The output filename such as 'ReleaseNotes.md' or 'ReleaseNotes.txt'")] - [JsonProperty("filename", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)] public string FileName { get; set; } [DefaultValue(true)] - [JsonProperty("createInRoot", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("createInRoot", NullValueHandling = NullValueHandling.Ignore)] public bool CreateInRoot { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs index 6389105f..50dff5c2 100644 --- a/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Secrets/SecretsConfig.cs @@ -12,28 +12,28 @@ public SecretsConfig() Properties = new List(); } - [JsonProperty("disable", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("disable", NullValueHandling = NullValueHandling.Ignore)] public bool Disable { get; set; } - [JsonProperty("delimiter", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("delimiter", NullValueHandling = NullValueHandling.Ignore)] public string Delimiter { get; set; } - [JsonProperty("prefix", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("prefix", NullValueHandling = NullValueHandling.Ignore)] public string Prefix { get; set; } - [JsonProperty("className", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("className", NullValueHandling = NullValueHandling.Ignore)] public string ClassName { get; set; } - [JsonProperty("accessibility", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("accessibility", NullValueHandling = NullValueHandling.Ignore)] public Accessibility Accessibility { get; set; } - [JsonProperty("rootNamespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("rootNamespace", NullValueHandling = NullValueHandling.Ignore)] public string RootNamespace { get; set; } - [JsonProperty("namespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("namespace", NullValueHandling = NullValueHandling.Ignore)] public string Namespace { get; set; } - [JsonProperty("properties", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("properties", NullValueHandling = NullValueHandling.Ignore)] public List Properties { get; set; } public bool ContainsKey(string key) => Properties != null && Properties.Any(x => x.Name == key); diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs index 2cce195b..54df252c 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/SettingsConfig.cs @@ -14,31 +14,31 @@ public SettingsConfig() [DefaultValue(";")] [Description("The delimiter used for arrays. By default this will use a semi-colon.")] - [JsonProperty("delimiter", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("delimiter", NullValueHandling = NullValueHandling.Ignore)] public string Delimiter { get; set; } [DefaultValue("BuildTools_")] [Description("The prefix the Mobile.BuildTools should use to look for variables. Note if a variable exists with the exact name it will be used if one does not exist with the prefix.")] - [JsonProperty("prefix", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("prefix", NullValueHandling = NullValueHandling.Ignore)] public string Prefix { get; set; } [DefaultValue("AppSettings")] [Description("The name of the generated class.")] - [JsonProperty("className", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("className", NullValueHandling = NullValueHandling.Ignore)] public string ClassName { get; set; } [DefaultValue(Accessibility.Internal)] [Description("The default visibility of the generated class, either 'public' or 'internal'.")] - [JsonProperty("accessibility", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("accessibility", NullValueHandling = NullValueHandling.Ignore)] public Accessibility Accessibility { get; set; } [Description("If using a Shared project as is typically the case with an Uno Platform app, be sure to specify the Root Namespace to use as this will change otherwise based on which platform target you are compiling.")] - [JsonProperty("rootNamespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("rootNamespace", NullValueHandling = NullValueHandling.Ignore)] public string RootNamespace { get; set; } [DefaultValue("Helpers")] [Description("The partial relative namespace to generate. By default this will be the Helpers namespace, you may set it to the root namespace by providing a period '.' for the value.")] - [JsonProperty("namespace", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("namespace", NullValueHandling = NullValueHandling.Ignore)] public string Namespace { get; set; } [Description("The properties that should be generated in the generated AppSettings class.")] diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs index 37613f3b..578f9157 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs @@ -5,16 +5,16 @@ namespace Mobile.BuildTools.Models.Settings { public class ValueConfig { - [JsonProperty("name", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } - [JsonProperty("type", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public PropertyType PropertyType { get; set; } - [JsonProperty("isArray", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("isArray", NullValueHandling = NullValueHandling.Ignore)] public bool IsArray { get; set; } - [JsonProperty("defaultValue", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("defaultValue", NullValueHandling = NullValueHandling.Ignore)] public string DefaultValue { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs b/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs index 19122e99..41cfd164 100644 --- a/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs +++ b/src/Mobile.BuildTools.Reference/Models/TemplatedManifest.cs @@ -7,16 +7,16 @@ public class TemplatedManifest : ToolItem { [Description("The Regex escaped value of the Token. '$$' is used by default to look for token matching the pattern $TokenName$.")] [DefaultValue("$$")] - [JsonProperty("token", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)] public string Token { get; set; } [DefaultValue("Manifest_")] - [JsonProperty("variablePrefix", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("variablePrefix", NullValueHandling = NullValueHandling.Ignore)] public string VariablePrefix { get; set; } [Description("If set to true, this will generate a build time error if a token is found which does not have a value in the environment or secrets.json.")] [DefaultValue(false)] - [JsonProperty("missingTokensAsErrors", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("missingTokensAsErrors", NullValueHandling = NullValueHandling.Ignore)] public bool MissingTokensAsErrors { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/ToolItem.cs b/src/Mobile.BuildTools.Reference/Models/ToolItem.cs index 967dfaa2..224a1517 100644 --- a/src/Mobile.BuildTools.Reference/Models/ToolItem.cs +++ b/src/Mobile.BuildTools.Reference/Models/ToolItem.cs @@ -7,7 +7,7 @@ public abstract class ToolItem { [Description("Disables this Mobile.BuildTools Task")] [DefaultValue(false)] - [JsonProperty("disable", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("disable", NullValueHandling = NullValueHandling.Ignore)] public bool? Disable { get; set; } } } diff --git a/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs b/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs index 0c5ad0bb..786971ea 100644 --- a/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs +++ b/src/Mobile.BuildTools.Reference/Models/XamarinCss.cs @@ -6,11 +6,11 @@ namespace Mobile.BuildTools.Models public class XamarinCss : ToolItem { [DefaultValue(false)] - [JsonProperty("minify", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("minify", NullValueHandling = NullValueHandling.Ignore)] public bool Minify { get; set; } [DefaultValue(false)] - [JsonProperty("bundleScss", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("bundleScss", NullValueHandling = NullValueHandling.Ignore)] public bool BundleScss { get; set; } } } diff --git a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs index c306788c..33bd62b6 100644 --- a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs +++ b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs @@ -20,16 +20,13 @@ internal class SecretsClassGenerator : GeneratorBase> #pragma warning disable IDE1006, IDE0040 private string CompileGeneratedAttribute { get; } - private string[] SecretsFileLocations { get; } - - public SecretsClassGenerator(IBuildConfiguration buildConfiguration, params string[] secretsFileLocations) + public SecretsClassGenerator(IBuildConfiguration buildConfiguration) : base(buildConfiguration) { var assembly = typeof(SecretsClassGenerator).Assembly; var toolVersion = FileVersionInfo.GetVersionInfo(assembly.Location).ProductVersion; CompileGeneratedAttribute = @$"[GeneratedCodeAttribute(""Mobile.BuildTools.Generators.Secrets.SecretsClassGenerator"", ""{toolVersion}"")]"; - SecretsFileLocations = secretsFileLocations; Outputs = new List(); } #pragma warning restore IDE1006, IDE0040 @@ -47,33 +44,11 @@ protected override void ExecuteInternal() private void ProcessSettingsConfig(SettingsConfig settingsConfig) { - if (!SecretsFileLocations.Any(x => File.Exists(x))) - { - Log.LogMessage("No secrets.json was found in either the Project or Solution Directory."); - - if (settingsConfig.Properties.Any()) - { - var jObject = new JObject(); - foreach (var propConfig in settingsConfig.Properties) - { - jObject[propConfig.Name] = $"{propConfig.PropertyType}"; - Log.LogError($"Missing Secret parameter: {propConfig.Name}"); - } - - Log.LogWarning(@$"To fix the build please add a secrets.json in either the Project or Solution root directory."); - Log.LogMessage(jObject.ToString(Formatting.Indented)); - } - - return; - } - - var secrets = GetMergedSecrets(); - var replacement = string.Empty; var safeReplacement = string.Empty; if (string.IsNullOrEmpty(settingsConfig.ClassName)) - settingsConfig.ClassName = "Secrets"; + settingsConfig.ClassName = "AppSettings"; if (string.IsNullOrEmpty(settingsConfig.Namespace)) settingsConfig.Namespace = "Helpers"; @@ -84,25 +59,7 @@ private void ProcessSettingsConfig(SettingsConfig settingsConfig) if (string.IsNullOrEmpty(settingsConfig.Prefix)) settingsConfig.Prefix = "BuildTools_"; - foreach (var propConfig in settingsConfig.Properties) - { - if (string.IsNullOrEmpty(propConfig.DefaultValue)) - continue; - if (secrets.ContainsKey(propConfig.Name) || secrets.ContainsKey($"{settingsConfig.Prefix}{propConfig.Name}")) - continue; - - secrets.Add(propConfig.Name, propConfig.DefaultValue == "null" || propConfig.DefaultValue == "default" ? null : propConfig.DefaultValue); - } - - var hasErrors = false; - foreach(var propConfig in settingsConfig.Properties) - { - if (!secrets.ContainsKey(propConfig.Name) && !secrets.ContainsKey($"{settingsConfig.Prefix}{propConfig.Name}")) - { - hasErrors = true; - Log.LogError($"Missing Secret parameter: {propConfig.Name}"); - } - } + var secrets = GetMergedSecrets(settingsConfig, out var hasErrors); if (hasErrors) return; @@ -136,66 +93,31 @@ private void ProcessSettingsConfig(SettingsConfig settingsConfig) File.WriteAllText(intermediateFile, secretsClass); } - internal IDictionary GetMergedSecrets() + internal IDictionary GetMergedSecrets(SettingsConfig settingsConfig, out bool hasErrors) { - JObject jObject = null; - foreach (var file in SecretsFileLocations) - { - CreateOrMerge(file, ref jObject); - } - - if (jObject is null) + var env = EnvironmentAnalyzer.GatherEnvironmentVariables(Build); + var secrets = new Dictionary(); + hasErrors = false; + foreach(var prop in settingsConfig.Properties) { - throw new Exception("An unexpected error occurred. Could not locate any secrets."); - } - - var secrets = jObject.ToObject>(); - - if (Build?.Configuration?.Environment != null) - { - var settings = Build.Configuration.Environment; - var defaultSettings = settings.Defaults ?? new Dictionary(); - if (settings.Configuration != null && settings.Configuration.ContainsKey(Build.BuildConfiguration)) - { - foreach ((var key, var value) in settings.Configuration[Build.BuildConfiguration]) - defaultSettings[key] = value; - } - - UpdateVariables(defaultSettings, ref secrets); - } - - return secrets; - } - - private static void UpdateVariables(IDictionary settings, ref Dictionary output) - { - if (settings is null) - return; - - foreach ((var key, var value) in settings) - { - if (!output.ContainsKey(key)) - output[key] = value; - } - } - - internal void CreateOrMerge(string jsonFilePath, ref JObject secrets) - { - if(!string.IsNullOrEmpty(jsonFilePath) && File.Exists(jsonFilePath)) - { - var json = File.ReadAllText(jsonFilePath); - if(secrets is null) + var key = env.Keys.FirstOrDefault(x => x.Equals(prop.Name, StringComparison.InvariantCultureIgnoreCase) || x.Equals($"{settingsConfig.Prefix}{prop.Name}", StringComparison.InvariantCultureIgnoreCase)); + if(string.IsNullOrEmpty(key)) { - secrets = JObject.Parse(json); - } - else - { - foreach(var pair in secrets) + if(string.IsNullOrEmpty(prop.DefaultValue)) { - secrets[pair.Key] = pair.Value; + Log.LogError($"Missing Settings Key: {prop.Name}"); + hasErrors = true; + continue; } + + secrets[prop.Name] = prop.DefaultValue == "null" || prop.DefaultValue == "default" ? null : prop.DefaultValue; + continue; } + + secrets[prop.Name] = env[key]; } + + return env; } internal CodeWriter GenerateClass(SettingsConfig settingsConfig, IDictionary secrets) diff --git a/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs b/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs index c426c12b..1a104203 100644 --- a/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs +++ b/src/Mobile.BuildTools/Tasks/SecretsJsonTask.cs @@ -24,21 +24,21 @@ internal override void ExecuteInternal(IBuildConfiguration config) if (!config.Configuration.AppSettings.ContainsKey(ProjectName)) return; - var configJson = string.Format(Constants.SecretsJsonConfigurationFileFormat, config.BuildConfiguration.ToLower()); - var searchPaths = new[] - { - SolutionDirectory, - ProjectDirectory, - ConfigHelper.GetConfigurationPath(ProjectDirectory, SolutionDirectory) - } - .SelectMany(x => new[] { Path.Combine(x, Constants.SecretsJsonFileName), Path.Combine(x, configJson) }) - .Where(x => File.Exists(x)) - .ToList(); - - if (!string.IsNullOrEmpty(JsonSecretsFilePath) && !searchPaths.Contains(JsonSecretsFilePath)) - searchPaths.Add(JsonSecretsFilePath); - - var generator = new SecretsClassGenerator(config, searchPaths.ToArray()) + //var configJson = string.Format(Constants.SecretsJsonConfigurationFileFormat, config.BuildConfiguration.ToLower()); + //var searchPaths = new[] + //{ + // SolutionDirectory, + // ProjectDirectory, + // ConfigHelper.GetConfigurationPath(ProjectDirectory, SolutionDirectory) + //} + //.SelectMany(x => new[] { Path.Combine(x, Constants.SecretsJsonFileName), Path.Combine(x, configJson) }) + //.Where(x => File.Exists(x)) + //.ToList(); + + //if (!string.IsNullOrEmpty(JsonSecretsFilePath) && !searchPaths.Contains(JsonSecretsFilePath)) + // searchPaths.Add(JsonSecretsFilePath); + + var generator = new SecretsClassGenerator(config) { RootNamespace = RootNamespace, }; diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs index 7fd9e1b8..d7f64788 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs @@ -1,15 +1,15 @@ -using System; -using System.CodeDom.Compiler; -using System.Collections; +using System; +using System.CodeDom.Compiler; +using System.Collections; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using Microsoft.CSharp; -using Mobile.BuildTools.Build; -using Mobile.BuildTools.Generators.Secrets; -using Mobile.BuildTools.Models.Settings; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.CSharp; +using Mobile.BuildTools.Build; +using Mobile.BuildTools.Generators.Secrets; +using Mobile.BuildTools.Models.Settings; using Xunit; using Xunit.Abstractions; @@ -24,16 +24,16 @@ public SecretsClassGeneratorFixture(ITestOutputHelper testOutputHelper) { } - private SecretsClassGenerator GetGenerator(string testName = null) - { - if(string.IsNullOrEmpty(testName)) - { - var st = new StackTrace(); - testName = st.GetFrame(1).GetMethod().Name; - } - return GetGenerator(GetConfiguration(testName)); - } - + private SecretsClassGenerator GetGenerator(string testName = null) + { + if(string.IsNullOrEmpty(testName)) + { + var st = new StackTrace(); + testName = st.GetFrame(1).GetMethod().Name; + } + return GetGenerator(GetConfiguration(testName)); + } + private SecretsClassGenerator GetGenerator(IBuildConfiguration config) => new SecretsClassGenerator(config); @@ -45,9 +45,9 @@ public void ProcessSecretHidesValueForSafeOutput(bool firstRun) var value = "TestValue"; var pair = new KeyValuePair(TestKey, value); var generator = GetGenerator(); - var config = new SettingsConfig() - { - Properties = new List{ new ValueConfig { Name = TestKey, PropertyType = PropertyType.String } } + var config = new SettingsConfig() + { + Properties = new List{ new ValueConfig { Name = TestKey, PropertyType = PropertyType.String } } }; var output = generator.ProcessSecret(pair, config, firstRun, true); Assert.DoesNotContain(value, output); @@ -65,10 +65,10 @@ public void ProcessSecretGeneratesBooleanValue(Accessibility accessibility, bool var value = bool.TrueString; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SettingsConfig() + var config = new SettingsConfig() { - Accessibility = accessibility, - Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Bool } } + Accessibility = accessibility, + Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Bool } } }; var output = generator.ProcessSecret(pair, config, firstRun); @@ -86,10 +86,10 @@ public void ProcessSecretGeneratesIntegerValue(Accessibility accessibility, bool var value = "2"; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SettingsConfig() + var config = new SettingsConfig() { - Accessibility = accessibility, - Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Int } } + Accessibility = accessibility, + Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Int } } }; var output = generator.ProcessSecret(pair, config, firstRun); @@ -107,10 +107,10 @@ public void ProcessSecretGeneratesDoubleValue(Accessibility accessibility, bool var value = "2.2"; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SettingsConfig() + var config = new SettingsConfig() { - Accessibility = accessibility, - Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Double } } + Accessibility = accessibility, + Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.Double } } }; var output = generator.ProcessSecret(pair, config, firstRun); @@ -128,10 +128,10 @@ public void ProcessSecretGeneratesStringValue(Accessibility accessibility, bool var value = "TestValue"; var pair = new KeyValuePair(key, value); var generator = GetGenerator(); - var config = new SettingsConfig() + var config = new SettingsConfig() { - Accessibility = accessibility, - Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.String } } + Accessibility = accessibility, + Properties = new List { new ValueConfig { Name = TestKey, PropertyType = PropertyType.String } } }; var output = generator.ProcessSecret(pair, config, firstRun); @@ -198,17 +198,17 @@ public void GeneratesCorrectNamespace_WithSpecifiedBaseNamespace(string relative [InlineData(true, PropertyType.Uri, "static readonly System.Uri[]", "System.Array.Empty()")] public void HandleNullValue(bool isArray, PropertyType type, string typeDeclaration, string valueDeclaration) { - var pair = new KeyValuePair(TestKey, "null"); - var generator = GetGenerator(); - var valueConfig = new ValueConfig - { - Name = TestKey, - PropertyType = type, - IsArray = isArray - }; - var secretsConfig = new SettingsConfig { Properties = new List { valueConfig } }; - var output = generator.ProcessSecret(pair, secretsConfig, false); - Assert.Contains($"{typeDeclaration} {TestKey}", output); + var pair = new KeyValuePair(TestKey, "null"); + var generator = GetGenerator(); + var valueConfig = new ValueConfig + { + Name = TestKey, + PropertyType = type, + IsArray = isArray + }; + var secretsConfig = new SettingsConfig { Properties = new List { valueConfig } }; + var output = generator.ProcessSecret(pair, secretsConfig, false); + Assert.Contains($"{typeDeclaration} {TestKey}", output); Assert.Contains(valueDeclaration, output); } @@ -236,9 +236,9 @@ public void HandleNullValue(bool isArray, PropertyType type, string typeDeclarat // Double [InlineData(false, false, PropertyType.Double, "const double", "5.3", "5.3")] [InlineData(true, false, PropertyType.Double, "const double", "5.3", "5.3")] - [InlineData(false, true, PropertyType.Double, "static readonly double[]", "5.3;3.3", "new double[] { 5.3, 3.3 }")] - [InlineData(true, true, PropertyType.Double, "static readonly double[]", "5.3;3.3", "new double[] { 5.3, 3.3 }")] - // Float + [InlineData(false, true, PropertyType.Double, "static readonly double[]", "5.3;3.3", "new double[] { 5.3, 3.3 }")] + [InlineData(true, true, PropertyType.Double, "static readonly double[]", "5.3;3.3", "new double[] { 5.3, 3.3 }")] + // Float [InlineData(false, false, PropertyType.Float, "const float", "5.3F", "5.3F")] [InlineData(false, true, PropertyType.Float, "static readonly float[]", "5.3F;3.3F", "new float[] { 5.3F, 3.3F }")] // Guid @@ -247,7 +247,7 @@ public void HandleNullValue(bool isArray, PropertyType type, string typeDeclarat // Int [InlineData(false, false, PropertyType.Int, "const int", "6", "6")] [InlineData(false, false, PropertyType.Int, "const int", "6.0", "6")] - [InlineData(true, false, PropertyType.Int, "const int", "6", "6")] + [InlineData(true, false, PropertyType.Int, "const int", "6", "6")] [InlineData(true, false, PropertyType.Int, "const int", "6.0", "6")] [InlineData(false, true, PropertyType.Int, "static readonly int[]", "6;5", "new int[] { 6, 5 }")] [InlineData(true, true, PropertyType.Int, "static readonly int[]", "6;5", "new int[] { 6, 5 }")] @@ -272,11 +272,11 @@ public void ProcessSecretValue(bool firstRun, bool isArray, PropertyType type, s { var pair = new KeyValuePair(TestKey, value); var generator = GetGenerator(); - var valueConfig = new ValueConfig - { - Name = TestKey, - PropertyType = type, - IsArray = isArray + var valueConfig = new ValueConfig + { + Name = TestKey, + PropertyType = type, + IsArray = isArray }; var secretsConfig = new SettingsConfig { Properties = new List { valueConfig } }; var output = generator.ProcessSecret(pair, secretsConfig, firstRun); @@ -323,68 +323,76 @@ public void ProcessSecretValue(bool firstRun, bool isArray, PropertyType type, s [InlineData("uriArray.json", PropertyType.Uri, typeof(Uri[]), true)] [InlineData("ushort.json", PropertyType.UShort, typeof(ushort), false)] [InlineData("ushortArray.json", PropertyType.UShort, typeof(ushort[]), true)] - public void GeneratesValidClass(string secretsFile, PropertyType propertyType, Type expectedType, bool isArray) - { - var config = GetConfiguration($"{nameof(GeneratesValidClass)}-{expectedType.Name}"); + public void GeneratesValidClass(string secretsFile, PropertyType propertyType, Type expectedType, bool isArray) + { + var testDirectory = Path.Combine("Tests", nameof(SecretsClassGeneratorFixture), nameof(GeneratesValidClass), expectedType.Name); + if (Directory.Exists(testDirectory)) + Directory.Delete(testDirectory, true); + + Directory.CreateDirectory(testDirectory); + File.Copy(Path.Combine("Templates", "Secrets", secretsFile), Path.Combine(testDirectory, "secrets.json")); + + var config = GetConfiguration($"{nameof(GeneratesValidClass)}-{expectedType.Name}"); + config.SolutionDirectory = config.ProjectDirectory = testDirectory; config.SettingsConfig = new List{ new SettingsConfig { Properties = new List { - new ValueConfig - { - Name = "Prop", - PropertyType = propertyType, - IsArray = isArray + new ValueConfig + { + Name = "Prop", + PropertyType = propertyType, + IsArray = isArray } } } - }; - var generator = new SecretsClassGenerator(config, Path.Combine("Templates", "Secrets", secretsFile)) - { - RootNamespace = config.ProjectName - }; - generator.Execute(); + }; + var generator = new SecretsClassGenerator(config) + { + RootNamespace = config.ProjectName + }; + generator.Execute(); Assert.Single(generator.Outputs); - - var filePath = generator.Outputs.First().ItemSpec; - Assert.True(File.Exists(filePath)); - - var csc = new CSharpCodeProvider(); - var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.dll", "System.Core.dll" }, Path.Combine(config.IntermediateOutputPath, $"{Path.GetFileNameWithoutExtension(secretsFile)}.dll"), true); - var results = csc.CompileAssemblyFromFile(parameters, filePath); - results.Errors.Cast().ToList().ForEach(error => _testOutputHelper.WriteLine(error.ErrorText)); - - Assert.Empty(results.Errors); - Assert.NotNull(results.CompiledAssembly); - var secretsClassType = results.CompiledAssembly.DefinedTypes.FirstOrDefault(t => t.Name == config.SettingsConfig.First().ClassName); - Assert.NotNull(secretsClassType); - var propField = secretsClassType.GetField("Prop", BindingFlags.NonPublic | BindingFlags.Static); - Assert.NotNull(propField); - Assert.Equal(expectedType.FullName, propField.FieldType.FullName); - - object value = null; - var ex = Record.Exception(() => value = propField.GetValue(null)); - Assert.Null(ex); - Assert.NotNull(value); - - if(isArray) - { - var array = (IEnumerable)value; - Assert.NotNull(array); - Assert.Equal(3, array.Cast().Count()); - } + + var filePath = generator.Outputs.First().ItemSpec; + Assert.True(File.Exists(filePath)); + + var csc = new CSharpCodeProvider(); + var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.dll", "System.Core.dll" }, Path.Combine(config.IntermediateOutputPath, $"{Path.GetFileNameWithoutExtension(secretsFile)}.dll"), true); + var results = csc.CompileAssemblyFromFile(parameters, filePath); + results.Errors.Cast().ToList().ForEach(error => _testOutputHelper.WriteLine(error.ErrorText)); + + Assert.Empty(results.Errors); + Assert.NotNull(results.CompiledAssembly); + var secretsClassType = results.CompiledAssembly.DefinedTypes.FirstOrDefault(t => t.Name == config.SettingsConfig.First().ClassName); + Assert.NotNull(secretsClassType); + var propField = secretsClassType.GetField("Prop", BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(propField); + Assert.Equal(expectedType.FullName, propField.FieldType.FullName); + + object value = null; + var ex = Record.Exception(() => value = propField.GetValue(null)); + Assert.Null(ex); + Assert.NotNull(value); + + if(isArray) + { + var array = (IEnumerable)value; + Assert.NotNull(array); + Assert.Equal(3, array.Cast().Count()); + } } - public class SecretValue - { - public PropertyType Type { get; set; } - public string Value { get; set; } - public bool FirstRun { get; set; } - public bool IsArray { get; set; } - public string TypeDeclaration { get; set; } - public string ValueDeclaration { get; set; } + public class SecretValue + { + public PropertyType Type { get; set; } + public string Value { get; set; } + public bool FirstRun { get; set; } + public bool IsArray { get; set; } + public string TypeDeclaration { get; set; } + public string ValueDeclaration { get; set; } } } } From 0a8967e512b6472197e5c7154b31e371ae45e9a5 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sun, 4 Jul 2021 08:15:17 -0700 Subject: [PATCH 09/17] simplify dependencies --- .../Mobile.BuildTools.Reference.csproj | 1 - src/Mobile.BuildTools/Mobile.BuildTools.csproj | 9 --------- .../Mobile.BuildTools.Tests.csproj | 6 +++--- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj b/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj index 6a323192..f11cbf17 100644 --- a/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj +++ b/src/Mobile.BuildTools.Reference/Mobile.BuildTools.Reference.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Mobile.BuildTools/Mobile.BuildTools.csproj b/src/Mobile.BuildTools/Mobile.BuildTools.csproj index 469981aa..0282c9b3 100644 --- a/src/Mobile.BuildTools/Mobile.BuildTools.csproj +++ b/src/Mobile.BuildTools/Mobile.BuildTools.csproj @@ -29,26 +29,17 @@ - - - - - - - - - diff --git a/tests/Mobile.BuildTools.Tests/Mobile.BuildTools.Tests.csproj b/tests/Mobile.BuildTools.Tests/Mobile.BuildTools.Tests.csproj index 620950ae..fef03c98 100644 --- a/tests/Mobile.BuildTools.Tests/Mobile.BuildTools.Tests.csproj +++ b/tests/Mobile.BuildTools.Tests/Mobile.BuildTools.Tests.csproj @@ -13,7 +13,6 @@ - - - + + + From a5bb6b4b23a50716e72ce889cfbac88b2116a8e1 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Sun, 4 Jul 2021 20:02:20 -0700 Subject: [PATCH 10/17] adding config schema tests --- .../Models/AppConfig.cs | 2 + .../Tasks/LocateBuildToolsConfigTask.cs | 2 +- .../ConfigurationSchemaFixture.cs | 30 ++++++++++ .../Serialization/appconfig.test.json | 6 ++ .../Serialization/artifactcopy.test.json | 6 ++ .../Templates/Serialization/css.test.json | 6 ++ .../Templates/Serialization/e2e.sample.json | 60 +++++++++++++++++++ .../Templates/Serialization/google.test.json | 7 +++ 8 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 tests/Mobile.BuildTools.Tests/Fixtures/Configuration/ConfigurationSchemaFixture.cs create mode 100644 tests/Mobile.BuildTools.Tests/Templates/Serialization/appconfig.test.json create mode 100644 tests/Mobile.BuildTools.Tests/Templates/Serialization/artifactcopy.test.json create mode 100644 tests/Mobile.BuildTools.Tests/Templates/Serialization/css.test.json create mode 100644 tests/Mobile.BuildTools.Tests/Templates/Serialization/e2e.sample.json create mode 100644 tests/Mobile.BuildTools.Tests/Templates/Serialization/google.test.json diff --git a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs index 8e93b674..9a6137c0 100644 --- a/src/Mobile.BuildTools.Reference/Models/AppConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/AppConfig.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; +using Newtonsoft.Json.Schema.Generation; namespace Mobile.BuildTools.Models { @@ -12,6 +13,7 @@ public class AppConfig : ToolItem public AppConfigStrategy Strategy { get; set; } } + [JSchemaGenerationProvider(typeof(StringEnumGenerationProvider))] public enum AppConfigStrategy { TransformOnly, diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index 2adaad93..fe0ba6ca 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -97,7 +97,7 @@ internal void ValidateConfigSchema() var generator = new JSchemaGenerator(); var schema = generator.Generate(typeof(BuildToolsConfig)); var config = JObject.Parse(File.ReadAllText(BuildToolsConfigFilePath)); - if(!config.IsValid(schema, out IList errorMessages)) + if (!config.IsValid(schema, out IList errorMessages)) { foreach(var error in errorMessages) { diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Configuration/ConfigurationSchemaFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Configuration/ConfigurationSchemaFixture.cs new file mode 100644 index 00000000..3afd54c7 --- /dev/null +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Configuration/ConfigurationSchemaFixture.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.IO; +using Mobile.BuildTools.Models; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Newtonsoft.Json.Schema.Generation; +using Xunit; + +namespace Mobile.BuildTools.Tests.Fixtures.Configuration +{ + public class ConfigurationSchemaFixture + { + [Theory] + [InlineData("appconfig.test.json")] + [InlineData("artifactcopy.test.json")] + [InlineData("css.test.json")] + [InlineData("google.test.json")] + [InlineData("e2e.sample.json")] + public void ConfigurationHasValidSchema(string fileName) + { + var filePath = Path.Combine("Templates", "Serialization", fileName); + var generator = new JSchemaGenerator(); + var schema = generator.Generate(typeof(BuildToolsConfig)); + var config = JObject.Parse(File.ReadAllText(filePath)); + config.IsValid(schema, out IList errors); + + Assert.Empty(errors); + } + } +} diff --git a/tests/Mobile.BuildTools.Tests/Templates/Serialization/appconfig.test.json b/tests/Mobile.BuildTools.Tests/Templates/Serialization/appconfig.test.json new file mode 100644 index 00000000..ffbd9a59 --- /dev/null +++ b/tests/Mobile.BuildTools.Tests/Templates/Serialization/appconfig.test.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json", + "appConfig": { + "strategy": "BundleAll" + } +} diff --git a/tests/Mobile.BuildTools.Tests/Templates/Serialization/artifactcopy.test.json b/tests/Mobile.BuildTools.Tests/Templates/Serialization/artifactcopy.test.json new file mode 100644 index 00000000..c28a5471 --- /dev/null +++ b/tests/Mobile.BuildTools.Tests/Templates/Serialization/artifactcopy.test.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json", + "artifactCopy": { + "disable": true + } +} diff --git a/tests/Mobile.BuildTools.Tests/Templates/Serialization/css.test.json b/tests/Mobile.BuildTools.Tests/Templates/Serialization/css.test.json new file mode 100644 index 00000000..67a800a8 --- /dev/null +++ b/tests/Mobile.BuildTools.Tests/Templates/Serialization/css.test.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json", + "css": { + "disable": false + } +} diff --git a/tests/Mobile.BuildTools.Tests/Templates/Serialization/e2e.sample.json b/tests/Mobile.BuildTools.Tests/Templates/Serialization/e2e.sample.json new file mode 100644 index 00000000..68464741 --- /dev/null +++ b/tests/Mobile.BuildTools.Tests/Templates/Serialization/e2e.sample.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json", + "automaticVersioning": { + "disable": true + }, + "images": { + "disable": true + }, + "projectSecrets": { + "E2E.Tests": { + "prefix": "Test_", + "properties": [ + { + "name": "AString", + "type": "String" + }, + { + "name": "AnInt", + "type": "Int" + }, + { + "name": "ADouble", + "type": "Double" + }, + { + "name": "ABool", + "type": "Bool" + }, + { + "name": "AFloat", + "type": "Float" + }, + { + "name": "ADate", + "type": "DateTime" + }, + { + "name": "AStringArray", + "type": "String", + "isArray": true + }, + { + "name": "AUri", + "type": "Uri" + }, + { + "name": "AppCenterSecret", + "type": "String", + "defaultValue": "default" + }, + { + "name": "SomeDefaultBool", + "type": "Bool", + "defaultValue": "null" + } + ] + } + }, + "debug": true +} diff --git a/tests/Mobile.BuildTools.Tests/Templates/Serialization/google.test.json b/tests/Mobile.BuildTools.Tests/Templates/Serialization/google.test.json new file mode 100644 index 00000000..97f3d716 --- /dev/null +++ b/tests/Mobile.BuildTools.Tests/Templates/Serialization/google.test.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json", + "google": { + "servicesJson": "FooBar1", + "infoPlist": "FooBar2" + } +} From 3e9347e72d7e5c8b2f222573fde362a7126dc13e Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 5 Jul 2021 09:14:20 -0700 Subject: [PATCH 11/17] adding test cases for Secrets Generation --- .../Utils/EnvironmentAnalyzer.cs | 47 ++-- .../Secrets/SecretsClassGenerator.cs | 23 +- .../Fixtures/FixtureBase.cs | 2 +- .../SecretsClassGeneratorFixture.cs | 119 ++++++++++ .../Utils/EnvironmentAnalyzerFixture.cs | 209 ++++++++++++++++++ 5 files changed, 376 insertions(+), 24 deletions(-) create mode 100644 tests/Mobile.BuildTools.Tests/Fixtures/Utils/EnvironmentAnalyzerFixture.cs diff --git a/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs b/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs index 3f1bc918..19588b5f 100644 --- a/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs +++ b/src/Mobile.BuildTools.Reference/Utils/EnvironmentAnalyzer.cs @@ -28,27 +28,24 @@ public static IDictionary GatherEnvironmentVariables(IBuildConfi return env; } - LoadConfigurationEnvironment(buildConfiguration, ref env); - - foreach(var key in Environment.GetEnvironmentVariables().Keys) - { - env[key.ToString()] = Environment.GetEnvironmentVariable(key.ToString()); - } + env = GetEnvironmentVariables(buildConfiguration); var projectDirectory = buildConfiguration.ProjectDirectory; var solutionDirectory = buildConfiguration.SolutionDirectory; var configuration = buildConfiguration.BuildConfiguration; new[] { - Path.Combine(projectDirectory, Constants.AppSettingsJsonFileName), - Path.Combine(projectDirectory, string.Format(Constants.AppSettingsJsonConfigurationFileFormat, configuration)), - Path.Combine(solutionDirectory, Constants.AppSettingsJsonFileName), - Path.Combine(solutionDirectory, string.Format(Constants.AppSettingsJsonConfigurationFileFormat, configuration)), // Legacy Support Path.Combine(projectDirectory, Constants.SecretsJsonFileName), Path.Combine(projectDirectory, string.Format(Constants.SecretsJsonConfigurationFileFormat, configuration)), Path.Combine(solutionDirectory, Constants.SecretsJsonFileName), - Path.Combine(solutionDirectory, string.Format(Constants.SecretsJsonConfigurationFileFormat, configuration)) + Path.Combine(solutionDirectory, string.Format(Constants.SecretsJsonConfigurationFileFormat, configuration)), + // End Legacy Support + + Path.Combine(projectDirectory, Constants.AppSettingsJsonFileName), + Path.Combine(projectDirectory, string.Format(Constants.AppSettingsJsonConfigurationFileFormat, configuration)), + Path.Combine(solutionDirectory, Constants.AppSettingsJsonFileName), + Path.Combine(solutionDirectory, string.Format(Constants.AppSettingsJsonConfigurationFileFormat, configuration)), }.Distinct() .ForEach(x => { @@ -80,21 +77,32 @@ public static IDictionary GatherEnvironmentVariables(IBuildConfi return env; } - private static void LoadConfigurationEnvironment(IBuildConfiguration buildConfiguration, ref Dictionary env) + private static Dictionary GetEnvironmentVariables(IBuildConfiguration buildConfiguration) { - var config = buildConfiguration.Configuration; - foreach ((var key, var value) in config.Environment.Defaults) + var env = new Dictionary(); + foreach((var key, var value) in buildConfiguration.Configuration.Environment.Defaults) { env[key] = value; } - if(config.Environment.Configuration.ContainsKey(buildConfiguration.BuildConfiguration)) + if(buildConfiguration.Configuration.Environment.Configuration.ContainsKey(buildConfiguration.BuildConfiguration)) { - foreach ((var key, var value) in config.Environment.Configuration[buildConfiguration.BuildConfiguration]) + var configEnvironment = buildConfiguration.Configuration.Environment.Configuration[buildConfiguration.BuildConfiguration]; + if(configEnvironment is not null) { - env[key] = value; + foreach((var key, var value) in configEnvironment) + { + env[key] = value; + } } } + + foreach (var key in Environment.GetEnvironmentVariables().Keys) + { + env[key.ToString()] = Environment.GetEnvironmentVariable(key.ToString()); + } + + return env; } internal static void UpdateVariables(IDictionary settings, ref Dictionary output) @@ -267,10 +275,7 @@ private static void LoadSecrets(string path, ref Dictionary env) var secrets = JObject.Parse(json); foreach(var secret in secrets) { - if (!env.ContainsKey(secret.Key)) - { - env.Add(secret.Key, secret.Value.ToString()); - } + env[secret.Key] = secret.Value.ToString(); } } } diff --git a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs index 33bd62b6..e826d548 100644 --- a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs +++ b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs @@ -95,12 +95,31 @@ private void ProcessSettingsConfig(SettingsConfig settingsConfig) internal IDictionary GetMergedSecrets(SettingsConfig settingsConfig, out bool hasErrors) { + if (string.IsNullOrEmpty(settingsConfig.Prefix)) + settingsConfig.Prefix = "BuildTools_"; + var env = EnvironmentAnalyzer.GatherEnvironmentVariables(Build); var secrets = new Dictionary(); hasErrors = false; foreach(var prop in settingsConfig.Properties) { - var key = env.Keys.FirstOrDefault(x => x.Equals(prop.Name, StringComparison.InvariantCultureIgnoreCase) || x.Equals($"{settingsConfig.Prefix}{prop.Name}", StringComparison.InvariantCultureIgnoreCase)); + var searchKeys = new[] + { + $"{settingsConfig.Prefix}{prop.Name}", + $"{settingsConfig.Prefix}_{prop.Name}", + prop.Name, + }; + + string key = null; + foreach(var searchKey in searchKeys) + { + if (!string.IsNullOrEmpty(key)) + break; + + key = env.Keys.FirstOrDefault(x => + x.Equals(searchKey, StringComparison.InvariantCultureIgnoreCase)); + } + if(string.IsNullOrEmpty(key)) { if(string.IsNullOrEmpty(prop.DefaultValue)) @@ -117,7 +136,7 @@ internal IDictionary GetMergedSecrets(SettingsConfig settingsCon secrets[prop.Name] = env[key]; } - return env; + return secrets; } internal CodeWriter GenerateClass(SettingsConfig settingsConfig, IDictionary secrets) diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs b/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs index fcf45c47..935fd8d2 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs @@ -25,7 +25,7 @@ protected FixtureBase(string projectDirectory, ITestOutputHelper testOutputHelpe protected TestBuildConfiguration GetConfiguration(string testName = null) { var stackTrace = new StackTrace(); - var testOutput = Path.Combine("Tests", GetType().Name, testName ?? stackTrace.GetFrame(1).GetMethod().Name); + var testOutput = Path.Combine("Tests", GetType().Name, testName ?? stackTrace.GetFrame(1).GetMethod().Name, "obj"); ResetTestOutputDirectory(testOutput); return new TestBuildConfiguration diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs index d7f64788..5b89dce2 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Generators/SecretsClassGeneratorFixture.cs @@ -10,6 +10,7 @@ using Mobile.BuildTools.Build; using Mobile.BuildTools.Generators.Secrets; using Mobile.BuildTools.Models.Settings; +using Newtonsoft.Json; using Xunit; using Xunit.Abstractions; @@ -385,6 +386,124 @@ public void GeneratesValidClass(string secretsFile, PropertyType propertyType, T } } + [Fact] + public void MergedSecretsContainsSingleElement() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp", + PropertyType = PropertyType.String, + DefaultValue = "Hello World" + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + + var generator = new SecretsClassGenerator(config); + var mergedSecrets = generator.GetMergedSecrets(settingsConfig, out var hasErrors); + + Assert.False(hasErrors); + Assert.Single(mergedSecrets); + } + + [Fact] + public void GetsDefaultValueFromValueConfig() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp", + PropertyType = PropertyType.String, + DefaultValue = "Hello World" + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + + var generator = new SecretsClassGenerator(config); + var mergedSecrets = generator.GetMergedSecrets(settingsConfig, out var hasErrors); + + Assert.False(hasErrors); + Assert.True(mergedSecrets.ContainsKey("SampleProp")); + Assert.Equal("Hello World", mergedSecrets["SampleProp"]); + } + + [Fact] + public void GetsValuesFromPrefixedHostEnvironmentVariable() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Prefix = "SampleTest_", + Properties = new List + { + new ValueConfig + { + Name = "SampleProp1", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + Environment.SetEnvironmentVariable("SampleTest_SampleProp1", nameof(GetsValuesFromPrefixedHostEnvironmentVariable), EnvironmentVariableTarget.Process); + + var generator = new SecretsClassGenerator(config); + var mergedSecrets = generator.GetMergedSecrets(settingsConfig, out var hasErrors); + Environment.SetEnvironmentVariable("SampleTest_SampleProp1", null, EnvironmentVariableTarget.Process); + + Assert.False(hasErrors); + Assert.True(mergedSecrets.ContainsKey("SampleProp1")); + Assert.Equal(nameof(GetsValuesFromPrefixedHostEnvironmentVariable), mergedSecrets["SampleProp1"]); + } + + [Fact] + public void GetsPrefixedValueOverExactMatch() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Prefix = "SampleTest_", + Properties = new List + { + new ValueConfig + { + Name = "SampleProp2", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + Environment.SetEnvironmentVariable("SampleTest_SampleProp2", nameof(GetsValuesFromPrefixedHostEnvironmentVariable), EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("SampleProp2", "Wrong Value", EnvironmentVariableTarget.Process); + + var generator = new SecretsClassGenerator(config); + var mergedSecrets = generator.GetMergedSecrets(settingsConfig, out var hasErrors); + Environment.SetEnvironmentVariable("SampleTest_SampleProp2", null, EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("SampleProp2", null, EnvironmentVariableTarget.Process); + + Assert.False(hasErrors); + Assert.True(mergedSecrets.ContainsKey("SampleProp2")); + Assert.Equal(nameof(GetsValuesFromPrefixedHostEnvironmentVariable), mergedSecrets["SampleProp2"]); + } + + public class SecretValue { public PropertyType Type { get; set; } diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/Utils/EnvironmentAnalyzerFixture.cs b/tests/Mobile.BuildTools.Tests/Fixtures/Utils/EnvironmentAnalyzerFixture.cs new file mode 100644 index 00000000..76f70d35 --- /dev/null +++ b/tests/Mobile.BuildTools.Tests/Fixtures/Utils/EnvironmentAnalyzerFixture.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mobile.BuildTools.Build; +using Mobile.BuildTools.Generators.Secrets; +using Mobile.BuildTools.Models.Settings; +using Mobile.BuildTools.Utils; +using Newtonsoft.Json; +using Xunit; +using Xunit.Abstractions; + +namespace Mobile.BuildTools.Tests.Fixtures.Utils +{ + public class EnvironmentAnalyzerFixture : FixtureBase, IDisposable + { + public EnvironmentAnalyzerFixture(ITestOutputHelper testOutputHelper) + : base(nameof(EnvironmentAnalyzerFixture), testOutputHelper) + { + } + + [Theory] + [InlineData("secrets.json")] + [InlineData("appsettings.json")] + public void GetsValuesFromJson(string filename) + { + var config = GetConfiguration($"{nameof(GetsValuesFromJson)}-{Path.GetFileNameWithoutExtension(filename)}"); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + var secrets = new + { + SampleProp = "Hello Tests" + }; + File.WriteAllText(Path.Combine(projectDir, filename), JsonConvert.SerializeObject(secrets)); + + var mergedSecrets = EnvironmentAnalyzer.GatherEnvironmentVariables(config); + + Assert.True(mergedSecrets.ContainsKey("SampleProp")); + Assert.Equal(secrets.SampleProp, mergedSecrets["SampleProp"]); + } + + [Fact] + public void GetsValuesFromConfigurationEnvironment() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.Environment.Defaults.Add("SampleProp", "Hello Tests"); + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + + var mergedSecrets = EnvironmentAnalyzer.GatherEnvironmentVariables(config); + + Assert.True(mergedSecrets.ContainsKey("SampleProp")); + Assert.Equal(config.Configuration.Environment.Defaults["SampleProp"], mergedSecrets["SampleProp"]); + } + + [Fact] + public void GetsValuesForBuildConfigurationFromConfigurationEnvironment() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.Environment.Defaults.Add("SampleProp", "Hello Tests"); + config.Configuration.Environment.Configuration[config.BuildConfiguration] = new Dictionary + { + { "SampleProp", "Hello Override" } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + + var mergedSecrets = EnvironmentAnalyzer.GatherEnvironmentVariables(config); + + Assert.True(mergedSecrets.ContainsKey("SampleProp")); + Assert.Equal(config.Configuration.Environment.Configuration[config.BuildConfiguration]["SampleProp"], mergedSecrets["SampleProp"]); + } + + [Fact] + public void GetsValuesFromHostEnvironment() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp1", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + Environment.SetEnvironmentVariable("SampleProp1", nameof(GetsValuesFromHostEnvironment), EnvironmentVariableTarget.Process); + + var mergedSecrets = EnvironmentAnalyzer.GatherEnvironmentVariables(config); + + Assert.True(mergedSecrets.ContainsKey("SampleProp1")); + Assert.Equal(nameof(GetsValuesFromHostEnvironment), mergedSecrets["SampleProp1"]); + } + + + + [Fact] + public void OverridesConfigEnvironmentFromHostEnvironment() + { + var config = GetConfiguration(); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp3", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + config.Configuration.Environment.Defaults["SampleProp3"] = "Hello Config Environment"; + + Environment.SetEnvironmentVariable("SampleProp3", nameof(OverridesConfigEnvironmentFromHostEnvironment), EnvironmentVariableTarget.Process); + + var mergedSecrets = EnvironmentAnalyzer.GatherEnvironmentVariables(config); + + Assert.True(mergedSecrets.ContainsKey("SampleProp3")); + Assert.Equal(nameof(OverridesConfigEnvironmentFromHostEnvironment), mergedSecrets["SampleProp3"]); + } + + [Theory] + [InlineData("secrets.json")] + [InlineData("appsettings.json")] + public void OverridesConfigEnvironmentFromJson(string filename) + { + var config = GetConfiguration($"{nameof(OverridesConfigEnvironmentFromJson)}-{Path.GetFileNameWithoutExtension(filename)}"); + var settingsConfig = new SettingsConfig + { + Properties = new List + { + new ValueConfig + { + Name = "SampleProp", + PropertyType = PropertyType.String + } + } + }; + config.Configuration.AppSettings[config.ProjectName] = new List(new[] { settingsConfig }); + var projectDir = new DirectoryInfo(config.IntermediateOutputPath).Parent.FullName; + config.SolutionDirectory = config.ProjectDirectory = projectDir; + config.Configuration.Environment.Defaults["SampleProp"] = "Hello Config Environment"; + var secrets = new + { + SampleProp = "Hello Tests" + }; + File.WriteAllText(Path.Combine(projectDir, filename), JsonConvert.SerializeObject(secrets)); + + var mergedSecrets = EnvironmentAnalyzer.GatherEnvironmentVariables(config); + + Assert.True(mergedSecrets.ContainsKey("SampleProp")); + Assert.Equal(secrets.SampleProp, mergedSecrets["SampleProp"]); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("SampleProp", null, EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("SampleProp1", null, EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("SampleProp2", null, EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("SampleProp3", null, EnvironmentVariableTarget.Process); + } + } +} From 6e9be2533345f5deb3c492198596bcd6f1c4693b Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 5 Jul 2021 09:16:54 -0700 Subject: [PATCH 12/17] remove deprecated BuildHostSecrets Task --- .../Secrets/BuildHostSecretsGenerator.cs | 89 --------------- src/Mobile.BuildTools/Secrets.targets | 18 --- .../Tasks/BuildHostSecretsTask.cs | 38 ------- .../BuildHostSecretsGeneratorFixture.cs | 105 ------------------ 4 files changed, 250 deletions(-) delete mode 100644 src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs delete mode 100644 src/Mobile.BuildTools/Tasks/BuildHostSecretsTask.cs delete mode 100644 tests/Mobile.BuildTools.Tests/Fixtures/Generators/BuildHostSecretsGeneratorFixture.cs diff --git a/src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs b/src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs deleted file mode 100644 index f9c1b93b..00000000 --- a/src/Mobile.BuildTools/Generators/Secrets/BuildHostSecretsGenerator.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Mobile.BuildTools.Build; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Mobile.BuildTools.Generators.Secrets -{ - internal class BuildHostSecretsGenerator : GeneratorBase - { - public BuildHostSecretsGenerator(IBuildConfiguration buildConfiguration) - : base(buildConfiguration) - { - } - - public string SecretsJsonFilePath { get; set; } - - protected override void ExecuteInternal() - { - var configs = Build.GetSettingsConfig(); - var secrets = new Dictionary(); - foreach(var config in configs) - { - foreach((var key, var value) in GetSecrets(config?.Prefix)) - { - secrets.Add(key, value); - } - } - - if (!secrets.Any()) - { - Log?.LogMessage(" No Build Host Secrets Found..."); - return; - } - - Log?.LogMessage($"Generating {Path.GetFileName(SecretsJsonFilePath)}"); - var json = GetJObjectFromSecrets(secrets); - - WriteJsonFile(SecretsJsonFilePath, json.ToString(Formatting.Indented)); - - Log?.LogMessage(Build.Configuration.Debug ? json.ToString(Formatting.Indented) : SanitizeJObject(json)); - } - - internal string SanitizeJObject(JObject jObject) - { - foreach(var pair in jObject) - { - jObject[pair.Key] = "*****"; - } - - return jObject.ToString(Formatting.Indented); - } - - internal void WriteJsonFile(string path, string json) - { - var dirPath = Path.GetDirectoryName(path); - Directory.CreateDirectory(dirPath); - File.WriteAllText(path, json); - } - - internal JObject GetJObjectFromSecrets(IDictionary secrets) - { - var json = new JObject(); - foreach (var secret in secrets) - { - var key = secret.Key; - var value = secret.Value; - if (double.TryParse(value, out var d)) - { - json.Add(key, Math.Abs(d % 1) <= (double.Epsilon * 100) ? (int)d : d); - } - else if (bool.TryParse(value, out var b)) - { - json.Add(key, b); - } - else - { - json.Add(key, value); - } - } - return json; - } - - internal IDictionary GetSecrets(string knownPrefix) => - Utils.EnvironmentAnalyzer.GetSecrets(Build, knownPrefix); - } -} diff --git a/src/Mobile.BuildTools/Secrets.targets b/src/Mobile.BuildTools/Secrets.targets index 9388df94..3cdf7c89 100644 --- a/src/Mobile.BuildTools/Secrets.targets +++ b/src/Mobile.BuildTools/Secrets.targets @@ -3,24 +3,6 @@ - - - - - - - - GetGenerator(GetConfiguration()); - - private BuildHostSecretsGenerator GetGenerator(IBuildConfiguration config) => - new BuildHostSecretsGenerator(config) - { - SecretsJsonFilePath = SecretsJsonFilePath, - }; - - private void SetTestVariable() - { - var buildConfig = GetConfiguration(); - var prefix = buildConfig.GetSettingsConfig().First().Prefix; - Environment.SetEnvironmentVariable($"{prefix}Test1", "SomeValue"); - } - - [Fact] - public void DoesNotThrowException() - { - SetTestVariable(); - IGenerator generator = GetGenerator(); - var ex = Record.Exception(() => generator.Execute()); - - if (ex != null) - { - _testOutputHelper.WriteLine(ex.ToString()); - } - - Assert.Null(ex); - } - - [Fact] - public void CreatesJObject() - { - SetTestVariable(); - var buildConfig = GetConfiguration(); - var generator = GetGenerator(buildConfig); - var secretsConfig = buildConfig.GetSettingsConfig().First(); - var secrets = generator.GetSecrets(secretsConfig.Prefix); - - var jsonObject = generator.GetJObjectFromSecrets(secrets); - - Assert.NotNull(jsonObject); - Assert.IsType(jsonObject); - Assert.Equal("SomeValue", jsonObject["Test1"].ToString()); - - } - - [Fact] - public void RetrievesEnvironmentVariables() - { - SetTestVariable(); - var buildConfig = GetConfiguration(); - var generator = GetGenerator(buildConfig); - var secretsConfig = buildConfig.GetSettingsConfig().First(); - var secrets = generator.GetSecrets(secretsConfig.Prefix); - - Assert.NotNull(secrets); - Assert.NotEmpty(secrets); - foreach (var s in secrets) - { - _testOutputHelper.WriteLine($"{s.GetType()}: {s.Key}: {s.Value}"); - _testOutputHelper.WriteLine($" ---- {Environment.GetEnvironmentVariable(s.ToString())}"); - } - - Assert.Contains($"Test1", secrets.Keys); - } - - [Fact] - public void JObjectIsSanitized() - { - var generator = GetGenerator(); - var secretValue = "TestSecret"; - var json = JObject.Parse($"{{\"Secret\":\"{secretValue}\"}}"); - Assert.Equal(secretValue, json["Secret"]); - - var sanitized = generator.SanitizeJObject(json); - var secret = JsonConvert.DeserializeAnonymousType(sanitized, new { Secret = "" }); - Assert.NotEqual(secretValue, secret.Secret); - Assert.Equal("*****", secret.Secret); - } - } -} From bcece6a97b1bd218312df1a0cde0e6cb17937432 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 5 Jul 2021 09:32:28 -0700 Subject: [PATCH 13/17] fix validation --- src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index fe0ba6ca..c3bb8bf0 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -94,21 +94,23 @@ internal void ValidateConfigSchema() { try { + var path = Path.Combine(BuildToolsConfigFilePath, Constants.BuildToolsConfigFileName); + Log.LogMessage($"Validating buildtools.json at the path: {path}"); var generator = new JSchemaGenerator(); var schema = generator.Generate(typeof(BuildToolsConfig)); - var config = JObject.Parse(File.ReadAllText(BuildToolsConfigFilePath)); + var config = JObject.Parse(File.ReadAllText(path)); if (!config.IsValid(schema, out IList errorMessages)) { foreach(var error in errorMessages) { - Log.LogError("Invalid buildtools.json schema detected.."); + Log.LogError("Invalid buildtools.json schema detected..."); Log.LogError(error); } } } catch (Exception ex) { - Log.LogError("There was an unhandled exception while attempting to valid the buildtools.json."); + Log.LogError("There was an unhandled exception while attempting to validate the buildtools.json."); Log.LogError(ex.Message); } } From 9ae1dab0d6fb760ae5542b28cf6961ab18d2952f Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 5 Jul 2021 09:43:16 -0700 Subject: [PATCH 14/17] make IsArray nullable --- docs/schemas/v2/buildtools.schema.json | 9 ++++++++- .../Models/Settings/ValueConfig.cs | 10 +++++++--- .../Generators/Secrets/SecretsClassGenerator.cs | 3 ++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/schemas/v2/buildtools.schema.json b/docs/schemas/v2/buildtools.schema.json index 6df75cf3..a88fd327 100644 --- a/docs/schemas/v2/buildtools.schema.json +++ b/docs/schemas/v2/buildtools.schema.json @@ -404,12 +404,14 @@ ], "properties": { "name": { + "description": "The property name of the value to generate", "type": [ "string", "null" ] }, "type": { + "description": "The property type of the generated property", "type": "string", "enum": [ "String", @@ -434,9 +436,14 @@ ] }, "isArray": { - "type": "boolean" + "description": "Forces the property to be an Array type", + "type": [ + "boolean", + "null" + ] }, "defaultValue": { + "description": "Sets the default value of the property. Can be `null` or `default` to set the default value of the property type.", "type": [ "string", "null" diff --git a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs index 578f9157..5978c5f0 100644 --- a/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs +++ b/src/Mobile.BuildTools.Reference/Models/Settings/ValueConfig.cs @@ -1,19 +1,23 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.ComponentModel; +using Newtonsoft.Json; namespace Mobile.BuildTools.Models.Settings { public class ValueConfig { + [Description("The property name of the value to generate")] [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } + [Description("The property type of the generated property")] [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public PropertyType PropertyType { get; set; } + [Description("Forces the property to be an Array type")] [JsonProperty("isArray", NullValueHandling = NullValueHandling.Ignore)] - public bool IsArray { get; set; } + public bool? IsArray { get; set; } + [Description("Sets the default value of the property. Can be `null` or `default` to set the default value of the property type.")] [JsonProperty("defaultValue", NullValueHandling = NullValueHandling.Ignore)] public string DefaultValue { get; set; } } diff --git a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs index e826d548..2f9451f3 100644 --- a/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs +++ b/src/Mobile.BuildTools/Generators/Secrets/SecretsClassGenerator.cs @@ -292,8 +292,9 @@ internal string GetNamespace(SettingsConfig settings) return $"{rootNamespace}.{relativeNamespace}"; } - internal string PropertyBuilder(KeyValuePair secret, Type type, IValueHandler valueHandler, bool isArray, bool safeOutput, string accessibility) + internal string PropertyBuilder(KeyValuePair secret, Type type, IValueHandler valueHandler, bool? isArrayNullable, bool safeOutput, string accessibility) { + var isArray = isArrayNullable ?? false; var output = string.Empty; var typeDeclaration = type.GetStandardTypeName(); var accessModifier = type.FullName == typeDeclaration || isArray ? "static readonly" : "const"; From a27ed939790053b532e3e5c9d0c4d7dc03ccbbae Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 5 Jul 2021 10:16:58 -0700 Subject: [PATCH 15/17] fix tests --- tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs b/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs index 935fd8d2..fcf45c47 100644 --- a/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs +++ b/tests/Mobile.BuildTools.Tests/Fixtures/FixtureBase.cs @@ -25,7 +25,7 @@ protected FixtureBase(string projectDirectory, ITestOutputHelper testOutputHelpe protected TestBuildConfiguration GetConfiguration(string testName = null) { var stackTrace = new StackTrace(); - var testOutput = Path.Combine("Tests", GetType().Name, testName ?? stackTrace.GetFrame(1).GetMethod().Name, "obj"); + var testOutput = Path.Combine("Tests", GetType().Name, testName ?? stackTrace.GetFrame(1).GetMethod().Name); ResetTestOutputDirectory(testOutput); return new TestBuildConfiguration From c6f70caa3e263ab974d9c0d4cfa228db045ff9cb Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 5 Jul 2021 10:17:37 -0700 Subject: [PATCH 16/17] keep from full config activation for secrets migration --- src/Mobile.BuildTools.Reference/Utils/ConfigHelper.shared.cs | 4 +++- src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.shared.cs b/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.shared.cs index 936fcf96..a4806f09 100644 --- a/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.shared.cs +++ b/src/Mobile.BuildTools.Reference/Utils/ConfigHelper.shared.cs @@ -50,7 +50,7 @@ public static bool Exists(string path, out string filePath) return File.Exists(filePath); } - public static BuildToolsConfig GetConfig(string path) + public static BuildToolsConfig GetConfig(string path, bool skipActivation = false) { var filePath = GetConfigurationPath(path); var configurationDirectoryPath = filePath; @@ -71,6 +71,8 @@ public static BuildToolsConfig GetConfig(string path) } var config = JsonConvert.DeserializeObject(json, GetSerializerSettings()); + if (skipActivation) + return config; var props = typeof(BuildToolsConfig).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => x.PropertyType != typeof(bool) && x.PropertyType != typeof(string)); diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index c3bb8bf0..9d492cf0 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -118,7 +118,9 @@ internal void ValidateConfigSchema() #pragma warning disable CS0612 // Project Secrets is obsolete. This converts to the new AppSettings. internal void MigrateSecretsToSettings() { - var config = ConfigHelper.GetConfig(BuildToolsConfigFilePath); + var config = ConfigHelper.GetConfig(BuildToolsConfigFilePath, skipActivation: true); + if (config.AppSettings is null) + config.AppSettings = new Dictionary>(); if (config.ProjectSecrets is not null && config.ProjectSecrets.Any()) { From 5b376961565c844da8bc0fb9cd78f189930af3b3 Mon Sep 17 00:00:00 2001 From: Dan Siegel Date: Mon, 5 Jul 2021 10:21:29 -0700 Subject: [PATCH 17/17] use behavior instead of Disable property for Versioning Behavior --- docs/schemas/v2/buildtools.schema.json | 8 -------- .../Models/AutomaticVersioning.cs | 2 +- src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/docs/schemas/v2/buildtools.schema.json b/docs/schemas/v2/buildtools.schema.json index a88fd327..8808b358 100644 --- a/docs/schemas/v2/buildtools.schema.json +++ b/docs/schemas/v2/buildtools.schema.json @@ -75,14 +75,6 @@ "versionOffset": { "description": "If you need to offset from your build number, you may want to set the Version Offset to get a version that will work for you.", "type": "integer" - }, - "disable": { - "description": "Disables this Mobile.BuildTools Task", - "type": [ - "boolean", - "null" - ], - "default": false } } }, diff --git a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs index c78f9042..ded23b56 100644 --- a/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs +++ b/src/Mobile.BuildTools.Reference/Models/AutomaticVersioning.cs @@ -3,7 +3,7 @@ namespace Mobile.BuildTools.Models { - public class AutomaticVersioning : ToolItem + public class AutomaticVersioning { [DefaultValue(VersionBehavior.PreferBuildNumber)] [Description("Sets the default behavior for versioning the app. By default the Mobile.BuildTools will attempt to use a Build number and will fallback to a timestamp.")] diff --git a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs index 9d492cf0..b96d0e35 100644 --- a/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs +++ b/src/Mobile.BuildTools/Tasks/LocateBuildToolsConfigTask.cs @@ -76,7 +76,7 @@ public override bool Execute() // Only run these tasks for Android and iOS projects if they're not explicitly disabled EnableArtifactCopy = !crossTargetingProject && IsEnabled(configuration?.ArtifactCopy) && isPlatformHead; - EnableAutomaticVersioning = !crossTargetingProject && IsEnabled(configuration?.AutomaticVersioning) && isPlatformHead; + EnableAutomaticVersioning = !crossTargetingProject && configuration.AutomaticVersioning is not null && configuration.AutomaticVersioning.Behavior != VersionBehavior.Off && isPlatformHead; EnableImageProcessing = !crossTargetingProject && IsEnabled(configuration?.Images) && (platform == Platform.iOS || platform == Platform.Android); EnableTemplateManifests = !crossTargetingProject && IsEnabled(configuration?.Manifests) && isPlatformHead; EnableReleaseNotes = IsEnabled(configuration?.ReleaseNotes) && isPlatformHead && EnvironmentAnalyzer.IsInGitRepo(ProjectDir);