From a7e902163a2c09beafa4380c27ab3cea62f2c78a Mon Sep 17 00:00:00 2001 From: Muhammad Danish <88161975+mdanish-kh@users.noreply.github.com> Date: Sat, 24 Oct 2026 02:39:10 +0500 Subject: [PATCH 1/3] Bump manifest schema version to 1.28.0 --- Directory.Packages.props | 2 +- src/WingetCreateCLI/Commands/BaseCommand.cs | 2 +- src/WingetCreateCLI/Commands/NewCommand.cs | 2 +- src/WingetCreateCLI/Commands/ShowCommand.cs | 2 +- src/WingetCreateCLI/Commands/SubmitCommand.cs | 2 +- src/WingetCreateCLI/Commands/UpdateCommand.cs | 8 +- src/WingetCreateCLI/PromptHelper.cs | 2 +- src/WingetCreateCLI/Properties/Resources.resx | 21 +++ src/WingetCreateCLI/WingetCreateCLI.csproj | 2 +- .../Models/DefaultLocaleManifestModels.cs | 4 +- .../Models/InstallerManifestModels.cs | 152 ++++++++++++++- .../Models/LocaleManifestModels.cs | 4 +- .../Models/Partials/InstallerPartials.cs | 140 ++++++++++++++ .../Models/SingletonManifestModels.cs | 152 ++++++++++++++- .../Models/VersionManifestModels.cs | 4 +- src/WingetCreateCore/WingetCreateCore.csproj | 10 +- ...blisher.DontMoveInstallerFieldsToRoot.yaml | 44 ++++- .../TestPublisher.FullJsonSingleton1_28.json | 177 ++++++++++++++++++ .../TestPublisher.FullYamlSingleton1_28.yaml | 125 +++++++++++++ ...stPublisher.MoveInstallerFieldsToRoot.yaml | 44 ++++- .../UnitTests/LocalizationTests.cs | 2 +- .../UnitTests/UpdateCommandTests.cs | 35 +++- .../WingetCreateTests.csproj | 6 + 23 files changed, 915 insertions(+), 27 deletions(-) create mode 100644 src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullJsonSingleton1_28.json create mode 100644 src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullYamlSingleton1_28.yaml diff --git a/Directory.Packages.props b/Directory.Packages.props index 77bb574b..608ddd61 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,7 +12,7 @@ - + diff --git a/src/WingetCreateCLI/Commands/BaseCommand.cs b/src/WingetCreateCLI/Commands/BaseCommand.cs index e987b1c3..e9003ef5 100644 --- a/src/WingetCreateCLI/Commands/BaseCommand.cs +++ b/src/WingetCreateCLI/Commands/BaseCommand.cs @@ -15,7 +15,6 @@ namespace Microsoft.WingetCreateCLI.Commands using System.Threading.Tasks; using Microsoft.WingetCreateCLI.Logging; using Microsoft.WingetCreateCLI.Models.Settings; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCLI.Telemetry; using Microsoft.WingetCreateCLI.Telemetry.Events; using Microsoft.WingetCreateCore; @@ -30,6 +29,7 @@ namespace Microsoft.WingetCreateCLI.Commands using Octokit; using RestSharp; using Sharprompt; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Abstract base command class that all commands inherit from. diff --git a/src/WingetCreateCLI/Commands/NewCommand.cs b/src/WingetCreateCLI/Commands/NewCommand.cs index 829fc237..a50f37ee 100644 --- a/src/WingetCreateCLI/Commands/NewCommand.cs +++ b/src/WingetCreateCLI/Commands/NewCommand.cs @@ -15,7 +15,6 @@ namespace Microsoft.WingetCreateCLI.Commands using CommandLine.Text; using Microsoft.WingetCreateCLI.Logging; using Microsoft.WingetCreateCLI.Models.Settings; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCLI.Telemetry; using Microsoft.WingetCreateCLI.Telemetry.Events; using Microsoft.WingetCreateCore; @@ -27,6 +26,7 @@ namespace Microsoft.WingetCreateCLI.Commands using Microsoft.WingetCreateCore.Models.Version; using Newtonsoft.Json.Schema.Generation; using Sharprompt; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Command to launch a wizard that prompt users for information to generate a new manifest. diff --git a/src/WingetCreateCLI/Commands/ShowCommand.cs b/src/WingetCreateCLI/Commands/ShowCommand.cs index 555f68ec..548aa8d2 100644 --- a/src/WingetCreateCLI/Commands/ShowCommand.cs +++ b/src/WingetCreateCLI/Commands/ShowCommand.cs @@ -10,7 +10,6 @@ namespace Microsoft.WingetCreateCLI.Commands using CommandLine.Text; using Microsoft.WingetCreateCLI.Logging; using Microsoft.WingetCreateCLI.Models.Settings; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCLI.Telemetry; using Microsoft.WingetCreateCLI.Telemetry.Events; using Microsoft.WingetCreateCore; @@ -18,6 +17,7 @@ namespace Microsoft.WingetCreateCLI.Commands using Microsoft.WingetCreateCore.Models.Installer; using Microsoft.WingetCreateCore.Models.Singleton; using Microsoft.WingetCreateCore.Models.Version; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Show command to display manifest from the packages repository. diff --git a/src/WingetCreateCLI/Commands/SubmitCommand.cs b/src/WingetCreateCLI/Commands/SubmitCommand.cs index 81d248b0..45738217 100644 --- a/src/WingetCreateCLI/Commands/SubmitCommand.cs +++ b/src/WingetCreateCLI/Commands/SubmitCommand.cs @@ -11,12 +11,12 @@ namespace Microsoft.WingetCreateCLI.Commands using CommandLine.Text; using Microsoft.VisualBasic; using Microsoft.WingetCreateCLI.Logging; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCLI.Telemetry; using Microsoft.WingetCreateCLI.Telemetry.Events; using Microsoft.WingetCreateCore; using Microsoft.WingetCreateCore.Models; using Microsoft.WingetCreateCore.Models.Singleton; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Command to validate and submit a manifest to the GitHub Windows Package Manager repository. diff --git a/src/WingetCreateCLI/Commands/UpdateCommand.cs b/src/WingetCreateCLI/Commands/UpdateCommand.cs index bb208ec3..e063c6cd 100644 --- a/src/WingetCreateCLI/Commands/UpdateCommand.cs +++ b/src/WingetCreateCLI/Commands/UpdateCommand.cs @@ -15,7 +15,6 @@ namespace Microsoft.WingetCreateCLI.Commands using Microsoft.Extensions.Logging.Abstractions; using Microsoft.WingetCreateCLI.Logging; using Microsoft.WingetCreateCLI.Models.Settings; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCLI.Telemetry; using Microsoft.WingetCreateCLI.Telemetry.Events; using Microsoft.WingetCreateCore; @@ -27,6 +26,7 @@ namespace Microsoft.WingetCreateCLI.Commands using Microsoft.WingetCreateCore.Models.Locale; using Microsoft.WingetCreateCore.Models.Version; using Sharprompt; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Command for updating the elements of an existing or local manifest. @@ -640,6 +640,12 @@ private static Manifests ConvertSingletonToMultifileManifest(WingetCreateCore.Mo cfg.CreateMap(); cfg.CreateMap(); cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); }, NullLoggerFactory.Instance); var mapper = config.CreateMapper(); diff --git a/src/WingetCreateCLI/PromptHelper.cs b/src/WingetCreateCLI/PromptHelper.cs index 6b221969..5a04e677 100644 --- a/src/WingetCreateCLI/PromptHelper.cs +++ b/src/WingetCreateCLI/PromptHelper.cs @@ -10,12 +10,12 @@ namespace Microsoft.WingetCreateCLI using System.Linq; using System.Reflection; using Microsoft.WingetCreateCLI.Logging; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCore; using Microsoft.WingetCreateCore.Common; using Microsoft.WingetCreateCore.Models.DefaultLocale; using Microsoft.WingetCreateCore.Models.Installer; using Sharprompt; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Provides functionality for prompting the user for input to obtain field values for a manifest. diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx index a264a300..dad5ef02 100644 --- a/src/WingetCreateCLI/Properties/Resources.resx +++ b/src/WingetCreateCLI/Properties/Resources.resx @@ -1486,4 +1486,25 @@ Warning: Using this argument may result in the token being logged. Consider an a Found mixed font and non-font installer types in the manifest. This is not a supported configuration and the manifest should be edited to contain only font or non-font installers before submission. + + Desired state configuration (DSC) resources related to the package + + + DSC resources contained in PowerShell modules + + + The URL of the repository containing the PowerShell module + + + The name of the PowerShell module containing DSC resources + + + The DSC resources contained in the module or package + + + DSC resources contained in the package using the DSC v3 specification + + + The fully qualified type of the DSC resource + \ No newline at end of file diff --git a/src/WingetCreateCLI/WingetCreateCLI.csproj b/src/WingetCreateCLI/WingetCreateCLI.csproj index 93cd08f6..6f5b8f00 100644 --- a/src/WingetCreateCLI/WingetCreateCLI.csproj +++ b/src/WingetCreateCLI/WingetCreateCLI.csproj @@ -5,7 +5,7 @@ net9.0-windows10.0.26100.0 WingetCreateCLI Microsoft.WingetCreateCLI - 1.12 + 1.28 x64;x86 win-x64;win-x86 true diff --git a/src/WingetCreateCore/Models/DefaultLocaleManifestModels.cs b/src/WingetCreateCore/Models/DefaultLocaleManifestModels.cs index 71a33484..7105ed2b 100644 --- a/src/WingetCreateCore/Models/DefaultLocaleManifestModels.cs +++ b/src/WingetCreateCore/Models/DefaultLocaleManifestModels.cs @@ -133,7 +133,7 @@ public System.Collections.Generic.IDictionary AdditionalProperti } /// - /// A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.12.0 + /// A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.28.0 /// [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] public partial class DefaultLocaleManifest @@ -335,7 +335,7 @@ public partial class DefaultLocaleManifest [Newtonsoft.Json.JsonProperty("ManifestVersion", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.RegularExpression(@"^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$")] - public string ManifestVersion { get; set; } = "1.12.0"; + public string ManifestVersion { get; set; } = "1.28.0"; diff --git a/src/WingetCreateCore/Models/InstallerManifestModels.cs b/src/WingetCreateCore/Models/InstallerManifestModels.cs index c5229c24..00e15135 100644 --- a/src/WingetCreateCore/Models/InstallerManifestModels.cs +++ b/src/WingetCreateCore/Models/InstallerManifestModels.cs @@ -466,6 +466,38 @@ public partial class Authentication + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// References to desired state configuration (DSC) resources that are related to the package. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class DesiredStateConfiguration + { + /// + /// Contains data about DSC resources that are contained in PowerShell modules. + /// + [Newtonsoft.Json.JsonProperty("PowerShell", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.MaxLength(16)] + public System.Collections.Generic.List PowerShell { get; set; } + + /// + /// Contains data about DSC resources that are contained in the package using the DSC v3 specification. + /// + [Newtonsoft.Json.JsonProperty("DSCv3", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public DSCv3 DSCv3 { get; set; } + + + private System.Collections.Generic.IDictionary _additionalProperties; [Newtonsoft.Json.JsonExtensionData] @@ -638,6 +670,9 @@ public partial class Installer [Newtonsoft.Json.JsonProperty("Authentication", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public Authentication Authentication { get; set; } + [Newtonsoft.Json.JsonProperty("DesiredStateConfiguration", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public DesiredStateConfiguration DesiredStateConfiguration { get; set; } + private System.Collections.Generic.IDictionary _additionalProperties; @@ -652,7 +687,7 @@ public System.Collections.Generic.IDictionary AdditionalProperti } /// - /// A representation of a single-file manifest representing an app installers in the OWC. v1.12.0 + /// A representation of a single-file manifest representing an app installers in the OWC. v1.28.0 /// [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] public partial class InstallerManifest @@ -802,6 +837,9 @@ public partial class InstallerManifest [Newtonsoft.Json.JsonProperty("Authentication", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public Authentication Authentication { get; set; } + [Newtonsoft.Json.JsonProperty("DesiredStateConfiguration", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public DesiredStateConfiguration DesiredStateConfiguration { get; set; } + [Newtonsoft.Json.JsonProperty("Installers", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [System.ComponentModel.DataAnnotations.Required] [System.ComponentModel.DataAnnotations.MinLength(1)] @@ -821,7 +859,7 @@ public partial class InstallerManifest [Newtonsoft.Json.JsonProperty("ManifestVersion", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.RegularExpression(@"^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$")] - public string ManifestVersion { get; set; } = "1.12.0"; + public string ManifestVersion { get; set; } = "1.28.0"; @@ -955,6 +993,68 @@ public partial class MicrosoftEntraIdAuthenticationInfo + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class PowerShell + { + [Newtonsoft.Json.JsonProperty("RepositoryUrl", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(2048)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^([Hh][Tt][Tt][Pp][Ss]?)://.+$")] + public string RepositoryUrl { get; set; } + + /// + /// The name of the module containing resources. + /// + [Newtonsoft.Json.JsonProperty("ModuleName", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.StringLength(100)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^\w+([.-]\w+)*$")] + public string ModuleName { get; set; } + + /// + /// The resources contained within the module. + /// + [Newtonsoft.Json.JsonProperty("Resources", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + [System.ComponentModel.DataAnnotations.MaxLength(64)] + public System.Collections.Generic.List Resources { get; set; } = new System.Collections.Generic.List(); + + + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class DSCv3 + { + /// + /// The resources contained within the package. + /// + [Newtonsoft.Json.JsonProperty("Resources", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + [System.ComponentModel.DataAnnotations.MaxLength(128)] + public System.Collections.Generic.List Resources { get; set; } = new System.Collections.Generic.List(); + + + private System.Collections.Generic.IDictionary _additionalProperties; [Newtonsoft.Json.JsonExtensionData] @@ -1119,6 +1219,54 @@ public enum FilesFileType } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class Resources + { + /// + /// The name of the resource. + /// + [Newtonsoft.Json.JsonProperty("Name", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(100)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^[A-Za-z][-_A-Za-z0-9]*$")] + public string Name { get; set; } + + + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class Resources2 + { + /// + /// The name of the resource. + /// + [Newtonsoft.Json.JsonProperty("Type", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(256)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^\w+(\.\w+){0,2}\/\w+$")] + public string Type { get; set; } + + + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] public enum ExpectedReturnCodeReturnResponse { diff --git a/src/WingetCreateCore/Models/LocaleManifestModels.cs b/src/WingetCreateCore/Models/LocaleManifestModels.cs index 35f9ceeb..15263e40 100644 --- a/src/WingetCreateCore/Models/LocaleManifestModels.cs +++ b/src/WingetCreateCore/Models/LocaleManifestModels.cs @@ -133,7 +133,7 @@ public System.Collections.Generic.IDictionary AdditionalProperti } /// - /// A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.12.0 + /// A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.28.0 /// [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] public partial class LocaleManifest @@ -324,7 +324,7 @@ public partial class LocaleManifest [Newtonsoft.Json.JsonProperty("ManifestVersion", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.RegularExpression(@"^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$")] - public string ManifestVersion { get; set; } = "1.12.0"; + public string ManifestVersion { get; set; } = "1.28.0"; diff --git a/src/WingetCreateCore/Models/Partials/InstallerPartials.cs b/src/WingetCreateCore/Models/Partials/InstallerPartials.cs index 5b30845b..54a382d1 100644 --- a/src/WingetCreateCore/Models/Partials/InstallerPartials.cs +++ b/src/WingetCreateCore/Models/Partials/InstallerPartials.cs @@ -231,6 +231,146 @@ public override bool Equals(object obj) } } + /// + /// Partial class that implements helper methods for the Authentication class. + /// +#pragma warning disable SA1402 // File may only contain a single type + public partial class Authentication +#pragma warning restore SA1402 // File may only contain a single type + { + /// + public override bool Equals(object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + Authentication other = (Authentication)obj; + return this.AuthenticationType == other.AuthenticationType && Equals(this.MicrosoftEntraIdAuthenticationInfo, other.MicrosoftEntraIdAuthenticationInfo); + } + } + + /// + /// Partial class that implements helper methods for the MicrosoftEntraIdAuthenticationInfo class. + /// +#pragma warning disable SA1402 // File may only contain a single type + public partial class MicrosoftEntraIdAuthenticationInfo +#pragma warning restore SA1402 // File may only contain a single type + { + /// + public override bool Equals(object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + MicrosoftEntraIdAuthenticationInfo other = (MicrosoftEntraIdAuthenticationInfo)obj; + return this.Resource == other.Resource && this.Scope == other.Scope; + } + } + + /// + /// Partial class that implements helper methods for the DesiredStateConfiguration class. + /// +#pragma warning disable SA1402 // File may only contain a single type + public partial class DesiredStateConfiguration +#pragma warning restore SA1402 // File may only contain a single type + { + /// + public override bool Equals(object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + DesiredStateConfiguration other = (DesiredStateConfiguration)obj; + return (this.PowerShell == null ? other.PowerShell == null : other.PowerShell != null && this.PowerShell.SequenceEqual(other.PowerShell)) && Equals(this.DSCv3, other.DSCv3); + } + } + + /// + /// Partial class that implements helper methods for the PowerShell class. + /// +#pragma warning disable SA1402 // File may only contain a single type + public partial class PowerShell +#pragma warning restore SA1402 // File may only contain a single type + { + /// + public override bool Equals(object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + PowerShell other = (PowerShell)obj; + return this.RepositoryUrl == other.RepositoryUrl && this.ModuleName == other.ModuleName && (this.Resources == null ? other.Resources == null : other.Resources != null && this.Resources.SequenceEqual(other.Resources)); + } + } + + /// + /// Partial class that implements helper methods for the DSCv3 class. + /// +#pragma warning disable SA1402 // File may only contain a single type + public partial class DSCv3 +#pragma warning restore SA1402 // File may only contain a single type + { + /// + public override bool Equals(object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + DSCv3 other = (DSCv3)obj; + return this.Resources == null ? other.Resources == null : other.Resources != null && this.Resources.SequenceEqual(other.Resources); + } + } + + /// + /// Partial class that implements helper methods for the Resources class. + /// +#pragma warning disable SA1402 // File may only contain a single type + public partial class Resources +#pragma warning restore SA1402 // File may only contain a single type + { + /// + public override bool Equals(object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + Resources other = (Resources)obj; + return this.Name == other.Name; + } + } + + /// + /// Partial class that implements helper methods for the Resources2 class. + /// +#pragma warning disable SA1402 // File may only contain a single type + public partial class Resources2 +#pragma warning restore SA1402 // File may only contain a single type + { + /// + public override bool Equals(object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + Resources2 other = (Resources2)obj; + return this.Type == other.Type; + } + } + /// /// Partial InstallerManifest class for defining a string type ReleaseDateTimeField. /// Workaround for issue with model generating ReleaseDate with DateTimeOffset type. diff --git a/src/WingetCreateCore/Models/SingletonManifestModels.cs b/src/WingetCreateCore/Models/SingletonManifestModels.cs index 4317e924..1c9db8c2 100644 --- a/src/WingetCreateCore/Models/SingletonManifestModels.cs +++ b/src/WingetCreateCore/Models/SingletonManifestModels.cs @@ -589,6 +589,38 @@ public partial class Authentication + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// References to desired state configuration (DSC) resources that are related to the package. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class DesiredStateConfiguration + { + /// + /// Contains data about DSC resources that are contained in PowerShell modules. + /// + [Newtonsoft.Json.JsonProperty("PowerShell", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.MaxLength(16)] + public System.Collections.Generic.List PowerShell { get; set; } + + /// + /// Contains data about DSC resources that are contained in the package using the DSC v3 specification. + /// + [Newtonsoft.Json.JsonProperty("DSCv3", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public DSCv3 DSCv3 { get; set; } + + + private System.Collections.Generic.IDictionary _additionalProperties; [Newtonsoft.Json.JsonExtensionData] @@ -761,6 +793,9 @@ public partial class Installer [Newtonsoft.Json.JsonProperty("Authentication", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public Authentication Authentication { get; set; } + [Newtonsoft.Json.JsonProperty("DesiredStateConfiguration", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public DesiredStateConfiguration DesiredStateConfiguration { get; set; } + private System.Collections.Generic.IDictionary _additionalProperties; @@ -775,7 +810,7 @@ public System.Collections.Generic.IDictionary AdditionalProperti } /// - /// A representation of a single-file manifest representing an app in the OWC. v1.12.0 + /// A representation of a single-file manifest representing an app in the OWC. v1.28.0 /// [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] public partial class SingletonManifest @@ -1087,6 +1122,9 @@ public partial class SingletonManifest [Newtonsoft.Json.JsonProperty("Authentication", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public Authentication Authentication { get; set; } + [Newtonsoft.Json.JsonProperty("DesiredStateConfiguration", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public DesiredStateConfiguration DesiredStateConfiguration { get; set; } + [Newtonsoft.Json.JsonProperty("Installers", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [System.ComponentModel.DataAnnotations.Required] [System.ComponentModel.DataAnnotations.MinLength(1)] @@ -1106,7 +1144,7 @@ public partial class SingletonManifest [Newtonsoft.Json.JsonProperty("ManifestVersion", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.RegularExpression(@"^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$")] - public string ManifestVersion { get; set; } = "1.12.0"; + public string ManifestVersion { get; set; } = "1.28.0"; @@ -1346,6 +1384,68 @@ public partial class MicrosoftEntraIdAuthenticationInfo + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class PowerShell + { + [Newtonsoft.Json.JsonProperty("RepositoryUrl", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(2048)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^([Hh][Tt][Tt][Pp][Ss]?)://.+$")] + public string RepositoryUrl { get; set; } + + /// + /// The name of the module containing resources. + /// + [Newtonsoft.Json.JsonProperty("ModuleName", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.StringLength(100)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^\w+([.-]\w+)*$")] + public string ModuleName { get; set; } + + /// + /// The resources contained within the module. + /// + [Newtonsoft.Json.JsonProperty("Resources", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + [System.ComponentModel.DataAnnotations.MaxLength(64)] + public System.Collections.Generic.List Resources { get; set; } = new System.Collections.Generic.List(); + + + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class DSCv3 + { + /// + /// The resources contained within the package. + /// + [Newtonsoft.Json.JsonProperty("Resources", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + [System.ComponentModel.DataAnnotations.MaxLength(128)] + public System.Collections.Generic.List Resources { get; set; } = new System.Collections.Generic.List(); + + + private System.Collections.Generic.IDictionary _additionalProperties; [Newtonsoft.Json.JsonExtensionData] @@ -1510,6 +1610,54 @@ public enum FilesFileType } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class Resources + { + /// + /// The name of the resource. + /// + [Newtonsoft.Json.JsonProperty("Name", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(100)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^[A-Za-z][-_A-Za-z0-9]*$")] + public string Name { get; set; } + + + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class Resources2 + { + /// + /// The name of the resource. + /// + [Newtonsoft.Json.JsonProperty("Type", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(256)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^\w+(\.\w+){0,2}\/\w+$")] + public string Type { get; set; } + + + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] public enum ExpectedReturnCodeReturnResponse { diff --git a/src/WingetCreateCore/Models/VersionManifestModels.cs b/src/WingetCreateCore/Models/VersionManifestModels.cs index f0b8bf64..f6493030 100644 --- a/src/WingetCreateCore/Models/VersionManifestModels.cs +++ b/src/WingetCreateCore/Models/VersionManifestModels.cs @@ -10,7 +10,7 @@ namespace Microsoft.WingetCreateCore.Models.Version #pragma warning disable // Disable all warnings /// - /// A representation of a multi-file manifest representing an app version in the OWC. v1.12.0 + /// A representation of a multi-file manifest representing an app version in the OWC. v1.28.0 /// [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "11.0.0.0 (Newtonsoft.Json v13.0.0.0)")] public partial class VersionManifest @@ -55,7 +55,7 @@ public partial class VersionManifest [Newtonsoft.Json.JsonProperty("ManifestVersion", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.RegularExpression(@"^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$")] - public string ManifestVersion { get; set; } = "1.12.0"; + public string ManifestVersion { get; set; } = "1.28.0"; diff --git a/src/WingetCreateCore/WingetCreateCore.csproj b/src/WingetCreateCore/WingetCreateCore.csproj index 8687e17e..63b60288 100644 --- a/src/WingetCreateCore/WingetCreateCore.csproj +++ b/src/WingetCreateCore/WingetCreateCore.csproj @@ -48,11 +48,11 @@ - - - - - + + + + + diff --git a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.DontMoveInstallerFieldsToRoot.yaml b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.DontMoveInstallerFieldsToRoot.yaml index 82e8a7dd..a80546fb 100644 --- a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.DontMoveInstallerFieldsToRoot.yaml +++ b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.DontMoveInstallerFieldsToRoot.yaml @@ -77,6 +77,20 @@ Installers: DownloadCommandProhibited: true RepairBehavior: modify ArchiveBinariesDependOnPath: true + Authentication: + AuthenticationType: microsoftEntraId + MicrosoftEntraIdAuthenticationInfo: + Resource: TestResource1 + Scope: TestScope1 + DesiredStateConfiguration: + PowerShell: + - RepositoryUrl: https://fakeTestUrl.com/repository1 + ModuleName: TestModule1 + Resources: + - Name: TestResource1 + DSCv3: + Resources: + - Type: TestPublisher/TestResource1 - Architecture: x86 InstallerType: zip InstallerUrl: https://fakedomain.com/WingetCreateTestZipInstaller.zip @@ -147,6 +161,20 @@ Installers: DownloadCommandProhibited: false RepairBehavior: installer ArchiveBinariesDependOnPath: false + Authentication: + AuthenticationType: microsoftEntraId + MicrosoftEntraIdAuthenticationInfo: + Resource: TestResource2 + Scope: TestScope2 + DesiredStateConfiguration: + PowerShell: + - RepositoryUrl: https://fakeTestUrl.com/repository2 + ModuleName: TestModule2 + Resources: + - Name: TestResource2 + DSCv3: + Resources: + - Type: TestPublisher/TestResource2 - Architecture: arm InstallerType: zip InstallerUrl: https://fakedomain.com/WingetCreateTestZipInstaller.zip @@ -217,6 +245,20 @@ Installers: DownloadCommandProhibited: false RepairBehavior: modify ArchiveBinariesDependOnPath: false + Authentication: + AuthenticationType: microsoftEntraId + MicrosoftEntraIdAuthenticationInfo: + Resource: TestResource2 + Scope: TestScope2 + DesiredStateConfiguration: + PowerShell: + - RepositoryUrl: https://fakeTestUrl.com/repository2 + ModuleName: TestModule2 + Resources: + - Name: TestResource2 + DSCv3: + Resources: + - Type: TestPublisher/TestResource2 PackageLocale: en-US ManifestType: singleton -ManifestVersion: 1.9.0 \ No newline at end of file +ManifestVersion: 1.28.0 \ No newline at end of file diff --git a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullJsonSingleton1_28.json b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullJsonSingleton1_28.json new file mode 100644 index 00000000..b55671c5 --- /dev/null +++ b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullJsonSingleton1_28.json @@ -0,0 +1,177 @@ +{ + "PackageIdentifier": "TestPublisher.FullJsonSingleton1_28", + "PackageVersion": "0.1.2.3", + "PackageLocale": "en-US", + "DefaultLocale": "en-US", + "Publisher": "TestPublisher", + "PublisherUrl": "https://fakeTestUrl.com", + "PublisherSupportUrl": "https://fakeTestUrl.com", + "PrivacyUrl": "https://fakeTestUrl.com", + "Author": "fakeAuthor", + "PackageName": "Full Singleton 1.28 Test", + "PackageUrl": "https://fakeTestUrl.com", + "License": "MIT", + "LicenseUrl": "https://fakeTestUrl.com", + "Copyright": "fakeCopyright", + "CopyrightUrl": "https://fakeTestUrl.com", + "ShortDescription": "A manifest used to verify that all fields from a full 1.28 singleton manifest can be deserialized and updated.", + "Description": "A manifest used to verify that all fields from a full 1.28 singleton manifest can be deserialized and updated.", + "Moniker": "testMoniker", + "Tags": [ + "testSingleton" + ], + "Agreements": [ + { + "AgreementLabel": "fakeAgreementLabel", + "AgreementUrl": "https://fakeTestUrl.com", + "Agreement": "fakeAgreementContent" + } + ], + "ReleaseNotes": "fakeReleaseNotes", + "ReleaseNotesUrl": "https://fakeTestUrl.com", + "PurchaseUrl": "https://fakeTestUrl.com", + "InstallationNotes": "fakeInstallationNotes", + "Documentations": [ + { + "DocumentLabel": "fakeDocumentLabel" + }, + { + "DocumentUrl": "fakeDocumentUrl" + } + ], + "Icons": [ + { + "IconUrl": "https://fakeTestIcon", + "IconFileType": "png", + "IconResolution": "32x32", + "IconTheme": "light", + "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321" + } + ], + "Installers": [ + { + "MinimumOSVersion": "10.0.0.0", + "Architecture": "x64", + "InstallerUrl": "https://fakedomain.com/WingetCreateTestExeInstaller.exe", + "InstallerType": "exe", + "InstallerSha256": "A7803233EEDB6A4B59B3024CCF9292A6FFFB94507DC998AA67C5B745D197A5DC", + "ProductCode": "FakeProductCode", + "PackageFamilyName": "FakePackageFamilyName", + "InstallModes": [ + "silent" + ], + "InstallerSwitches": { + "Silent": "/s", + "Upgrade": "/u", + "Repair": "/r" + }, + "InstallerSuccessCodes": [ + 1 + ], + "ExpectedReturnCodes": [ + { + "InstallerReturnCode": 123, + "ReturnResponse": "alreadyInstalled", + "ReturnResponseUrl": "https://fakeTestUrl.com" + } + ], + "UpgradeBehavior": "install", + "Commands": [ + "fakeCommand" + ], + "Protocols": [ + "fakeProtocol" + ], + "FileExtensions": [ + ".exe" + ], + "Dependencies": { + "WindowsFeatures": [ + "fakeValue" + ], + "WindowsLibraries": [ + "fakeValue" + ], + "PackageDependencies": [ + { + "MinimumVersion": "10.0.0.0" + } + ], + "ExternalDependencies": [ + "fakeValue" + ] + }, + "Markets": { + "AllowedMarkets": [ + "fakeAllowedMarket" + ], + "ExcludedMarkets": [ + "fakeExcludedMarket" + ] + }, + "InstallerAbortsTerminal": true, + "InstallLocationRequired": true, + "RequireExplicitUpgrade": true, + "UnsupportedOSArchitectures": [ + "arm64" + ], + "AppsAndFeaturesEntries": [ + { + "DisplayName": "testName", + "Publisher": "testPublisher", + "DisplayVersion": "testVersion", + "UpgradeCode": "fakeProductCode" + } + ], + "ElevationRequirement": "elevationRequired", + "ReleaseDate": "2022-01-01", + "DisplayInstallWarnings": true, + "UnsupportedArguments": [ + "log", + "location" + ], + "NestedInstallerFiles": [ + { + "RelativeFilePath": "RelativeFilePath", + "PortableCommandAlias": "PortableCommandAlias" + } + ], + "InstallationMetadata": { + "DefaultInstallLocation": "%ProgramFiles%\\TestApp", + "Files": [ + { + "RelativeFilePath": "main.exe", + "FileSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82", + "FileType": "launch", + "InvocationParameter": "/arg" + } + ] + }, + "DownloadCommandProhibited": true, + "RepairBehavior": "modify", + "ArchiveBinariesDependOnPath": true, + "DesiredStateConfiguration": { + "PowerShell": [ + { + "RepositoryUrl": "https://fakeTestUrl.com", + "ModuleName": "TestModule", + "Resources": [ + { + "Name": "TestResource" + } + ] + } + ], + "DSCv3": { + "Resources": [ + { + "Type": "TestPublisher/TestResource" + } + ] + } + } + } + ], + "ManifestType": "singleton", + "ManifestVersion": "1.28.0" +} \ No newline at end of file diff --git a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullYamlSingleton1_28.yaml b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullYamlSingleton1_28.yaml new file mode 100644 index 00000000..549eb97c --- /dev/null +++ b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.FullYamlSingleton1_28.yaml @@ -0,0 +1,125 @@ +PackageIdentifier: TestPublisher.FullYamlSingleton1_28 +PackageVersion: 0.1.2.3 +PackageLocale: en-US +DefaultLocale: en-US +Publisher: TestPublisher +PublisherUrl: https://fakeTestUrl.com +PublisherSupportUrl: https://fakeTestUrl.com +PrivacyUrl: https://fakeTestUrl.com +Author: fakeAuthor +PackageName: Full Singleton 1.28 Test +PackageUrl: https://fakeTestUrl.com +License: MIT +LicenseUrl: https://fakeTestUrl.com +Copyright: fakeCopyright +CopyrightUrl: https://fakeTestUrl.com +ShortDescription: A manifest used to verify that all fields from a full 1.28 singleton manifest can be deserialized and updated. +Description: A manifest used to verify that all fields from a full 1.28 singleton manifest can be deserialized and updated. +Moniker: testMoniker +Tags: +- testSingleton +Agreements: +- AgreementLabel: fakeAgreementLabel + AgreementUrl: https://fakeTestUrl.com + Agreement: fakeAgreementContent +ReleaseNotes: fakeReleaseNotes +ReleaseNotesUrl: https://fakeTestUrl.com +PurchaseUrl: https://fakeTestUrl.com +InstallationNotes: fakeInstallationNotes +Documentations: +- DocumentLabel: fakeDocumentLabel +- DocumentUrl: fakeDocumentUrl +Icons: + - IconUrl: https://fakeTestIcon + IconFileType: png + IconResolution: 32x32 + IconTheme: light + IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 +Installers: +- MinimumOSVersion: 10.0.0.0 + Architecture: x64 + InstallerUrl: https://fakedomain.com/WingetCreateTestExeInstaller.exe + InstallerType: exe + InstallerSha256: A7803233EEDB6A4B59B3024CCF9292A6FFFB94507DC998AA67C5B745D197A5DC + ProductCode: FakeProductCode + PackageFamilyName: FakePackageFamilyName + InstallModes: + - silent + InstallerSwitches: + Silent: /s + Upgrade: /u + Repair: /r + InstallerSuccessCodes: + - 1 + ExpectedReturnCodes: + - InstallerReturnCode: 123 + ReturnResponse: alreadyInstalled + ReturnResponseUrl: https://fakeTestUrl.com + UpgradeBehavior: install + Commands: + - fakeCommand + Protocols: + - fakeProtocol + FileExtensions: + - .exe + Dependencies: + WindowsFeatures: + - fakeValue + WindowsLibraries: + - fakeValue + PackageDependencies: + - MinimumVersion: 10.0.0.0 + ExternalDependencies: + - fakeValue + Markets: + AllowedMarkets: + - fakeAllowedMarket + ExcludedMarkets: + - fakeExcludedMarket + InstallerAbortsTerminal: true + InstallLocationRequired: true + RequireExplicitUpgrade: true + UnsupportedOSArchitectures: + - arm64 + AppsAndFeaturesEntries: + - DisplayName: testName + Publisher: testPublisher + DisplayVersion: testVersion + UpgradeCode: fakeProductCode + ElevationRequirement: elevationRequired + ReleaseDate: 1-1-2022 + DisplayInstallWarnings: true + UnsupportedArguments: + - log + - location + NestedInstallerFiles: + - RelativeFilePath: RelativeFilePath + PortableCommandAlias: PortableCommandAlias + InstallationMetadata: + DefaultInstallLocation: "%ProgramFiles%\\TestApp" + Files: + - RelativeFilePath: "main.exe" + FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 + FileType: launch + InvocationParameter: "/arg" + DownloadCommandProhibited: true + RepairBehavior: modify + ArchiveBinariesDependOnPath: true + DesiredStateConfiguration: + PowerShell: + - RepositoryUrl: https://fakeTestUrl.com + ModuleName: TestModule + Resources: + - Name: TestResource + DSCv3: + Resources: + - Type: TestPublisher/TestResource +# `winget validate` fails if this field is used as it requires verified publisher feature +# Uncomment when verified publisher feature is implemented +# Authentication: +# AuthenticationType: microsoftEntraId +# MicrosoftEntraIdAuthenticationInfo: +# Scope: DefaultScope +# Resource: DefaultResource +ManifestType: singleton +ManifestVersion: 1.28.0 \ No newline at end of file diff --git a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.MoveInstallerFieldsToRoot.yaml b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.MoveInstallerFieldsToRoot.yaml index cc03ea60..2af08b76 100644 --- a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.MoveInstallerFieldsToRoot.yaml +++ b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.MoveInstallerFieldsToRoot.yaml @@ -76,6 +76,20 @@ Installers: DownloadCommandProhibited: true RepairBehavior: modify ArchiveBinariesDependOnPath: true + Authentication: + AuthenticationType: microsoftEntraId + MicrosoftEntraIdAuthenticationInfo: + Resource: TestResource + Scope: TestScope + DesiredStateConfiguration: + PowerShell: + - RepositoryUrl: https://fakeTestUrl.com/repository + ModuleName: TestModule + Resources: + - Name: TestResource + DSCv3: + Resources: + - Type: TestPublisher/TestResource - Architecture: x86 InstallerType: zip InstallerUrl: https://fakedomain.com/WingetCreateTestZipInstaller.zip @@ -146,6 +160,20 @@ Installers: DownloadCommandProhibited: true RepairBehavior: modify ArchiveBinariesDependOnPath: true + Authentication: + AuthenticationType: microsoftEntraId + MicrosoftEntraIdAuthenticationInfo: + Resource: TestResource + Scope: TestScope + DesiredStateConfiguration: + PowerShell: + - RepositoryUrl: https://fakeTestUrl.com/repository + ModuleName: TestModule + Resources: + - Name: TestResource + DSCv3: + Resources: + - Type: TestPublisher/TestResource - Architecture: arm InstallerType: zip InstallerUrl: https://fakedomain.com/WingetCreateTestZipInstaller.zip @@ -217,6 +245,20 @@ Installers: DownloadCommandProhibited: true RepairBehavior: modify ArchiveBinariesDependOnPath: true + Authentication: + AuthenticationType: microsoftEntraId + MicrosoftEntraIdAuthenticationInfo: + Resource: TestResource + Scope: TestScope + DesiredStateConfiguration: + PowerShell: + - RepositoryUrl: https://fakeTestUrl.com/repository + ModuleName: TestModule + Resources: + - Name: TestResource + DSCv3: + Resources: + - Type: TestPublisher/TestResource PackageLocale: en-US ManifestType: singleton -ManifestVersion: 1.9.0 \ No newline at end of file +ManifestVersion: 1.28.0 \ No newline at end of file diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/LocalizationTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/LocalizationTests.cs index e645df4f..2ac2a275 100644 --- a/src/WingetCreateTests/WingetCreateTests/UnitTests/LocalizationTests.cs +++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/LocalizationTests.cs @@ -10,7 +10,6 @@ namespace Microsoft.WingetCreateUnitTests using System.IO; using System.Linq; using System.Reflection; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCore; using Microsoft.WingetCreateCore.Models.DefaultLocale; using Microsoft.WingetCreateCore.Models.Installer; @@ -18,6 +17,7 @@ namespace Microsoft.WingetCreateUnitTests using Microsoft.WingetCreateCore.Models.Singleton; using Microsoft.WingetCreateCore.Models.Version; using NUnit.Framework; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Test cases for verifying that strings are correctly localized. diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs index 544581c7..878195b2 100644 --- a/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs +++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs @@ -11,7 +11,6 @@ namespace Microsoft.WingetCreateUnitTests using Microsoft.WingetCreateCLI.Commands; using Microsoft.WingetCreateCLI.Logging; using Microsoft.WingetCreateCLI.Models.Settings; - using Microsoft.WingetCreateCLI.Properties; using Microsoft.WingetCreateCLI.Telemetry.Events; using Microsoft.WingetCreateCore; using Microsoft.WingetCreateCore.Common; @@ -22,6 +21,7 @@ namespace Microsoft.WingetCreateUnitTests using Microsoft.WingetCreateTests; using NUnit.Framework; using NUnit.Framework.Legacy; + using Resources = Microsoft.WingetCreateCLI.Properties.Resources; /// /// Test cases for verifying that the "update" command is working as expected. @@ -1039,6 +1039,19 @@ public async Task UpdateFullYamlSingletonVersion1_12() ClassicAssert.IsNotNull(updatedManifests, "Command should have succeeded"); } + /// + /// Ensures that all fields from the YAML Singleton v1.28 manifest can be deserialized and updated correctly. + /// + /// A representing the result of the asynchronous operation. + [Test] + public async Task UpdateFullYamlSingletonVersion1_28() + { + TestUtils.InitializeMockDownloads(TestConstants.TestZipInstaller, TestConstants.TestExeInstaller); + (UpdateCommand command, var initialManifestContent) = GetUpdateCommandAndManifestData("TestPublisher.FullYamlSingleton1_28", null, this.tempPath, null); + var updatedManifests = await RunUpdateCommand(command, initialManifestContent); + ClassicAssert.IsNotNull(updatedManifests, "Command should have succeeded"); + } + /// /// Ensures that all fields from the JSON Singleton v1.1 manifest can be deserialized and updated correctly. /// @@ -1156,6 +1169,19 @@ public async Task UpdateFullJsonSingletonVersion1_12() ClassicAssert.IsNotNull(updatedManifests, "Command should have succeeded"); } + /// + /// Ensures that all fields from the Singleton JSON v1.28 manifest can be deserialized and updated correctly. + /// + /// A representing the result of the asynchronous operation. + [Test] + public async Task UpdateFullJsonSingletonVersion1_28() + { + TestUtils.InitializeMockDownloads(TestConstants.TestZipInstaller, TestConstants.TestExeInstaller); + (UpdateCommand command, var initialManifestContent) = GetUpdateCommandAndManifestData("TestPublisher.FullJsonSingleton1_28", null, this.tempPath, null, manifestFormat: ManifestFormat.Json); + var updatedManifests = await RunUpdateCommand(command, initialManifestContent); + ClassicAssert.IsNotNull(updatedManifests, "Command should have succeeded"); + } + /// /// Ensures that version specific fields are reset after an update when using YAML manifests. /// @@ -1526,6 +1552,7 @@ public async Task MoveInstallerFieldsToRoot() ClassicAssert.IsTrue(updatedInstallerManifest.DownloadCommandProhibited == true, "DownloadCommandProhibited at the root level should be true"); ClassicAssert.IsNotNull(updatedInstallerManifest.RepairBehavior, "RepairBehavior at the root level should not be null"); ClassicAssert.IsTrue(updatedInstallerManifest.ArchiveBinariesDependOnPath == true, "ArchiveBinariesDependOnPath at the root level should be true"); + ClassicAssert.AreEqual(AuthenticationType.MicrosoftEntraId, updatedInstallerManifest.Authentication.AuthenticationType); foreach (var installer in updatedInstallerManifest.Installers) { @@ -1559,6 +1586,8 @@ public async Task MoveInstallerFieldsToRoot() ClassicAssert.IsNull(installer.DownloadCommandProhibited, "DownloadCommandProhibited at the installer level should be null"); ClassicAssert.IsNull(installer.RepairBehavior, "RepairBehavior at the installer level should be null"); ClassicAssert.IsNull(installer.ArchiveBinariesDependOnPath, "ArchiveBinariesDependOnPath at the installer level should be null"); + ClassicAssert.IsNull(installer.Authentication, "Authentication at the installer level should be null"); + ClassicAssert.IsNull(installer.DesiredStateConfiguration, "DesiredStateConfiguration at the installer level should be null"); } } @@ -1608,6 +1637,8 @@ public async Task DontMoveInstallerFieldsToRoot() ClassicAssert.IsNull(updatedInstallerManifest.DownloadCommandProhibited, "DownloadCommandProhibited at the root level should be null"); ClassicAssert.IsNull(updatedInstallerManifest.RepairBehavior, "RepairBehavior at the root level should be null"); ClassicAssert.IsNull(updatedInstallerManifest.ArchiveBinariesDependOnPath, "ArchiveBinariesDependOnPath at the root level should be null"); + ClassicAssert.IsNull(updatedInstallerManifest.Authentication, "Authentication at the root level should be null"); + ClassicAssert.IsNull(updatedInstallerManifest.DesiredStateConfiguration, "DesiredStateConfiguration at the root level should be null"); foreach (var installer in updatedInstallerManifest.Installers) { @@ -1641,6 +1672,8 @@ public async Task DontMoveInstallerFieldsToRoot() ClassicAssert.IsNotNull(installer.DownloadCommandProhibited, "DownloadCommandProhibited at the installer level should not be null"); ClassicAssert.IsNotNull(installer.RepairBehavior, "RepairBehavior at the installer level should not be null"); ClassicAssert.IsNotNull(installer.ArchiveBinariesDependOnPath, "ArchiveBinariesDependOnPath at the installer level should not be null"); + ClassicAssert.IsNotNull(installer.Authentication, "Authentication at the installer level should not be null"); + ClassicAssert.IsNotNull(installer.DesiredStateConfiguration, "DesiredStateConfiguration at the installer level should not be null"); } } diff --git a/src/WingetCreateTests/WingetCreateTests/WingetCreateTests.csproj b/src/WingetCreateTests/WingetCreateTests/WingetCreateTests.csproj index 8b49b3f3..8004aee9 100644 --- a/src/WingetCreateTests/WingetCreateTests/WingetCreateTests.csproj +++ b/src/WingetCreateTests/WingetCreateTests/WingetCreateTests.csproj @@ -67,9 +67,15 @@ PreserveNewest + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest From a7f753e12e24bffe3a0da7fb4e43f4a3a3c0ae6c Mon Sep 17 00:00:00 2001 From: Muhammad Danish <88161975+mdanish-kh@users.noreply.github.com> Date: Sat, 24 Oct 2026 06:20:43 +0500 Subject: [PATCH 2/3] address review comment --- .../WingetCreateTests/UnitTests/UpdateCommandTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs index 878195b2..5006226e 100644 --- a/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs +++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs @@ -1553,6 +1553,11 @@ public async Task MoveInstallerFieldsToRoot() ClassicAssert.IsNotNull(updatedInstallerManifest.RepairBehavior, "RepairBehavior at the root level should not be null"); ClassicAssert.IsTrue(updatedInstallerManifest.ArchiveBinariesDependOnPath == true, "ArchiveBinariesDependOnPath at the root level should be true"); ClassicAssert.AreEqual(AuthenticationType.MicrosoftEntraId, updatedInstallerManifest.Authentication.AuthenticationType); + ClassicAssert.IsNotNull(updatedInstallerManifest.DesiredStateConfiguration, "DesiredStateConfiguration at the root level should not be null"); + ClassicAssert.AreEqual("https://fakeTestUrl.com/repository", updatedInstallerManifest.DesiredStateConfiguration.PowerShell.Single().RepositoryUrl); + ClassicAssert.AreEqual("TestModule", updatedInstallerManifest.DesiredStateConfiguration.PowerShell.Single().ModuleName); + ClassicAssert.AreEqual("TestResource", updatedInstallerManifest.DesiredStateConfiguration.PowerShell.Single().Resources.Single().Name); + ClassicAssert.AreEqual("TestPublisher/TestResource", updatedInstallerManifest.DesiredStateConfiguration.DSCv3.Resources.Single().Type); foreach (var installer in updatedInstallerManifest.Installers) { From 8b657111945946e63f3ff936ddfc5b11cef7b484 Mon Sep 17 00:00:00 2001 From: Muhammad Danish <88161975+mdanish-kh@users.noreply.github.com> Date: Tue, 27 Oct 2026 00:19:47 +0500 Subject: [PATCH 3/3] handle nested schemas for interactive mode edits --- src/WingetCreateCLI/PromptHelper.cs | 77 ++++++----- .../UnitTests/PromptHelperTests.cs | 123 ++++++++++++++++++ 2 files changed, 165 insertions(+), 35 deletions(-) create mode 100644 src/WingetCreateTests/WingetCreateTests/UnitTests/PromptHelperTests.cs diff --git a/src/WingetCreateCLI/PromptHelper.cs b/src/WingetCreateCLI/PromptHelper.cs index 5a04e677..33d0504b 100644 --- a/src/WingetCreateCLI/PromptHelper.cs +++ b/src/WingetCreateCLI/PromptHelper.cs @@ -490,7 +490,7 @@ private static List GenerateInstallerSelectionList(List insta var installerTuple = string.Join(" | ", new[] { installer.Architecture.ToEnumAttributeValue(), - installer.InstallerType.ToEnumAttributeValue(), + installer.InstallerType?.ToEnumAttributeValue(), installer.Scope?.ToEnumAttributeValue(), installer.InstallerLocale, installer.InstallerUrl, @@ -515,56 +515,63 @@ private static List GenerateInstallerSelectionList(List insta private static void ApplyChangesToIndividualInstallers(Installer installerCopy, List installers) { // Skip architecture as the default value when instantiated is x86. - var modifiedFields = installerCopy.GetType().GetProperties() - .Select(prop => prop) - .Where(pi => - pi.GetValue(installerCopy) != null && - pi.Name != nameof(Installer.Architecture) && - pi.Name != nameof(Installer.AdditionalProperties)); - - foreach (var field in modifiedFields) + foreach (var property in typeof(Installer).GetProperties().Where(p => + p.Name != nameof(Installer.Architecture) && p.Name != nameof(Installer.AdditionalProperties))) { - foreach (Installer installer in installers) + var value = property.GetValue(installerCopy); + if (value != null) { - var fieldValue = field.GetValue(installerCopy); - var prop = installer.GetType().GetProperty(field.Name); - if (prop.PropertyType.IsValueType) + foreach (var installer in installers) { - prop.SetValue(installer, fieldValue); + property.SetValue(installer, ApplyPropertyChanges(value, property.GetValue(installer))); } - else if (fieldValue is IList list) + } + } + } + + private static object ApplyPropertyChanges(object value, object target) + { + var pending = new Queue<(object Source, object Target)>(); + var result = CopyValue(value, target); + while (pending.Count > 0) + { + var (source, destination) = pending.Dequeue(); + if (source is IList list) + { + foreach (var item in list) { - prop.SetValue(installer, list.DeepClone()); + ((IList)destination).Add(CopyValue(item, null)); } - else if (fieldValue is Dependencies dependencies) + } + else + { + foreach (var property in source.GetType().GetProperties().Where(p => p.CanWrite && p.Name != nameof(Installer.AdditionalProperties))) { - ApplyDependencyChangesToInstaller(dependencies, installer); + var propertyValue = property.GetValue(source); + if (propertyValue != null) + { + property.SetValue(destination, CopyValue(propertyValue, property.GetValue(destination))); + } } } } - } - /// - /// Clones any non-null property values of the dependencies object and assigns them to the provided installer object. - /// - /// Dependencies object with new values. - /// Installer object to assign new changes to. - private static void ApplyDependencyChangesToInstaller(Dependencies dependencies, Installer installer) - { - var modifiedFields = dependencies.GetType().GetProperties() - .Select(prop => prop) - .Where(pi => pi.GetValue(dependencies) != null); + return result; - foreach (var field in modifiedFields.Where(f => f.Name != nameof(Installer.AdditionalProperties))) + object CopyValue(object source, object destination) { - var fieldValue = field.GetValue(dependencies); - installer.Dependencies ??= new Dependencies(); - var prop = installer.Dependencies.GetType().GetProperty(field.Name); + if (source == null || source.GetType().IsValueType || source is string) + { + return source; + } - if (fieldValue is IList list) + if (source is IList || destination == null) { - prop.SetValue(installer.Dependencies, list.DeepClone()); + destination = Activator.CreateInstance(source.GetType()); } + + pending.Enqueue((source, destination)); + return destination; } } diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/PromptHelperTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/PromptHelperTests.cs new file mode 100644 index 00000000..e1c55c81 --- /dev/null +++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/PromptHelperTests.cs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.WingetCreateUnitTests +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using Microsoft.WingetCreateCLI; + using Microsoft.WingetCreateCore.Models.Installer; + using Newtonsoft.Json.Linq; + using NUnit.Framework; + + /// + /// Tests for applying interactive installer edits. + /// + public class PromptHelperTests + { + /// + /// Verifies partial edits preserve existing fields and replace lists without sharing their elements. + /// + [Test] + public void ApplyPartialChangesToAllInstallers() + { + var edits = new Installer + { + InstallerSwitches = new InstallerSwitches { Silent = "new" }, + AppsAndFeaturesEntries = new List { new AppsAndFeaturesEntry { DisplayName = "new" } }, + InstallerAbortsTerminal = false, + }; + var installers = new List + { + new Installer { Architecture = Architecture.X64, InstallerSwitches = new InstallerSwitches { Silent = "old", Custom = "keep" }, InstallerAbortsTerminal = true }, + new Installer { Architecture = Architecture.Arm64 }, + }; + typeof(PromptHelper).GetMethod("ApplyChangesToIndividualInstallers", BindingFlags.NonPublic | BindingFlags.Static) + .Invoke(null, new object[] { edits, installers }); + + Assert.That(installers[0].InstallerSwitches.Custom, Is.EqualTo("keep")); + Assert.That(installers[0].InstallerSwitches.Silent, Is.EqualTo("new")); + Assert.That(installers[0].InstallerAbortsTerminal, Is.False); + Assert.That(installers[0].Architecture, Is.EqualTo(Architecture.X64)); + Assert.That(installers[1].Architecture, Is.EqualTo(Architecture.Arm64)); + installers[0].AppsAndFeaturesEntries[0].DisplayName = "changed again"; + Assert.That(installers[1].AppsAndFeaturesEntries[0].DisplayName, Is.EqualTo("new")); + Assert.That(edits.AppsAndFeaturesEntries[0].DisplayName, Is.EqualTo("new")); + } + + /// + /// Verifies every editable field, including nested properties, reaches each installer. + /// + /// The editable installer property. + [TestCaseSource(nameof(EditableInstallerProperties))] + public void ApplyChangesToAllInstallers(PropertyInfo property) + { + var edits = new Installer(); + property.SetValue(edits, CreateValue(property.PropertyType)); + var expected = JToken.FromObject(property.GetValue(edits)); + var installers = new List { new Installer(), new Installer() }; + + typeof(PromptHelper).GetMethod("ApplyChangesToIndividualInstallers", BindingFlags.NonPublic | BindingFlags.Static) + .Invoke(null, new object[] { edits, installers }); + + foreach (var installer in installers) + { + var actual = property.GetValue(installer); + Assert.That(actual, Is.Not.Null, property.Name); + Assert.That(JToken.DeepEquals(expected, JToken.FromObject(actual)), Is.True, property.Name); + if (!property.PropertyType.IsValueType && property.PropertyType != typeof(string)) + { + Assert.That(actual, Is.Not.SameAs(property.GetValue(edits)), property.Name); + } + } + } + + private static IEnumerable EditableInstallerProperties() + { + var properties = typeof(Installer).GetProperties(); + var names = (List)typeof(PromptHelper) + .GetMethod("FilterPropertiesAndCreateSelectionList", BindingFlags.NonPublic | BindingFlags.Static) + .MakeGenericMethod(typeof(Installer)) + .Invoke(null, new object[] { new Installer(), properties }); + return properties.Where(p => names.Contains(p.Name) && p.Name != nameof(Installer.Architecture)) + .Select(p => new TestCaseData(p).SetArgDisplayNames(p.Name)); + } + + private static object CreateValue(Type type) + { + type = Nullable.GetUnderlyingType(type) ?? type; + if (type == typeof(string)) + { + return "test"; + } + + if (type.IsEnum) + { + return Enum.GetValues(type).GetValue(0); + } + + if (type.IsValueType) + { + return Convert.ChangeType(1, type); + } + + var value = Activator.CreateInstance(type); + if (value is IList list) + { + list.Add(CreateValue(type.GetGenericArguments()[0])); + } + else + { + foreach (var property in type.GetProperties().Where(p => p.CanWrite && p.Name != nameof(Installer.AdditionalProperties))) + { + property.SetValue(value, CreateValue(property.PropertyType)); + } + } + + return value; + } + } +}