From 4e4d3c05a14c5eb6a4956226151a0b7c41e3924d Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 10 Jun 2025 13:10:37 -0700
Subject: [PATCH 01/24] WIP
---
src/WingetCreateCLI/Commands/BaseCommand.cs | 5 +
src/WingetCreateCLI/Commands/CacheCommand.cs | 3 +
src/WingetCreateCLI/Commands/DscCommand.cs | 118 ++++++++++++++++++
.../Commands/DscCommands/BaseDscCommand.cs | 36 ++++++
.../DscCommands/DscSettingsCommand.cs | 67 ++++++++++
src/WingetCreateCLI/Commands/InfoCommand.cs | 3 +
.../Commands/SettingsCommand.cs | 3 +
src/WingetCreateCLI/Program.cs | 5 +-
src/WingetCreateCLI/UserSettings.cs | 5 +
9 files changed, 242 insertions(+), 3 deletions(-)
create mode 100644 src/WingetCreateCLI/Commands/DscCommand.cs
create mode 100644 src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
create mode 100644 src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
diff --git a/src/WingetCreateCLI/Commands/BaseCommand.cs b/src/WingetCreateCLI/Commands/BaseCommand.cs
index 61d3875e..1099dd10 100644
--- a/src/WingetCreateCLI/Commands/BaseCommand.cs
+++ b/src/WingetCreateCLI/Commands/BaseCommand.cs
@@ -61,6 +61,11 @@ public abstract class BaseCommand
///
public static string Extension => Serialization.ManifestSerializer.AssociatedFileExtension;
+ ///
+ /// Gets a value indicating whether or not the command requires a GitHub token to be set.
+ ///
+ public virtual bool RequiresGitHubToken { get; } = true;
+
///
/// Gets or sets the GitHub token used to submit a pull request on behalf of the user.
///
diff --git a/src/WingetCreateCLI/Commands/CacheCommand.cs b/src/WingetCreateCLI/Commands/CacheCommand.cs
index b30298ad..dfbee690 100644
--- a/src/WingetCreateCLI/Commands/CacheCommand.cs
+++ b/src/WingetCreateCLI/Commands/CacheCommand.cs
@@ -37,6 +37,9 @@ public class CacheCommand : BaseCommand
///
[Option('o', "open", Required = true, SetName = nameof(Open), HelpText = "Open_HelpText", ResourceType = typeof(Resources))]
public bool Open { get; set; }
+
+ ///
+ public override bool RequiresGitHubToken => false;
///
/// Executes the cache command flow.
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
new file mode 100644
index 00000000..aa19dd33
--- /dev/null
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -0,0 +1,118 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateCLI.Commands;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using CommandLine;
+using Microsoft.WingetCreateCLI.Commands.DscCommands;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+///
+/// Command for managin the application using dsc v3.
+///
+[Verb("dsc", HelpText = "Manage the application using dsc v3.")]
+public class DscCommand : BaseCommand
+{
+ ///
+ public override bool RequiresGitHubToken => false;
+
+ ///
+ /// Gets or sets the unbound arguments that exist after the positional parameters.
+ ///
+ [Value(2, Hidden = true)]
+ public IList UnboundArgs { get; set; } = new List();
+
+ ///
+ /// Gets or sets the input for the dsc Get operation.
+ ///
+ [Option('g', "get", SetName = "GetMethod", HelpText = "Command for the Get flow.")]
+ public string Get { get; set; }
+
+ ///
+ /// Gets or sets the input for the dsc Set operation.
+ ///
+ [Option('s', "set", SetName = "SetMethod", HelpText = "Command for the Set flow.")]
+ public string Set { get; set; }
+
+ ///
+ /// Gets or sets the input for the dsc Test operation.
+ ///
+ [Option('t', "test", SetName = "TestMethod", HelpText = "Command for the Test flow.")]
+ public string Test { get; set; }
+
+ ///
+ /// Gets or sets the input for the dsc Export operation.
+ ///
+ [Option('e', "export", SetName = "ExportMethod", HelpText = "Command for the Export flow.")]
+ public string Export { get; set; }
+
+ ///
+ /// Executes the dsc command flow.
+ ///
+ /// Boolean representing success or fail of the command.
+ public override async Task Execute()
+ {
+ BaseDscCommand dscCommand;
+ var dscScope = this.UnboundArgs.FirstOrDefault()?.ToLowerInvariant() ?? string.Empty;
+ if (dscScope == DscSettingsCommand.CommandName)
+ {
+ dscCommand = new DscSettingsCommand();
+ }
+ else
+ {
+ Console.WriteLine($"Unknown DSC scope: {dscScope}");
+ return false;
+ }
+
+ JToken input;
+ if (this.TryParse(this.Get, out input))
+ {
+ dscCommand.Get(input);
+ }
+ else if (this.TryParse(this.Set, out input))
+ {
+ dscCommand.Set(input);
+ }
+ else if (this.TryParse(this.Test, out input))
+ {
+ dscCommand.Test(input);
+ }
+ else if (this.TryParse(this.Export, out input))
+ {
+ dscCommand.Export(input);
+ }
+ else
+ {
+ Console.WriteLine("No valid DSC command provided. Use -g, -s, -t, or -e to specify a command.");
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool TryParse(string json, out JToken token)
+ {
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ token = null;
+ return false;
+ }
+
+ try
+ {
+ token = JToken.Parse(json);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error parsing JSON: {ex.Message}");
+ token = null;
+ return false;
+ }
+ }
+}
diff --git a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
new file mode 100644
index 00000000..c7c7507d
--- /dev/null
+++ b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
+
+using Newtonsoft.Json.Linq;
+
+///
+/// Base class for DSC commands.
+///
+public abstract class BaseDscCommand
+{
+ ///
+ /// DSC Get command.
+ ///
+ /// Input for the Get command.
+ public abstract void Get(JToken input);
+
+ ///
+ /// DSC Set command.
+ ///
+ /// Input for the Set command.
+ public abstract void Set(JToken input);
+
+ ///
+ /// DSC Test command.
+ ///
+ /// Input for the Test command.
+ public abstract void Test(JToken input);
+
+ ///
+ /// DSC Export command.
+ ///
+ /// Input for the Export command.
+ public abstract void Export(JToken input);
+}
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
new file mode 100644
index 00000000..7585335d
--- /dev/null
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
+
+using System;
+using Microsoft.WingetCreateCLI.Models.Settings;
+using Newtonsoft.Json.Linq;
+
+///
+/// Command for managing the settings using dsc v3.
+///
+public class DscSettingsCommand : BaseDscCommand
+{
+ ///
+ /// Represents the name of the command used to access settings functionality.
+ ///
+ public const string CommandName = "settings";
+
+ ///
+ public override void Get(JToken input)
+ {
+ Console.WriteLine(UserSettings.ToJson());
+ }
+
+ ///
+ public override void Set(JToken input)
+ {
+ // No-op
+ }
+
+ ///
+ public override void Test(JToken input)
+ {
+ // No-op
+ }
+
+ ///
+ public override void Export(JToken input)
+ {
+ // No-op
+ }
+
+ private class UserSettingsFunctionData
+ {
+ public enum ActionType
+ {
+ Partial,
+ Full,
+ }
+
+ public UserSettingsFunctionData(JToken token)
+ {
+
+ }
+
+ ///
+ /// Gets or sets the action type for the settings command.
+ ///
+ public ActionType Action { get; set; } = ActionType.Partial;
+
+ ///
+ /// Gets or sets the settings manifest to be used for the command.
+ ///
+ public SettingsManifest Settings { get; set; }
+ }
+}
diff --git a/src/WingetCreateCLI/Commands/InfoCommand.cs b/src/WingetCreateCLI/Commands/InfoCommand.cs
index b4441e72..2fe5f8fb 100644
--- a/src/WingetCreateCLI/Commands/InfoCommand.cs
+++ b/src/WingetCreateCLI/Commands/InfoCommand.cs
@@ -20,6 +20,9 @@ namespace Microsoft.WingetCreateCLI.Commands
[Verb("info", HelpText = "InfoCommand_HelpText", ResourceType = typeof(Resources))]
public class InfoCommand : BaseCommand
{
+ ///
+ public override bool RequiresGitHubToken => false;
+
///
/// Executes the info command flow.
///
diff --git a/src/WingetCreateCLI/Commands/SettingsCommand.cs b/src/WingetCreateCLI/Commands/SettingsCommand.cs
index 821de4aa..28025699 100644
--- a/src/WingetCreateCLI/Commands/SettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/SettingsCommand.cs
@@ -22,6 +22,9 @@ namespace Microsoft.WingetCreateCLI.Commands
[Verb("settings", HelpText = "SettingsCommand_HelpText", ResourceType = typeof(Resources))]
public class SettingsCommand : BaseCommand
{
+ ///
+ public override bool RequiresGitHubToken => false;
+
///
/// Executes the token command flow.
///
diff --git a/src/WingetCreateCLI/Program.cs b/src/WingetCreateCLI/Program.cs
index 9cb7e5d5..f2871bdf 100644
--- a/src/WingetCreateCLI/Program.cs
+++ b/src/WingetCreateCLI/Program.cs
@@ -52,6 +52,7 @@ private static async Task Main(string[] args)
typeof(CacheCommand),
typeof(ShowCommand),
typeof(InfoCommand),
+ typeof(DscCommand),
};
var parserResult = myParser.ParseArguments(args, types);
@@ -70,10 +71,8 @@ private static async Task Main(string[] args)
Logger.WarnLocalized(nameof(Resources.GitHubTokenWarning_Message));
}
- bool commandHandlesToken = command is not CacheCommand and not InfoCommand and not SettingsCommand;
-
// Do not load github client for commands that do not deal with a GitHub token.
- if (commandHandlesToken)
+ if (command.RequiresGitHubToken)
{
if (await command.LoadGitHubClient())
{
diff --git a/src/WingetCreateCLI/UserSettings.cs b/src/WingetCreateCLI/UserSettings.cs
index 0ff37970..cc5b38c4 100644
--- a/src/WingetCreateCLI/UserSettings.cs
+++ b/src/WingetCreateCLI/UserSettings.cs
@@ -230,6 +230,11 @@ private static void LoadSettings()
Visual = new Visual(),
};
}
+ }
+
+ public static string ToJson()
+ {
+ return JsonConvert.SerializeObject(Settings, Formatting.None);
}
}
}
From f10d9bbcb41c907c497488eb2f80f99a9b76fd03 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Wed, 11 Jun 2025 16:41:40 -0700
Subject: [PATCH 02/24] Init draft
---
src/WingetCreateCLI/Commands/DscCommand.cs | 1 -
.../Commands/DscCommands/BaseDscCommand.cs | 11 ++
.../DscCommands/DscSettingsCommand.cs | 135 ++++++++++++++++--
src/WingetCreateCLI/UserSettings.cs | 37 +++--
4 files changed, 159 insertions(+), 25 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index aa19dd33..259a96f3 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -9,7 +9,6 @@ namespace Microsoft.WingetCreateCLI.Commands;
using System.Threading.Tasks;
using CommandLine;
using Microsoft.WingetCreateCLI.Commands.DscCommands;
-using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
///
diff --git a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
index c7c7507d..f8840336 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
@@ -3,6 +3,8 @@
namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
+using System;
+using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
///
@@ -33,4 +35,13 @@ public abstract class BaseDscCommand
///
/// Input for the Export command.
public abstract void Export(JToken input);
+
+ ///
+ /// Writes a JSON output line to the console.
+ ///
+ /// The JSON token to be written.
+ protected void WriteJsonOutputLine(JToken token)
+ {
+ Console.WriteLine(token.ToString(Formatting.None));
+ }
}
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
index 7585335d..8b98874c 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -4,7 +4,9 @@
namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
using System;
+using System.Diagnostics;
using Microsoft.WingetCreateCLI.Models.Settings;
+using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
///
@@ -20,48 +22,157 @@ public class DscSettingsCommand : BaseDscCommand
///
public override void Get(JToken input)
{
- Console.WriteLine(UserSettings.ToJson());
+ this.Export(input);
}
///
public override void Set(JToken input)
{
- // No-op
+ var data = new UserSettingsFunctionData(input);
+ data.Get();
+
+ // Capture the diff before updating the output
+ var diff = data.DiffJson();
+
+ if (!data.Test())
+ {
+ data.Output.Settings = data.GetResolvedInput();
+ data.WriteOutput();
+ }
+
+ this.WriteJsonOutputLine(data.Output.ToJson());
+ this.WriteJsonOutputLine(diff);
}
///
public override void Test(JToken input)
{
- // No-op
+ var data = new UserSettingsFunctionData(input);
+
+ data.Get();
+ data.Output.InDesiredState = data.Test();
+
+ this.WriteJsonOutputLine(data.Output.ToJson());
+ this.WriteJsonOutputLine(data.DiffJson());
}
///
public override void Export(JToken input)
{
- // No-op
+ var data = new UserSettingsFunctionData(input);
+
+ data.Get();
+
+ this.WriteJsonOutputLine(data.Output.ToJson());
}
private class UserSettingsFunctionData
{
- public enum ActionType
+ private JObject resolvedInputUserSettings;
+ private JObject userSettings;
+
+ public UserSettingsFunctionData(JToken json = null)
+ {
+ this.Input = json == null ? new() : json.ToObject();
+ this.Output = new();
+ }
+
+ public UserSettingsResourceObject Input { get; }
+
+ public UserSettingsResourceObject Output { get; }
+
+ public void Get()
+ {
+ this.Output.Settings = this.GetUserSettings();
+ }
+
+ public bool Test()
+ {
+ return JToken.DeepEquals(this.GetResolvedInput(), this.GetValidSettings(this.Output.Settings));
+ }
+
+ public JArray DiffJson()
+ {
+ var diff = new JArray();
+ if (!this.Test())
+ {
+ diff.Add("settings");
+ }
+
+ return diff;
+ }
+
+ public JObject GetResolvedInput()
{
- Partial,
- Full,
+ Debug.Assert(this.Input.Settings != null, "Input settings should not be null.");
+ if (this.resolvedInputUserSettings == null)
+ {
+ if (UserSettingsResourceObject.ActionFull.Equals(this.Input.Action, StringComparison.OrdinalIgnoreCase))
+ {
+ this.Output.Action = UserSettingsResourceObject.ActionFull;
+ this.resolvedInputUserSettings = this.GetValidSettings(this.Input.Settings);
+ }
+ else
+ {
+ this.Output.Action = UserSettingsResourceObject.ActionPartial;
+ var mergedSettings = this.GetUserSettings();
+ mergedSettings.Merge(this.Input.Settings);
+ this.resolvedInputUserSettings = this.GetValidSettings(mergedSettings);
+ }
+ }
+
+ return this.resolvedInputUserSettings;
}
- public UserSettingsFunctionData(JToken token)
+ public JObject GetUserSettings()
{
+ this.userSettings ??= UserSettings.ToJson();
+ return (JObject)this.userSettings.DeepClone();
+ }
+ public void WriteOutput()
+ {
+ Debug.Assert(this.Output.Settings != null, "Output settings should not be null.");
+ UserSettings.SaveSettings(this.Output.Settings.ToObject());
}
///
- /// Gets or sets the action type for the settings command.
+ /// Validates and converts the provided settings into a structured format.
///
- public ActionType Action { get; set; } = ActionType.Partial;
+ /// An object containing settings to be validated.
+ /// An object representing the validated settings.
+ public JObject GetValidSettings(JObject settings)
+ {
+ var settingsManifest = settings.ToObject();
+ return JObject.FromObject(settingsManifest);
+ }
+ }
+
+ private class UserSettingsResourceObject
+ {
+ public const string ActionFull = "Full";
+ public const string ActionPartial = "Partial";
+
+ // TODO Make this required
+ [JsonProperty("settings")]
+ public JObject Settings { get; set; }
+
+ [JsonProperty("action", NullValueHandling = NullValueHandling.Ignore)]
+ public string Action { get; set; }
+
+ [JsonProperty("_inDesiredState", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? InDesiredState { get; set; }
///
- /// Gets or sets the settings manifest to be used for the command.
+ /// Converts the current object to a JSON representation.
///
- public SettingsManifest Settings { get; set; }
+ /// A Json object representing the current object.
+ public JObject ToJson()
+ {
+ return JObject.FromObject(this, new JsonSerializer
+ {
+ NullValueHandling = NullValueHandling.Ignore,
+ });
+ }
}
}
diff --git a/src/WingetCreateCLI/UserSettings.cs b/src/WingetCreateCLI/UserSettings.cs
index cc5b38c4..5af01e26 100644
--- a/src/WingetCreateCLI/UserSettings.cs
+++ b/src/WingetCreateCLI/UserSettings.cs
@@ -4,13 +4,15 @@
namespace Microsoft.WingetCreateCLI
{
using System;
- using System.Collections.Generic;
+ using System.Collections.Generic;
+ using System.Diagnostics;
using System.IO;
using Microsoft.WingetCreateCLI.Logging;
using Microsoft.WingetCreateCLI.Models.Settings;
using Microsoft.WingetCreateCLI.Properties;
- using Newtonsoft.Json;
-
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Linq;
+
///
/// UserSettings configuration class for WingetCreate.
///
@@ -185,12 +187,28 @@ public static void FirstRunTelemetryConsent()
///
/// Saves the current settings configurations to the settings.json file.
- ///
- public static void SaveSettings()
- {
+ ///
+ /// Optional settings manifest to save. If null, the current settings will be saved.
+ public static void SaveSettings(SettingsManifest settings = null)
+ {
+ if (settings != null)
+ {
+ Settings = settings;
+ }
+
+ Debug.Assert(Settings != null, "Settings should not be null when saving settings.");
string output = JsonConvert.SerializeObject(Settings, Formatting.Indented);
File.WriteAllText(SettingsJsonPath, output);
- }
+ }
+
+ ///
+ /// Gets the current settings as a Json object.
+ ///
+ /// A Json object representing the current settings.
+ public static JObject ToJson()
+ {
+ return JObject.FromObject(Settings);
+ }
///
/// Loads the correct settings file based on the following order.
@@ -231,10 +249,5 @@ private static void LoadSettings()
};
}
}
-
- public static string ToJson()
- {
- return JsonConvert.SerializeObject(Settings, Formatting.None);
- }
}
}
From 9113d1e714a4415c1ca9e6657d1ea45b011b0ba0 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Wed, 11 Jun 2025 17:26:58 -0700
Subject: [PATCH 03/24] Move files
---
.../DscCommands/DscSettingsCommand.cs | 121 +---------------
.../Models/DscModels/BaseResourceObject.cs | 18 +++
.../Models/DscModels/SettingsFunctionData.cs | 134 ++++++++++++++++++
.../DscModels/SettingsResourceObject.cs | 57 ++++++++
src/WingetCreateCLI/WingetCreateCLI.csproj | 4 -
5 files changed, 213 insertions(+), 121 deletions(-)
create mode 100644 src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
create mode 100644 src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs
create mode 100644 src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
index 8b98874c..98ee9a71 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -3,10 +3,7 @@
namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
-using System;
-using System.Diagnostics;
-using Microsoft.WingetCreateCLI.Models.Settings;
-using Newtonsoft.Json;
+using Microsoft.WingetCreateCLI.Models.DscModels;
using Newtonsoft.Json.Linq;
///
@@ -28,7 +25,7 @@ public override void Get(JToken input)
///
public override void Set(JToken input)
{
- var data = new UserSettingsFunctionData(input);
+ var data = new SettingsFunctionData(input);
data.Get();
// Capture the diff before updating the output
@@ -47,7 +44,7 @@ public override void Set(JToken input)
///
public override void Test(JToken input)
{
- var data = new UserSettingsFunctionData(input);
+ var data = new SettingsFunctionData(input);
data.Get();
data.Output.InDesiredState = data.Test();
@@ -59,120 +56,10 @@ public override void Test(JToken input)
///
public override void Export(JToken input)
{
- var data = new UserSettingsFunctionData(input);
+ var data = new SettingsFunctionData();
data.Get();
this.WriteJsonOutputLine(data.Output.ToJson());
}
-
- private class UserSettingsFunctionData
- {
- private JObject resolvedInputUserSettings;
- private JObject userSettings;
-
- public UserSettingsFunctionData(JToken json = null)
- {
- this.Input = json == null ? new() : json.ToObject();
- this.Output = new();
- }
-
- public UserSettingsResourceObject Input { get; }
-
- public UserSettingsResourceObject Output { get; }
-
- public void Get()
- {
- this.Output.Settings = this.GetUserSettings();
- }
-
- public bool Test()
- {
- return JToken.DeepEquals(this.GetResolvedInput(), this.GetValidSettings(this.Output.Settings));
- }
-
- public JArray DiffJson()
- {
- var diff = new JArray();
- if (!this.Test())
- {
- diff.Add("settings");
- }
-
- return diff;
- }
-
- public JObject GetResolvedInput()
- {
- Debug.Assert(this.Input.Settings != null, "Input settings should not be null.");
- if (this.resolvedInputUserSettings == null)
- {
- if (UserSettingsResourceObject.ActionFull.Equals(this.Input.Action, StringComparison.OrdinalIgnoreCase))
- {
- this.Output.Action = UserSettingsResourceObject.ActionFull;
- this.resolvedInputUserSettings = this.GetValidSettings(this.Input.Settings);
- }
- else
- {
- this.Output.Action = UserSettingsResourceObject.ActionPartial;
- var mergedSettings = this.GetUserSettings();
- mergedSettings.Merge(this.Input.Settings);
- this.resolvedInputUserSettings = this.GetValidSettings(mergedSettings);
- }
- }
-
- return this.resolvedInputUserSettings;
- }
-
- public JObject GetUserSettings()
- {
- this.userSettings ??= UserSettings.ToJson();
- return (JObject)this.userSettings.DeepClone();
- }
-
- public void WriteOutput()
- {
- Debug.Assert(this.Output.Settings != null, "Output settings should not be null.");
- UserSettings.SaveSettings(this.Output.Settings.ToObject());
- }
-
- ///
- /// Validates and converts the provided settings into a structured format.
- ///
- /// An object containing settings to be validated.
- /// An object representing the validated settings.
- public JObject GetValidSettings(JObject settings)
- {
- var settingsManifest = settings.ToObject();
- return JObject.FromObject(settingsManifest);
- }
- }
-
- private class UserSettingsResourceObject
- {
- public const string ActionFull = "Full";
- public const string ActionPartial = "Partial";
-
- // TODO Make this required
- [JsonProperty("settings")]
- public JObject Settings { get; set; }
-
- [JsonProperty("action", NullValueHandling = NullValueHandling.Ignore)]
- public string Action { get; set; }
-
- [JsonProperty("_inDesiredState", NullValueHandling = NullValueHandling.Ignore)]
- public bool? InDesiredState { get; set; }
-
- ///
- /// Converts the current object to a JSON representation.
- ///
- /// A Json object representing the current object.
- public JObject ToJson()
- {
- return JObject.FromObject(this, new JsonSerializer
- {
- NullValueHandling = NullValueHandling.Ignore,
- });
- }
- }
}
diff --git a/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
new file mode 100644
index 00000000..b4bcd070
--- /dev/null
+++ b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateCLI.Models.DscModels;
+
+using Newtonsoft.Json;
+
+///
+/// Represents a base resource object with a property indicating whether the resource is in its desired state.
+///
+public class BaseResourceObject
+{
+ ///
+ /// Gets or sets a value indicating whether the resource is in its desired state.
+ ///
+ [JsonProperty("_inDesiredState", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? InDesiredState { get; set; }
+}
diff --git a/src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs b/src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs
new file mode 100644
index 00000000..eb11047b
--- /dev/null
+++ b/src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs
@@ -0,0 +1,134 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateCLI.Models.DscModels;
+
+using System;
+using System.Diagnostics;
+using Microsoft.WingetCreateCLI.Models.Settings;
+using Newtonsoft.Json.Linq;
+
+///
+/// Represents the data structure for settings functionality in DSC operations.
+///
+public class SettingsFunctionData
+{
+ private JObject resolvedInputUserSettings;
+ private JObject userSettings;
+
+ ///
+ /// Initializes a new instance of the class with default settings.
+ ///
+ public SettingsFunctionData()
+ : this(null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An optional JSON token used to initialize the input settings.
+ public SettingsFunctionData(JToken json = null)
+ {
+ this.Output = new();
+ this.Input = json == null ? new() : json.ToObject();
+ }
+
+ ///
+ /// Gets the input settings resource object.
+ ///
+ public SettingsResourceObject Input { get; }
+
+ ///
+ /// Gets the output settings resource object.
+ ///
+ public SettingsResourceObject Output { get; }
+
+ ///
+ /// Loads the user settings into the output object.
+ ///
+ public void Get()
+ {
+ this.Output.Settings = this.GetUserSettings();
+ }
+
+ ///
+ /// Gets whether the resolved input settings and output settings are equivalent.
+ ///
+ /// >true if the settings are equivalent; otherwise, false.
+ public bool Test()
+ {
+ return JToken.DeepEquals(this.GetResolvedInput(), this.GetValidSettings(this.Output.Settings));
+ }
+
+ ///
+ /// Gets the differences between the current settings and the input settings.
+ ///
+ /// A Json array containing the differences.
+ public JArray DiffJson()
+ {
+ var diff = new JArray();
+ if (!this.Test())
+ {
+ diff.Add("settings");
+ }
+
+ return diff;
+ }
+
+ ///
+ /// Gets the resolved input settings based on the action specified in the input.
+ ///
+ /// >A Json object representing the resolved input settings.
+ public JObject GetResolvedInput()
+ {
+ Debug.Assert(this.Input.Settings != null, "Input settings should not be null.");
+ if (this.resolvedInputUserSettings == null)
+ {
+ if (SettingsResourceObject.ActionFull.Equals(this.Input.Action, StringComparison.OrdinalIgnoreCase))
+ {
+ this.Output.Action = SettingsResourceObject.ActionFull;
+ this.resolvedInputUserSettings = this.GetValidSettings(this.Input.Settings);
+ }
+ else
+ {
+ this.Output.Action = SettingsResourceObject.ActionPartial;
+ var mergedSettings = this.GetUserSettings();
+ mergedSettings.Merge(this.Input.Settings);
+ this.resolvedInputUserSettings = this.GetValidSettings(mergedSettings);
+ }
+ }
+
+ return this.resolvedInputUserSettings;
+ }
+
+ ///
+ /// Retrieves a deep-cloned JSON representation of the current user settings.
+ ///
+ /// A Json object representing the user settings.
+ public JObject GetUserSettings()
+ {
+ this.userSettings ??= UserSettings.ToJson();
+ return (JObject)this.userSettings.DeepClone();
+ }
+
+ ///
+ /// Writes the current output settings to persistent storage.
+ ///
+ public void WriteOutput()
+ {
+ Debug.Assert(this.Output.Settings != null, "Output settings should not be null.");
+ UserSettings.SaveSettings(this.Output.Settings.ToObject());
+ }
+
+ ///
+ /// Validates and converts the provided settings into a structured format.
+ ///
+ /// An object containing settings to be validated.
+ /// An object representing the validated settings.
+ public JObject GetValidSettings(JObject settings)
+ {
+ var settingsManifest = settings.ToObject();
+ return JObject.FromObject(settingsManifest);
+ }
+}
diff --git a/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
new file mode 100644
index 00000000..d03b291e
--- /dev/null
+++ b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateCLI.Models.DscModels;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+///
+/// Represents a settings resource object used in DSC operations.
+///
+public class SettingsResourceObject : BaseResourceObject
+{
+ ///
+ /// Defines the action type for full settings application.
+ ///
+ public const string ActionFull = "Full";
+
+ ///
+ /// Defines the action type for partial settings application.
+ ///
+ public const string ActionPartial = "Partial";
+
+ ///
+ /// Gets or sets the settings for the resource object.
+ ///
+ [JsonProperty("settings", Required = Required.Always)]
+ public JObject Settings { get; set; }
+
+ ///
+ /// Gets or sets the action to be performed on the settings resource object.
+ ///
+ [JsonProperty("action", NullValueHandling = NullValueHandling.Ignore)]
+ public string Action { get; set; }
+
+ ///
+ /// Creates a settings resource object with the specified settings and action.
+ ///
+ /// The JSON representation of the settings resource object.
+ /// A settings resource object.
+ public static SettingsResourceObject FromJson(JToken json)
+ {
+ return json.ToObject();
+ }
+
+ ///
+ /// Converts the current object to a JSON representation.
+ ///
+ /// A Json object representing the current object.
+ public JObject ToJson()
+ {
+ return JObject.FromObject(this, new JsonSerializer
+ {
+ NullValueHandling = NullValueHandling.Ignore,
+ });
+ }
+}
diff --git a/src/WingetCreateCLI/WingetCreateCLI.csproj b/src/WingetCreateCLI/WingetCreateCLI.csproj
index ed20a01c..3cdc473a 100644
--- a/src/WingetCreateCLI/WingetCreateCLI.csproj
+++ b/src/WingetCreateCLI/WingetCreateCLI.csproj
@@ -99,10 +99,6 @@
-
-
-
-
From bc9cd4af8b21d64ed279690442afb42b6e79a253 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Wed, 11 Jun 2025 19:34:23 -0700
Subject: [PATCH 04/24] Added schema
---
src/WingetCreateCLI/Commands/DscCommand.cs | 12 +++++-
.../Commands/DscCommands/BaseDscCommand.cs | 30 +++++++++++++++
.../DscCommands/DscSettingsCommand.cs | 12 ++++--
.../Models/DscModels/BaseResourceObject.cs | 37 ++++++++++++++++++-
.../DscModels/SettingsResourceObject.cs | 35 ++++++++++--------
5 files changed, 104 insertions(+), 22 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index 259a96f3..af2f84eb 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -50,6 +50,12 @@ public class DscCommand : BaseCommand
[Option('e', "export", SetName = "ExportMethod", HelpText = "Command for the Export flow.")]
public string Export { get; set; }
+ ///
+ /// Gets or sets a value indicating whether to execute the schema command.
+ ///
+ [Option("schema", SetName = "SchemaMethod", HelpText = "Command for the Schema flow.")]
+ public bool Schema { get; set; }
+
///
/// Executes the dsc command flow.
///
@@ -58,7 +64,7 @@ public override async Task Execute()
{
BaseDscCommand dscCommand;
var dscScope = this.UnboundArgs.FirstOrDefault()?.ToLowerInvariant() ?? string.Empty;
- if (dscScope == DscSettingsCommand.CommandName)
+ if (dscScope == "settings")
{
dscCommand = new DscSettingsCommand();
}
@@ -85,6 +91,10 @@ public override async Task Execute()
{
dscCommand.Export(input);
}
+ else if (this.Schema)
+ {
+ dscCommand.Schema();
+ }
else
{
Console.WriteLine("No valid DSC command provided. Use -g, -s, -t, or -e to specify a command.");
diff --git a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
index f8840336..56631d17 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
@@ -4,6 +4,7 @@
namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
using System;
+using Microsoft.WingetCreateCLI.Models.DscModels;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -12,6 +13,11 @@ namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
///
public abstract class BaseDscCommand
{
+ ///
+ /// Gets the name of the command used to access the DSC functionality.
+ ///
+ public virtual string CommandName { get; }
+
///
/// DSC Get command.
///
@@ -36,6 +42,30 @@ public abstract class BaseDscCommand
/// Input for the Export command.
public abstract void Export(JToken input);
+ ///
+ /// DSC Schema command.
+ ///
+ public abstract void Schema();
+
+ ///
+ /// Creates a Json schema for a DSC resource object.
+ ///
+ /// A Json object representing the schema.
+ protected JObject CreateSchema()
+ where T : BaseResourceObject, new()
+ {
+ var resourceObject = new T();
+ return new JObject
+ {
+ ["$schema"] = "http://json-schema.org/draft-07/schema#",
+ ["title"] = this.CommandName,
+ ["type"] = "object",
+ ["properties"] = resourceObject.GetProperties(),
+ ["required"] = resourceObject.GetRequiredProperties(),
+ ["additionalProperties"] = false,
+ };
+ }
+
///
/// Writes a JSON output line to the console.
///
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
index 98ee9a71..d086b0b7 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -11,10 +11,8 @@ namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
///
public class DscSettingsCommand : BaseDscCommand
{
- ///
- /// Represents the name of the command used to access settings functionality.
- ///
- public const string CommandName = "settings";
+ ///
+ public override string CommandName => "settings";
///
public override void Get(JToken input)
@@ -62,4 +60,10 @@ public override void Export(JToken input)
this.WriteJsonOutputLine(data.Output.ToJson());
}
+
+ ///
+ public override void Schema()
+ {
+ this.WriteJsonOutputLine(this.CreateSchema());
+ }
}
diff --git a/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
index b4bcd070..fb432384 100644
--- a/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
+++ b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
@@ -4,15 +4,50 @@
namespace Microsoft.WingetCreateCLI.Models.DscModels;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
///
/// Represents a base resource object with a property indicating whether the resource is in its desired state.
///
-public class BaseResourceObject
+public abstract class BaseResourceObject
{
///
/// Gets or sets a value indicating whether the resource is in its desired state.
///
[JsonProperty("_inDesiredState", NullValueHandling = NullValueHandling.Ignore)]
public bool? InDesiredState { get; set; }
+
+ ///
+ /// Gets the properties of the resource object.
+ ///
+ /// A Json object containing the properties of the resource.
+ public virtual JObject GetProperties()
+ {
+ return new JObject
+ {
+ ["_inDesiredState"] = new JObject
+ {
+ ["description"] = "Indicates whether an instance is in the desired state.",
+ ["type"] = "boolean",
+ },
+ };
+ }
+
+ ///
+ /// Gets the required properties of the resource object.
+ ///
+ /// A Json array containing the required properties.
+ public abstract JArray GetRequiredProperties();
+
+ ///
+ /// Converts the current object to a JSON representation.
+ ///
+ /// A Json object representing the current object.
+ public JObject ToJson()
+ {
+ return JObject.FromObject(this, new JsonSerializer
+ {
+ NullValueHandling = NullValueHandling.Ignore,
+ });
+ }
}
diff --git a/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
index d03b291e..c8aa48ff 100644
--- a/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
+++ b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
@@ -33,25 +33,28 @@ public class SettingsResourceObject : BaseResourceObject
[JsonProperty("action", NullValueHandling = NullValueHandling.Ignore)]
public string Action { get; set; }
- ///
- /// Creates a settings resource object with the specified settings and action.
- ///
- /// The JSON representation of the settings resource object.
- /// A settings resource object.
- public static SettingsResourceObject FromJson(JToken json)
+ ///
+ public override JObject GetProperties()
{
- return json.ToObject();
+ var baseProperties = base.GetProperties();
+ baseProperties["settings"] = new JObject
+ {
+ ["description"] = "The settings.",
+ ["type"] = "object",
+ };
+ baseProperties["action"] = new JObject
+ {
+ ["default"] = ActionPartial,
+ ["description"] = "The action used to apply the settings.",
+ ["type"] = "string",
+ ["enum"] = new JArray(ActionFull, ActionPartial),
+ };
+ return baseProperties; ;
}
- ///
- /// Converts the current object to a JSON representation.
- ///
- /// A Json object representing the current object.
- public JObject ToJson()
+ ///
+ public override JArray GetRequiredProperties()
{
- return JObject.FromObject(this, new JsonSerializer
- {
- NullValueHandling = NullValueHandling.Ignore,
- });
+ return ["settings"];
}
}
From 956357f535f3e0dd3b9b57b107917d95085648e8 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Thu, 12 Jun 2025 14:14:26 -0700
Subject: [PATCH 05/24] Init resource file
---
src/WingetCreateCLI/Commands/DscCommand.cs | 77 ++++++++++---------
.../Commands/DscCommands/BaseDscCommand.cs | 15 ++--
.../DscCommands/DscSettingsCommand.cs | 28 +++++--
...t.winget-create.settings.dsc.resource.json | 60 +++++++++++++++
src/WingetCreateCLI/WingetCreateCLI.csproj | 39 ++++++----
5 files changed, 156 insertions(+), 63 deletions(-)
create mode 100644 src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index af2f84eb..d43ad2e7 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -9,6 +9,7 @@ namespace Microsoft.WingetCreateCLI.Commands;
using System.Threading.Tasks;
using CommandLine;
using Microsoft.WingetCreateCLI.Commands.DscCommands;
+using Microsoft.WingetCreateCLI.Logging;
using Newtonsoft.Json.Linq;
///
@@ -62,66 +63,72 @@ public class DscCommand : BaseCommand
/// Boolean representing success or fail of the command.
public override async Task Execute()
{
- BaseDscCommand dscCommand;
- var dscScope = this.UnboundArgs.FirstOrDefault()?.ToLowerInvariant() ?? string.Empty;
- if (dscScope == "settings")
- {
- dscCommand = new DscSettingsCommand();
- }
- else
+ await Task.CompletedTask;
+
+ List dscCommands = [new DscSettingsCommand()];
+ var argCommandName = this.UnboundArgs.FirstOrDefault();
+ var dscCommand = dscCommands.FirstOrDefault(c => c.CommandName.Equals(argCommandName, StringComparison.OrdinalIgnoreCase));
+
+ if (dscCommand == null)
{
- Console.WriteLine($"Unknown DSC scope: {dscScope}");
+ Logger.Error($"Unknown DSC resource {argCommandName}");
return false;
}
- JToken input;
- if (this.TryParse(this.Get, out input))
- {
- dscCommand.Get(input);
- }
- else if (this.TryParse(this.Set, out input))
+ if (this.HandleOperation("Get", this.Get, (input) => dscCommand.Get(input)) ||
+ this.HandleOperation("Set", this.Set, (input) => dscCommand.Set(input)) ||
+ this.HandleOperation("Test", this.Test, (input) => dscCommand.Test(input)) ||
+ this.HandleOperation("Export", this.Export, (input) => dscCommand.Export(input)) ||
+ (this.Schema && dscCommand.Schema()))
{
- dscCommand.Set(input);
- }
- else if (this.TryParse(this.Test, out input))
- {
- dscCommand.Test(input);
+ return true;
}
- else if (this.TryParse(this.Export, out input))
+
+ Logger.Error("No valid DSC command provided.");
+ return false;
+ }
+
+ private bool HandleOperation(string name, string arg, Func op)
+ {
+ if (arg == null)
{
- dscCommand.Export(input);
+ // If no argument is provided, then we assume another operation is being requested.
+ return false;
}
- else if (this.Schema)
+
+ try
{
- dscCommand.Schema();
+ var input = this.GetJsonOrNull(arg);
+ if (!op(input))
+ {
+ Logger.Error($"Invalid input for {name} command.");
+ return false;
+ }
+
+ return true;
}
- else
+ catch (Exception ex)
{
- Console.WriteLine("No valid DSC command provided. Use -g, -s, -t, or -e to specify a command.");
+ Logger.Error(ex.Message);
return false;
}
-
- return true;
}
- private bool TryParse(string json, out JToken token)
+ private JToken GetJsonOrNull(string json)
{
if (string.IsNullOrWhiteSpace(json))
{
- token = null;
- return false;
+ return null;
}
try
{
- token = JToken.Parse(json);
- return true;
+ return JToken.Parse(json);
}
catch (Exception ex)
{
- Console.WriteLine($"Error parsing JSON: {ex.Message}");
- token = null;
- return false;
+ Logger.Error(ex.Message);
+ return null;
}
}
}
diff --git a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
index 56631d17..89288706 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
@@ -22,30 +22,35 @@ public abstract class BaseDscCommand
/// DSC Get command.
///
/// Input for the Get command.
- public abstract void Get(JToken input);
+ /// True if the command was successful; otherwise, false.
+ public abstract bool Get(JToken input);
///
/// DSC Set command.
///
/// Input for the Set command.
- public abstract void Set(JToken input);
+ /// True if the command was successful; otherwise, false.
+ public abstract bool Set(JToken input);
///
/// DSC Test command.
///
/// Input for the Test command.
- public abstract void Test(JToken input);
+ /// True if the command was successful; otherwise, false.
+ public abstract bool Test(JToken input);
///
/// DSC Export command.
///
/// Input for the Export command.
- public abstract void Export(JToken input);
+ /// True if the command was successful; otherwise, false.
+ public abstract bool Export(JToken input);
///
/// DSC Schema command.
///
- public abstract void Schema();
+ /// True if the command was successful; otherwise, false.
+ public abstract bool Schema();
///
/// Creates a Json schema for a DSC resource object.
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
index d086b0b7..1241be4f 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -15,14 +15,19 @@ public class DscSettingsCommand : BaseDscCommand
public override string CommandName => "settings";
///
- public override void Get(JToken input)
+ public override bool Get(JToken input)
{
- this.Export(input);
+ return this.Export(input);
}
///
- public override void Set(JToken input)
+ public override bool Set(JToken input)
{
+ if (input == null)
+ {
+ return false;
+ }
+
var data = new SettingsFunctionData(input);
data.Get();
@@ -37,11 +42,17 @@ public override void Set(JToken input)
this.WriteJsonOutputLine(data.Output.ToJson());
this.WriteJsonOutputLine(diff);
+ return true;
}
///
- public override void Test(JToken input)
+ public override bool Test(JToken input)
{
+ if ( input == null)
+ {
+ return false;
+ }
+
var data = new SettingsFunctionData(input);
data.Get();
@@ -49,21 +60,22 @@ public override void Test(JToken input)
this.WriteJsonOutputLine(data.Output.ToJson());
this.WriteJsonOutputLine(data.DiffJson());
+ return true;
}
///
- public override void Export(JToken input)
+ public override bool Export(JToken input)
{
var data = new SettingsFunctionData();
-
data.Get();
-
this.WriteJsonOutputLine(data.Output.ToJson());
+ return true;
}
///
- public override void Schema()
+ public override bool Schema()
{
this.WriteJsonOutputLine(this.CreateSchema());
+ return true;
}
}
diff --git a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
new file mode 100644
index 00000000..cd18bbea
--- /dev/null
+++ b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json",
+ "description": "Allows management of settings state via the DSC v3 command line interface protocol. See the help link for details.",
+ "export": {
+ "executable": "wingetcreate",
+ "input": "stdin",
+ "args": [
+ "dsc",
+ "settings",
+ "--export",
+ "'{}'"
+ ]
+ },
+ "get": {
+ "executable": "wingetcreate",
+ "input": "stdin",
+ "args": [
+ "dsc",
+ "settings",
+ "--get",
+ "'{}'"
+ ]
+ },
+ "schema": {
+ "command": {
+ "executable": "wingetcreate",
+ "args": [
+ "dsc",
+ "settings",
+ "--schema"
+ ]
+ }
+ },
+ "set": {
+ "executable": "wingetcreate",
+ "implementsPretest": true,
+ "return": "stateAndDiff",
+ "input": "stdin",
+ "args": [
+ "dsc",
+ "settings",
+ "--set"
+ ]
+ },
+ "test": {
+ "executable": "wingetcreate",
+ "return": "stateAndDiff",
+ "input": "stdin",
+ "args": [
+ "dsc",
+ "settings",
+ "--test"
+ ]
+ },
+ "tags": [
+ "WinGetCreate"
+ ],
+ "type": "Microsoft.WinGetCreate/Settings",
+ "version": "1.9.0"
+}
\ No newline at end of file
diff --git a/src/WingetCreateCLI/WingetCreateCLI.csproj b/src/WingetCreateCLI/WingetCreateCLI.csproj
index 3cdc473a..75214c1d 100644
--- a/src/WingetCreateCLI/WingetCreateCLI.csproj
+++ b/src/WingetCreateCLI/WingetCreateCLI.csproj
@@ -41,17 +41,17 @@
-
@@ -69,7 +69,7 @@
-
%(LinkBase)\%(Filename)%(Extension)
@@ -81,7 +81,7 @@
PublicResXFileCodeGenerator
-
+
@@ -96,8 +96,17 @@
-
-
+
+
+
+
+
+
+ %(Filename)%(Extension)
+ Always
+ Always
+
+
From 65b30f4882ebd7f60231c247f3a6f3278d811503 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Thu, 12 Jun 2025 15:41:42 -0700
Subject: [PATCH 06/24] Resx
---
src/WingetCreateCLI/Commands/DscCommand.cs | 39 +++------
.../Properties/Resources.Designer.cs | 81 +++++++++++++++++++
src/WingetCreateCLI/Properties/Resources.resx | 28 +++++++
3 files changed, 120 insertions(+), 28 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index d43ad2e7..6f1f00d2 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -10,12 +10,13 @@ namespace Microsoft.WingetCreateCLI.Commands;
using CommandLine;
using Microsoft.WingetCreateCLI.Commands.DscCommands;
using Microsoft.WingetCreateCLI.Logging;
+using Microsoft.WingetCreateCLI.Properties;
using Newtonsoft.Json.Linq;
///
/// Command for managin the application using dsc v3.
///
-[Verb("dsc", HelpText = "Manage the application using dsc v3.")]
+[Verb("dsc", HelpText = "DscCommand_HelpText", ResourceType = typeof(Resources))]
public class DscCommand : BaseCommand
{
///
@@ -30,31 +31,31 @@ public class DscCommand : BaseCommand
///
/// Gets or sets the input for the dsc Get operation.
///
- [Option('g', "get", SetName = "GetMethod", HelpText = "Command for the Get flow.")]
+ [Option('g', "get", SetName = "GetMethod", HelpText = "DscGet_HelpText", ResourceType = typeof(Resources))]
public string Get { get; set; }
///
/// Gets or sets the input for the dsc Set operation.
///
- [Option('s', "set", SetName = "SetMethod", HelpText = "Command for the Set flow.")]
+ [Option('s', "set", SetName = "SetMethod", HelpText = "DscSet_HelpText", ResourceType = typeof(Resources))]
public string Set { get; set; }
///
/// Gets or sets the input for the dsc Test operation.
///
- [Option('t', "test", SetName = "TestMethod", HelpText = "Command for the Test flow.")]
+ [Option('t', "test", SetName = "TestMethod", HelpText = "DscTest_HelpText", ResourceType = typeof(Resources))]
public string Test { get; set; }
///
/// Gets or sets the input for the dsc Export operation.
///
- [Option('e', "export", SetName = "ExportMethod", HelpText = "Command for the Export flow.")]
+ [Option('e', "export", SetName = "ExportMethod", HelpText = "DscExport_HelpText", ResourceType = typeof(Resources))]
public string Export { get; set; }
///
/// Gets or sets a value indicating whether to execute the schema command.
///
- [Option("schema", SetName = "SchemaMethod", HelpText = "Command for the Schema flow.")]
+ [Option("schema", SetName = "SchemaMethod", HelpText = "DscSchema_HelpText", ResourceType = typeof(Resources))]
public bool Schema { get; set; }
///
@@ -71,7 +72,7 @@ public override async Task Execute()
if (dscCommand == null)
{
- Logger.Error($"Unknown DSC resource {argCommandName}");
+ Logger.ErrorLocalized(Resources.DscResourceNotFound_Message, argCommandName);
return false;
}
@@ -84,7 +85,7 @@ public override async Task Execute()
return true;
}
- Logger.Error("No valid DSC command provided.");
+ Logger.ErrorLocalized(Resources.DscResourceOperationInvalid_Message);
return false;
}
@@ -98,10 +99,10 @@ private bool HandleOperation(string name, string arg, Func op)
try
{
- var input = this.GetJsonOrNull(arg);
+ var input = string.IsNullOrWhiteSpace(arg) ? null : JToken.Parse(arg);
if (!op(input))
{
- Logger.Error($"Invalid input for {name} command.");
+ Logger.ErrorLocalized(Resources.DscResourceOperationFailed_Message);
return false;
}
@@ -113,22 +114,4 @@ private bool HandleOperation(string name, string arg, Func op)
return false;
}
}
-
- private JToken GetJsonOrNull(string json)
- {
- if (string.IsNullOrWhiteSpace(json))
- {
- return null;
- }
-
- try
- {
- return JToken.Parse(json);
- }
- catch (Exception ex)
- {
- Logger.Error(ex.Message);
- return null;
- }
- }
}
diff --git a/src/WingetCreateCLI/Properties/Resources.Designer.cs b/src/WingetCreateCLI/Properties/Resources.Designer.cs
index 1eb6b026..9930eb78 100644
--- a/src/WingetCreateCLI/Properties/Resources.Designer.cs
+++ b/src/WingetCreateCLI/Properties/Resources.Designer.cs
@@ -735,6 +735,87 @@ public static string DownloadInstaller_Message {
}
}
+ ///
+ /// Looks up a localized string similar to DSC v3 resource commands..
+ ///
+ public static string DscCommand_HelpText {
+ get {
+ return ResourceManager.GetString("DscCommand_HelpText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Export the resource configuration.
+ ///
+ public static string DscExport_HelpText {
+ get {
+ return ResourceManager.GetString("DscExport_HelpText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Get the resource state.
+ ///
+ public static string DscGet_HelpText {
+ get {
+ return ResourceManager.GetString("DscGet_HelpText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to DSC resource not found: {0}.
+ ///
+ public static string DscResourceNotFound_Message {
+ get {
+ return ResourceManager.GetString("DscResourceNotFound_Message", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to DSC resource operation failed.
+ ///
+ public static string DscResourceOperationFailed_Message {
+ get {
+ return ResourceManager.GetString("DscResourceOperationFailed_Message", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid operation for the DSC resource.
+ ///
+ public static string DscResourceOperationInvalid_Message {
+ get {
+ return ResourceManager.GetString("DscResourceOperationInvalid_Message", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Execute the Schema command.
+ ///
+ public static string DscSchema_HelpText {
+ get {
+ return ResourceManager.GetString("DscSchema_HelpText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Set the resource state.
+ ///
+ public static string DscSet_HelpText {
+ get {
+ return ResourceManager.GetString("DscSet_HelpText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Test the resource state.
+ ///
+ public static string DscTest_HelpText {
+ get {
+ return ResourceManager.GetString("DscTest_HelpText", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Would you like to edit your manifests?.
///
diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx
index 14180ffb..87bbcb66 100644
--- a/src/WingetCreateCLI/Properties/Resources.resx
+++ b/src/WingetCreateCLI/Properties/Resources.resx
@@ -1406,4 +1406,32 @@ Warning: Using this argument may result in the token being logged. Consider an a
The forked repository could not be synced with the upstream commits due to a merge conflict. Resolve conflicts manually and try again.
+
+ DSC v3 resource commands.
+
+
+ Get the resource state
+
+
+ Set the resource state
+
+
+ Test the resource state
+
+
+ Export the resource configuration
+
+
+ Execute the Schema command
+
+
+ DSC resource not found: {0}
+ {Locked='{0}'} {0} is replaced by the DSC resource name
+
+
+ Invalid operation for the DSC resource
+
+
+ DSC resource operation failed
+
\ No newline at end of file
From 888ec82741df999b2aebdbf0f6555fb1a80c9112 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Thu, 12 Jun 2025 15:45:09 -0700
Subject: [PATCH 07/24] Fix resource file
---
.../microsoft.winget-create.settings.dsc.resource.json | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
index cd18bbea..1cf97b12 100644
--- a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
+++ b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
@@ -39,7 +39,10 @@
"args": [
"dsc",
"settings",
- "--set"
+ {
+ "jsonInputArg": "--set",
+ "mandatory": true
+ }
]
},
"test": {
@@ -49,7 +52,10 @@
"args": [
"dsc",
"settings",
- "--test"
+ {
+ "jsonInputArg": "--test",
+ "mandatory": true
+ }
]
},
"tags": [
From 26d50541743b45c81bca7bf16146d0f6d6ea8a61 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Thu, 12 Jun 2025 16:06:02 -0700
Subject: [PATCH 08/24] Resx
---
.../Models/DscModels/BaseResourceObject.cs | 3 +-
.../DscModels/SettingsResourceObject.cs | 5 ++--
.../Properties/Resources.Designer.cs | 29 ++++++++++++++++++-
src/WingetCreateCLI/Properties/Resources.resx | 11 ++++++-
src/WingetCreateCLI/WingetCreateCLI.csproj | 7 +++--
5 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
index fb432384..5f7f90a9 100644
--- a/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
+++ b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
@@ -3,6 +3,7 @@
namespace Microsoft.WingetCreateCLI.Models.DscModels;
+using Microsoft.WingetCreateCLI.Properties;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -27,7 +28,7 @@ public virtual JObject GetProperties()
{
["_inDesiredState"] = new JObject
{
- ["description"] = "Indicates whether an instance is in the desired state.",
+ ["description"] = Resources.DscResourcePropertyDescriptionInDesiredState,
["type"] = "boolean",
},
};
diff --git a/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
index c8aa48ff..a86b1fb5 100644
--- a/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
+++ b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
@@ -3,6 +3,7 @@
namespace Microsoft.WingetCreateCLI.Models.DscModels;
+using Microsoft.WingetCreateCLI.Properties;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -39,13 +40,13 @@ public override JObject GetProperties()
var baseProperties = base.GetProperties();
baseProperties["settings"] = new JObject
{
- ["description"] = "The settings.",
+ ["description"] = Resources.DscResourcePropertyDescriptionSettings,
["type"] = "object",
};
baseProperties["action"] = new JObject
{
["default"] = ActionPartial,
- ["description"] = "The action used to apply the settings.",
+ ["description"] = Resources.DscResourcePropertyDescriptionAction,
["type"] = "string",
["enum"] = new JArray(ActionFull, ActionPartial),
};
diff --git a/src/WingetCreateCLI/Properties/Resources.Designer.cs b/src/WingetCreateCLI/Properties/Resources.Designer.cs
index 9930eb78..1d8a6d00 100644
--- a/src/WingetCreateCLI/Properties/Resources.Designer.cs
+++ b/src/WingetCreateCLI/Properties/Resources.Designer.cs
@@ -745,7 +745,7 @@ public static string DscCommand_HelpText {
}
///
- /// Looks up a localized string similar to Export the resource configuration.
+ /// Looks up a localized string similar to Get all state instances.
///
public static string DscExport_HelpText {
get {
@@ -789,6 +789,33 @@ public static string DscResourceOperationInvalid_Message {
}
}
+ ///
+ /// Looks up a localized string similar to The action used to apply the settings..
+ ///
+ public static string DscResourcePropertyDescriptionAction {
+ get {
+ return ResourceManager.GetString("DscResourcePropertyDescriptionAction", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Indicates whether an instance is in the desired state..
+ ///
+ public static string DscResourcePropertyDescriptionInDesiredState {
+ get {
+ return ResourceManager.GetString("DscResourcePropertyDescriptionInDesiredState", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The settings content..
+ ///
+ public static string DscResourcePropertyDescriptionSettings {
+ get {
+ return ResourceManager.GetString("DscResourcePropertyDescriptionSettings", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Execute the Schema command.
///
diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx
index 87bbcb66..f5ef5985 100644
--- a/src/WingetCreateCLI/Properties/Resources.resx
+++ b/src/WingetCreateCLI/Properties/Resources.resx
@@ -1419,7 +1419,7 @@ Warning: Using this argument may result in the token being logged. Consider an a
Test the resource state
- Export the resource configuration
+ Get all state instancesExecute the Schema command
@@ -1434,4 +1434,13 @@ Warning: Using this argument may result in the token being logged. Consider an a
DSC resource operation failed
+
+ Indicates whether an instance is in the desired state.
+
+
+ The settings content.
+
+
+ The action used to apply the settings.
+
\ No newline at end of file
diff --git a/src/WingetCreateCLI/WingetCreateCLI.csproj b/src/WingetCreateCLI/WingetCreateCLI.csproj
index 75214c1d..e7e59ae9 100644
--- a/src/WingetCreateCLI/WingetCreateCLI.csproj
+++ b/src/WingetCreateCLI/WingetCreateCLI.csproj
@@ -97,9 +97,12 @@
-
+
+
+
+
+
-
%(Filename)%(Extension)
From c1b6c87ce1ec41600ad1e3b660fe8e667b74174f Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 13 Jun 2025 09:42:38 -0700
Subject: [PATCH 09/24] Fix resource file
---
.../microsoft.winget-create.settings.dsc.resource.json | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
index 1cf97b12..bcb8d6b7 100644
--- a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
+++ b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
@@ -35,7 +35,6 @@
"executable": "wingetcreate",
"implementsPretest": true,
"return": "stateAndDiff",
- "input": "stdin",
"args": [
"dsc",
"settings",
@@ -48,7 +47,6 @@
"test": {
"executable": "wingetcreate",
"return": "stateAndDiff",
- "input": "stdin",
"args": [
"dsc",
"settings",
From 8035d579059dd8a5fa480bd374c9b3c57cf35c7c Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Sun, 15 Jun 2025 10:06:28 -0700
Subject: [PATCH 10/24] Adding UT
---
src/WingetCreateCLI/Commands/DscCommand.cs | 13 +-
.../Properties/Resources.Designer.cs | 9 +
src/WingetCreateCLI/Properties/Resources.resx | 4 +
.../UnitTests/DscCommandTests.cs | 121 ++++++++++
.../UnitTests/DscSettingsCommandTests.cs | 213 ++++++++++++++++++
5 files changed, 356 insertions(+), 4 deletions(-)
create mode 100644 src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
create mode 100644 src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index 6f1f00d2..c7bae00c 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -68,11 +68,16 @@ public override async Task Execute()
List dscCommands = [new DscSettingsCommand()];
var argCommandName = this.UnboundArgs.FirstOrDefault();
- var dscCommand = dscCommands.FirstOrDefault(c => c.CommandName.Equals(argCommandName, StringComparison.OrdinalIgnoreCase));
+ if(string.IsNullOrWhiteSpace(argCommandName))
+ {
+ Logger.ErrorLocalized(nameof(Resources.DscResourceMissing_Message), string.Join(", ", dscCommands.Select(c => c.CommandName)));
+ return false;
+ }
+ var dscCommand = dscCommands.FirstOrDefault(c => c.CommandName.Equals(argCommandName, StringComparison.OrdinalIgnoreCase));
if (dscCommand == null)
{
- Logger.ErrorLocalized(Resources.DscResourceNotFound_Message, argCommandName);
+ Logger.ErrorLocalized(nameof(Resources.DscResourceNotFound_Message), argCommandName);
return false;
}
@@ -85,7 +90,7 @@ public override async Task Execute()
return true;
}
- Logger.ErrorLocalized(Resources.DscResourceOperationInvalid_Message);
+ Logger.ErrorLocalized(nameof(Resources.DscResourceOperationInvalid_Message));
return false;
}
@@ -102,7 +107,7 @@ private bool HandleOperation(string name, string arg, Func op)
var input = string.IsNullOrWhiteSpace(arg) ? null : JToken.Parse(arg);
if (!op(input))
{
- Logger.ErrorLocalized(Resources.DscResourceOperationFailed_Message);
+ Logger.ErrorLocalized(nameof(Resources.DscResourceOperationFailed_Message));
return false;
}
diff --git a/src/WingetCreateCLI/Properties/Resources.Designer.cs b/src/WingetCreateCLI/Properties/Resources.Designer.cs
index 1d8a6d00..79bf95fb 100644
--- a/src/WingetCreateCLI/Properties/Resources.Designer.cs
+++ b/src/WingetCreateCLI/Properties/Resources.Designer.cs
@@ -762,6 +762,15 @@ public static string DscGet_HelpText {
}
}
+ ///
+ /// Looks up a localized string similar to DSC resource is missing: {0}.
+ ///
+ public static string DscResourceMissing_Message {
+ get {
+ return ResourceManager.GetString("DscResourceMissing_Message", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to DSC resource not found: {0}.
///
diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx
index f5ef5985..288268a9 100644
--- a/src/WingetCreateCLI/Properties/Resources.resx
+++ b/src/WingetCreateCLI/Properties/Resources.resx
@@ -1428,6 +1428,10 @@ Warning: Using this argument may result in the token being logged. Consider an a
DSC resource not found: {0}{Locked='{0}'} {0} is replaced by the DSC resource name
+
+ DSC resource is missing: {0}
+ {Locked='{0}'} {0} is replaced by the available DSC resource names
+
Invalid operation for the DSC resource
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
new file mode 100644
index 00000000..ff58af6c
--- /dev/null
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
@@ -0,0 +1,121 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateUnitTests;
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using CommandLine;
+using Microsoft.WingetCreateCLI.Commands;
+using Microsoft.WingetCreateCLI.Commands.DscCommands;
+using Microsoft.WingetCreateCLI.Logging;
+using Microsoft.WingetCreateCLI.Properties;
+using NUnit.Framework;
+
+///
+/// Unit test class for the DSC Command.
+///
+public class DscCommandTests
+{
+ ///
+ /// Execute the DSC command
+ ///
+ /// The arguments to pass to the DSC command.
+ /// Result of executing the DSC command.
+ public static async Task ExecuteDscCommandAsync(List args)
+ {
+ var sw = new StringWriter();
+ Console.SetOut(sw);
+ var executeResult = await Parser.Default.ParseArguments(args).Value.Execute();
+ var output = sw.ToString();
+ return new(executeResult, output);
+ }
+
+ ///
+ /// OneTimeSetup method for the DSC command unit tests.
+ ///
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ Logger.Initialize();
+ }
+
+ [Test]
+ public async Task DscSettingsResource_Success()
+ {
+ // Arrange
+ var command = new DscSettingsCommand();
+
+ // Act
+ var result = await ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
+
+ // Assert
+ Assert.That(result.Success, Is.True);
+ }
+
+ [Test]
+ public async Task DscResourceMissing_ErrorMessage()
+ {
+ // Arrange
+ List dscCommands = [new DscSettingsCommand()];
+
+ // Act
+ var result = await ExecuteDscCommandAsync([]);
+
+ // Assert
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Output, Does.Contain(string.Format(Resources.DscResourceMissing_Message, string.Join(", ", dscCommands.Select(c => c.CommandName)))));
+ }
+
+ [Test]
+ public async Task DscResourceNotFound_ErrorMessage()
+ {
+ // Arrange
+ var dscResourceName = "ResourceNotFound";
+
+ // Act
+ var result = await ExecuteDscCommandAsync([dscResourceName]);
+
+ // Assert
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Output, Does.Contain(string.Format(Resources.DscResourceNotFound_Message, dscResourceName)));
+ }
+
+ [Test]
+ public async Task DscResourceInvalidOperation_ErrorMessage()
+ {
+ // Arrange
+ var command = new DscSettingsCommand();
+
+ // Act
+ var result = await ExecuteDscCommandAsync([command.CommandName]);
+
+ // Assert
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationInvalid_Message));
+ }
+
+ [Test]
+ public async Task DscSettingsResourceFailedOperation_ErrorMessage()
+ {
+ // Arrange
+ var command = new DscSettingsCommand();
+
+ // Act
+ var result = await ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
+
+ // Assert
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationFailed_Message));
+ }
+
+ ///
+ /// Result of executing a DSC command.
+ ///
+ /// Value indicating whether the command execution was successful.
+ /// Value containing the output of the command execution.
+ public record class ExecuteResult(bool Success, string Output);
+}
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
new file mode 100644
index 00000000..f02591b7
--- /dev/null
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
@@ -0,0 +1,213 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateUnitTests;
+
+using System.Threading.Tasks;
+using Microsoft.WingetCreateCLI;
+using Microsoft.WingetCreateCLI.Commands.DscCommands;
+using Microsoft.WingetCreateCLI.Logging;
+using Microsoft.WingetCreateCLI.Models.DscModels;
+using Microsoft.WingetCreateCLI.Models.Settings;
+using Microsoft.WingetCreateCLI.Properties;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+
+///
+/// Unit test class for the DSC settings Command.
+///
+public class DscSettingsCommandTests
+{
+ private const string MockName = "mock_name";
+ private const string MockOwner = "mock_owner";
+ private JToken rawOriginalSettings;
+
+ ///
+ /// Gets the settings state with default values.
+ ///
+ private SettingsManifest DefaultSettings => new();
+
+ ///
+ /// Gets the settings state after the test is run.
+ ///
+ private SettingsManifest CurrentSettings => UserSettings.ToJson().ToObject();
+
+ ///
+ /// Gets the settings state before the test is run.
+ ///
+ private SettingsManifest OriginalSettings => this.rawOriginalSettings.ToObject();
+
+ ///
+ /// OneTimeSetup method for the DSC command unit tests.
+ ///
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ Logger.Initialize();
+ }
+
+ ///
+ /// Setup method for the cache command unit tests.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ this.rawOriginalSettings = UserSettings.ToJson();
+ }
+
+ ///
+ /// Teardown method for each individual test.
+ ///
+ [TearDown]
+ public void TearDown()
+ {
+ UserSettings.SaveSettings(this.OriginalSettings);
+ }
+
+ [Test]
+ public async Task DscSettingsResource_Get_Success()
+ {
+ // Arrange
+ var command = new DscSettingsCommand();
+
+ // Act
+ var result = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
+
+ // Assert
+ Assert.That(result.Success, Is.True);
+ Assert.That(result.Output, Does.Contain(this.CreateGetResponse()));
+ }
+
+ [Test]
+ public async Task DscSettingsResource_Export_Success()
+ {
+ // Arrange
+ var command = new DscSettingsCommand();
+
+ // Act
+ var result = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--export", string.Empty]);
+
+ // Assert
+ Assert.That(result.Success, Is.True);
+ Assert.That(result.Output, Does.Contain(this.CreateGetResponse()));
+ }
+
+ [Test]
+ public async Task DscSettingsResource_SetEmpty_Fail()
+ {
+ // Arrange
+ var command = new DscSettingsCommand();
+
+ // Act
+ var result = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
+
+ // Assert
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationFailed_Message));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ [TestCase(null)]
+ public async Task DscSettingsResource_Set_Success(bool? isPartial)
+ {
+ // Arrange
+ this.ResetSettingsToDefaultValues();
+ var command = new DscSettingsCommand();
+
+ // Part 1: Update settings repo name only
+ {
+ // Act
+ var setRepoName = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial)]);
+
+ // Assert
+ Assert.That(setRepoName.Success, Is.True);
+ this.AssertSettingsRepoHasChanged(name: MockName);
+ }
+
+ // Part 2: Now update settings repo owner only
+ {
+ // Act
+ var setRepoOwner = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+
+ // Assert
+ Assert.That(setRepoOwner.Success, Is.True);
+ this.AssertSettingsRepoHasChanged(name: (isPartial ?? true) ? MockName : null, owner: MockOwner);
+ }
+ }
+
+ [Test]
+ public async Task DscSettingsResource_Test_Success(bool? isPartial)
+ {
+ // Arrange
+ this.ResetSettingsToDefaultValues();
+ var command = new DscSettingsCommand();
+ var currentSettings = this.CurrentSettings;
+ currentSettings.WindowsPackageManagerRepository.Name = MockName;
+ UserSettings.SaveSettings(currentSettings);
+
+ // TODO
+ }
+
+ private void ResetSettingsToDefaultValues()
+ {
+ UserSettings.SaveSettings(this.DefaultSettings);
+ }
+
+ private string CreateGetResponse(bool? isPartial = null)
+ {
+ return this.CreateResourceObject(UserSettings.ToJson(), isPartial);
+ }
+
+ private string CreateInput(string name = null, string owner = null, bool? isPartial = null)
+ {
+ var repo = new JObject();
+ if (name != null)
+ {
+ repo["name"] = name;
+ }
+
+ if (owner != null)
+ {
+ repo["owner"] = owner;
+ }
+
+ var input = new JObject
+ {
+ [nameof(WindowsPackageManagerRepository)] = repo,
+ };
+
+ return this.CreateResourceObject(input, isPartial);
+ }
+
+ private string CreateResourceObject(JObject settings, bool? isPartial)
+ {
+ var resourceObject = new SettingsResourceObject
+ {
+ Settings = settings,
+ Action = isPartial.HasValue ? (isPartial.Value ? SettingsResourceObject.ActionPartial : SettingsResourceObject.ActionFull) : null,
+ };
+ return JObject.FromObject(resourceObject).ToString(Formatting.None);
+ }
+
+ private void AssertSettingsRepoHasChanged(string name = null, string owner = null)
+ {
+ var currentSettings = this.CurrentSettings;
+ var defaultSettings = this.DefaultSettings;
+ if (name != null)
+ {
+ defaultSettings.WindowsPackageManagerRepository.Name = name;
+ }
+
+ if (owner != null)
+ {
+ defaultSettings.WindowsPackageManagerRepository.Owner = owner;
+ }
+
+ var currentSettingsJson = JToken.FromObject(currentSettings);
+ var defaultSettingsJson = JToken.FromObject(defaultSettings);
+ Assert.That(JToken.DeepEquals(currentSettingsJson, defaultSettingsJson), Is.True);
+ }
+}
From 12b66f4c4b636ad7de462f32dfee4560def09a05 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Mon, 16 Jun 2025 09:10:38 -0700
Subject: [PATCH 11/24] Adding more UT
---
.../Models/DscExecuteResult.cs | 61 ++++
.../WingetCreateTests/TestUtils.cs | 17 ++
.../UnitTests/DscCommandTests.cs | 36 +--
.../UnitTests/DscSettingsCommandTests.cs | 281 ++++++++++++++++--
4 files changed, 348 insertions(+), 47 deletions(-)
create mode 100644 src/WingetCreateTests/WingetCreateTests/Models/DscExecuteResult.cs
diff --git a/src/WingetCreateTests/WingetCreateTests/Models/DscExecuteResult.cs b/src/WingetCreateTests/WingetCreateTests/Models/DscExecuteResult.cs
new file mode 100644
index 00000000..5c4fb083
--- /dev/null
+++ b/src/WingetCreateTests/WingetCreateTests/Models/DscExecuteResult.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.WingetCreateUnitTests.Models;
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.WingetCreateCLI.Models.DscModels;
+using Newtonsoft.Json;
+
+///
+/// Result of executing a DSC command.
+///
+public class DscExecuteResult
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Value indicating whether the command execution was successful.
+ /// Output of the command execution.
+ public DscExecuteResult(bool success, string output)
+ {
+ this.Success = success;
+ this.Output = output;
+ }
+
+ ///
+ /// Gets a value indicating whether the command execution was successful.
+ ///
+ public bool Success { get; }
+
+ ///
+ /// Gets the output result of the operation.
+ ///
+ public string Output { get; }
+
+ ///
+ /// Gets the output as settings state.
+ ///
+ /// Settings state.
+ public SettingsResourceObject OutputState()
+ {
+ var lines = this.Output.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
+ Debug.Assert(lines.Length == 1, "Output should contain exactly one line.");
+ return JsonConvert.DeserializeObject(lines[0]);
+ }
+
+ ///
+ /// Gets the output as settings state and diff.
+ ///
+ /// Settings state and diff.
+ public (SettingsResourceObject State, List Diff) OutputStateAndDiff()
+ {
+ var lines = this.Output.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
+ Debug.Assert(lines.Length == 2, "Output should contain exactly two lines.");
+ var settingsObject = JsonConvert.DeserializeObject(lines[0]);
+ var diff = JsonConvert.DeserializeObject>(lines[1]);
+ return (settingsObject, diff);
+ }
+}
diff --git a/src/WingetCreateTests/WingetCreateTests/TestUtils.cs b/src/WingetCreateTests/WingetCreateTests/TestUtils.cs
index b503ec3a..6099f3ba 100644
--- a/src/WingetCreateTests/WingetCreateTests/TestUtils.cs
+++ b/src/WingetCreateTests/WingetCreateTests/TestUtils.cs
@@ -13,7 +13,10 @@ namespace Microsoft.WingetCreateTests
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
+ using CommandLine;
+ using Microsoft.WingetCreateCLI.Commands;
using Microsoft.WingetCreateCore;
+ using Microsoft.WingetCreateUnitTests.Models;
using Moq;
using Moq.Protected;
@@ -213,5 +216,19 @@ public static void DeleteCachedFiles(List testFileNames)
File.Delete(Path.Combine(PackageParser.InstallerDownloadPath, fileName));
}
}
+
+ ///
+ /// Execute the DSC command.
+ ///
+ /// The arguments to pass to the DSC command.
+ /// Result of executing the DSC command.
+ public static async Task ExecuteDscCommandAsync(List args)
+ {
+ var sw = new StringWriter();
+ Console.SetOut(sw);
+ var executeResult = await Parser.Default.ParseArguments(args).Value.Execute();
+ var output = sw.ToString();
+ return new(executeResult, output);
+ }
}
}
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
index ff58af6c..e83c089c 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
@@ -3,16 +3,13 @@
namespace Microsoft.WingetCreateUnitTests;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using CommandLine;
-using Microsoft.WingetCreateCLI.Commands;
using Microsoft.WingetCreateCLI.Commands.DscCommands;
using Microsoft.WingetCreateCLI.Logging;
using Microsoft.WingetCreateCLI.Properties;
+using Microsoft.WingetCreateTests;
using NUnit.Framework;
///
@@ -20,20 +17,6 @@ namespace Microsoft.WingetCreateUnitTests;
///
public class DscCommandTests
{
- ///
- /// Execute the DSC command
- ///
- /// The arguments to pass to the DSC command.
- /// Result of executing the DSC command.
- public static async Task ExecuteDscCommandAsync(List args)
- {
- var sw = new StringWriter();
- Console.SetOut(sw);
- var executeResult = await Parser.Default.ParseArguments(args).Value.Execute();
- var output = sw.ToString();
- return new(executeResult, output);
- }
-
///
/// OneTimeSetup method for the DSC command unit tests.
///
@@ -50,7 +33,7 @@ public async Task DscSettingsResource_Success()
var command = new DscSettingsCommand();
// Act
- var result = await ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
// Assert
Assert.That(result.Success, Is.True);
@@ -63,7 +46,7 @@ public async Task DscResourceMissing_ErrorMessage()
List dscCommands = [new DscSettingsCommand()];
// Act
- var result = await ExecuteDscCommandAsync([]);
+ var result = await TestUtils.ExecuteDscCommandAsync([]);
// Assert
Assert.That(result.Success, Is.False);
@@ -77,7 +60,7 @@ public async Task DscResourceNotFound_ErrorMessage()
var dscResourceName = "ResourceNotFound";
// Act
- var result = await ExecuteDscCommandAsync([dscResourceName]);
+ var result = await TestUtils.ExecuteDscCommandAsync([dscResourceName]);
// Assert
Assert.That(result.Success, Is.False);
@@ -91,7 +74,7 @@ public async Task DscResourceInvalidOperation_ErrorMessage()
var command = new DscSettingsCommand();
// Act
- var result = await ExecuteDscCommandAsync([command.CommandName]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName]);
// Assert
Assert.That(result.Success, Is.False);
@@ -105,17 +88,10 @@ public async Task DscSettingsResourceFailedOperation_ErrorMessage()
var command = new DscSettingsCommand();
// Act
- var result = await ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
// Assert
Assert.That(result.Success, Is.False);
Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationFailed_Message));
}
-
- ///
- /// Result of executing a DSC command.
- ///
- /// Value indicating whether the command execution was successful.
- /// Value containing the output of the command execution.
- public record class ExecuteResult(bool Success, string Output);
}
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
index f02591b7..a31f2feb 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
@@ -3,6 +3,7 @@
namespace Microsoft.WingetCreateUnitTests;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.WingetCreateCLI;
using Microsoft.WingetCreateCLI.Commands.DscCommands;
@@ -10,6 +11,7 @@ namespace Microsoft.WingetCreateUnitTests;
using Microsoft.WingetCreateCLI.Models.DscModels;
using Microsoft.WingetCreateCLI.Models.Settings;
using Microsoft.WingetCreateCLI.Properties;
+using Microsoft.WingetCreateTests;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
@@ -65,6 +67,10 @@ public void TearDown()
UserSettings.SaveSettings(this.OriginalSettings);
}
+ ///
+ /// Tests the Get operation.
+ ///
+ /// Async task.
[Test]
public async Task DscSettingsResource_Get_Success()
{
@@ -72,13 +78,20 @@ public async Task DscSettingsResource_Get_Success()
var command = new DscSettingsCommand();
// Act
- var result = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
+ var state = result.OutputState();
// Assert
Assert.That(result.Success, Is.True);
Assert.That(result.Output, Does.Contain(this.CreateGetResponse()));
+ Assert.That(state.Action, Is.Null);
+ this.AssertStateAndSettingsAreEqual(this.CurrentSettings, state);
}
+ ///
+ /// Tests the Export operation.
+ ///
+ /// Async task.
[Test]
public async Task DscSettingsResource_Export_Success()
{
@@ -86,13 +99,20 @@ public async Task DscSettingsResource_Export_Success()
var command = new DscSettingsCommand();
// Act
- var result = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--export", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--export", string.Empty]);
+ var state = result.OutputState();
// Assert
Assert.That(result.Success, Is.True);
Assert.That(result.Output, Does.Contain(this.CreateGetResponse()));
+ Assert.That(state.Action, Is.Null);
+ this.AssertStateAndSettingsAreEqual(this.CurrentSettings, state);
}
+ ///
+ /// Tests the Set operation with an empty input.
+ ///
+ /// Async task.
[Test]
public async Task DscSettingsResource_SetEmpty_Fail()
{
@@ -100,18 +120,22 @@ public async Task DscSettingsResource_SetEmpty_Fail()
var command = new DscSettingsCommand();
// Act
- var result = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
// Assert
Assert.That(result.Success, Is.False);
Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationFailed_Message));
}
+ ///
+ /// Tests the Set operation with diff.
+ ///
+ /// Async task.
[Test]
[TestCase(true)]
[TestCase(false)]
[TestCase(null)]
- public async Task DscSettingsResource_Set_Success(bool? isPartial)
+ public async Task DscSettingsResource_SetWithDiff_Success(bool? isPartial)
{
// Arrange
this.ResetSettingsToDefaultValues();
@@ -120,47 +144,221 @@ public async Task DscSettingsResource_Set_Success(bool? isPartial)
// Part 1: Update settings repo name only
{
// Act
- var setRepoName = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var setRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var stateAndDiff = setRepoName.OutputStateAndDiff();
// Assert
Assert.That(setRepoName.Success, Is.True);
- this.AssertSettingsRepoHasChanged(name: MockName);
+ this.AssertSettingsHasChanged(name: MockName);
+ this.AssertStateAndSettingsAreEqual(this.CurrentSettings, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.EqualTo(new List() { "settings" }));
+ this.AssertStateAction(stateAndDiff.State, isPartial);
}
// Part 2: Now update settings repo owner only
{
// Act
- var setRepoOwner = await DscCommandTests.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var setRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var stateAndDiff = setRepoOwner.OutputStateAndDiff();
// Assert
Assert.That(setRepoOwner.Success, Is.True);
- this.AssertSettingsRepoHasChanged(name: (isPartial ?? true) ? MockName : null, owner: MockOwner);
+ this.AssertSettingsHasChanged(name: (isPartial ?? true) ? MockName : null, owner: MockOwner);
+ this.AssertStateAndSettingsAreEqual(this.CurrentSettings, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.EqualTo(new List() { "settings" }));
+ this.AssertStateAction(stateAndDiff.State, isPartial);
}
}
+ ///
+ /// Tests the Set operation without diff.
+ ///
+ /// Async task.
[Test]
- public async Task DscSettingsResource_Test_Success(bool? isPartial)
+ [TestCase(true)]
+ [TestCase(false)]
+ [TestCase(null)]
+ public async Task DscSettingsResource_SetWithoutDiff_Success(bool? isPartial)
{
// Arrange
this.ResetSettingsToDefaultValues();
var command = new DscSettingsCommand();
- var currentSettings = this.CurrentSettings;
- currentSettings.WindowsPackageManagerRepository.Name = MockName;
- UserSettings.SaveSettings(currentSettings);
- // TODO
+ // Part 1: Update settings repo name only
+ {
+ // Arrange
+ this.UpdateSettings(name: MockName);
+ var currentSettingsBeforeExecute = this.CurrentSettings;
+
+ // Act
+ var setRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var stateAndDiff = setRepoName.OutputStateAndDiff();
+
+ // Assert
+ Assert.That(setRepoName.Success, Is.True);
+ this.AssertSettingsHasChanged(name: MockName);
+ this.AssertStateAndSettingsAreEqual(currentSettingsBeforeExecute, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.Empty);
+ this.AssertStateAction(stateAndDiff.State, isPartial);
+ }
+
+ // Part 2: Now update settings repo owner only
+ {
+ // Arrange
+ var defaultRepoName = this.DefaultSettings.WindowsPackageManagerRepository.Name;
+ this.UpdateSettings(name: (isPartial ?? true) ? null : defaultRepoName, owner: MockOwner);
+ var currentSettingsBeforeExecute = this.CurrentSettings;
+
+ // Act
+ var setRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var stateAndDiff = setRepoOwner.OutputStateAndDiff();
+
+ // Assert
+ Assert.That(setRepoOwner.Success, Is.True);
+ this.AssertSettingsHasChanged(name: (isPartial ?? true) ? MockName : null, owner: MockOwner);
+ this.AssertStateAndSettingsAreEqual(currentSettingsBeforeExecute, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.Empty);
+ this.AssertStateAction(stateAndDiff.State, isPartial);
+ }
+ }
+
+ ///
+ /// Tests the Test operation with diff.
+ ///
+ /// Async task.
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ [TestCase(null)]
+ public async Task DscSettingsResource_TestWithDiff_Success(bool? isPartial)
+ {
+ // Arrange
+ this.ResetSettingsToDefaultValues();
+ var command = new DscSettingsCommand();
+
+ // Part 1: Test settings repo name only
+ {
+ // Act
+ var testRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var stateAndDiff = testRepoName.OutputStateAndDiff();
+
+ // Assert
+ Assert.That(testRepoName.Success, Is.True);
+ this.AssertSettingsAreEqual(this.DefaultSettings, this.CurrentSettings);
+ this.AssertStateAndSettingsAreEqual(this.DefaultSettings, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.EqualTo(new List() { "settings" }));
+ this.AssertStateAction(stateAndDiff.State, isPartial);
+ }
+
+ // Part 2: Now test settings repo owner only
+ {
+ // Act
+ var testRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var stateAndDiff = testRepoOwner.OutputStateAndDiff();
+
+ // Assert
+ Assert.That(testRepoOwner.Success, Is.True);
+ this.AssertSettingsAreEqual(this.DefaultSettings, this.CurrentSettings);
+ this.AssertStateAndSettingsAreEqual(this.DefaultSettings, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.EqualTo(new List() { "settings" }));
+ this.AssertStateAction(stateAndDiff.State, isPartial);
+ }
}
+ ///
+ /// Tests the Test operation without diff.
+ ///
+ /// Async task.
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ [TestCase(null)]
+ public async Task DscSettingsResource_TestWithoutDiff_Success(bool? isPartial)
+ {
+ // Arrange
+ this.ResetSettingsToDefaultValues();
+ var command = new DscSettingsCommand();
+
+ // Part 1: Test settings repo name only
+ {
+ // Arrange
+ this.UpdateSettings(name: MockName);
+
+ // Act
+ var testRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var stateAndDiff = testRepoName.OutputStateAndDiff();
+
+ // Assert
+ Assert.That(testRepoName.Success, Is.True);
+ this.AssertStateAndSettingsAreEqual(this.CurrentSettings, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.Empty);
+ this.AssertStateAction(stateAndDiff.State, isPartial);
+ }
+
+ // Part 2: Now test settings repo owner only
+ {
+ // Arrange
+ var defaultRepoName = this.DefaultSettings.WindowsPackageManagerRepository.Name;
+ this.UpdateSettings(name: (isPartial ?? true) ? null : defaultRepoName, owner: MockOwner);
+
+ // Act
+ var testRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var stateAndDiff = testRepoOwner.OutputStateAndDiff();
+
+ // Assert
+ Assert.That(testRepoOwner.Success, Is.True);
+ this.AssertStateAndSettingsAreEqual(this.CurrentSettings, stateAndDiff.State);
+ Assert.That(stateAndDiff.Diff, Is.Empty);
+ this.AssertStateAction(stateAndDiff.State, isPartial);
+ }
+ }
+
+ ///
+ /// Resets the settings to default values.
+ ///
private void ResetSettingsToDefaultValues()
{
UserSettings.SaveSettings(this.DefaultSettings);
}
+ ///
+ /// Updates the settings with the provided name and owner.
+ ///
+ /// Optional name for the repository.
+ /// Optional owner for the repository.
+ private void UpdateSettings(string name = null, string owner = null)
+ {
+ var settings = this.CurrentSettings;
+ if (name != null)
+ {
+ settings.WindowsPackageManagerRepository.Name = name;
+ }
+
+ if (owner != null)
+ {
+ settings.WindowsPackageManagerRepository.Owner = owner;
+ }
+
+ UserSettings.SaveSettings(settings);
+ }
+
+ ///
+ /// Create the response for the Get operation.
+ ///
+ /// Optional parameter to indicate if the response is partial.
+ /// A JSON string representing the response.
private string CreateGetResponse(bool? isPartial = null)
{
return this.CreateResourceObject(UserSettings.ToJson(), isPartial);
}
+ ///
+ /// Creates the operation input.
+ ///
+ /// Optional name for the repository.
+ /// Optional owner for the repository.
+ /// Optional parameter to indicate if the input is partial.
+ /// A JSON string representing the operation input.
private string CreateInput(string name = null, string owner = null, bool? isPartial = null)
{
var repo = new JObject();
@@ -182,6 +380,12 @@ private string CreateInput(string name = null, string owner = null, bool? isPart
return this.CreateResourceObject(input, isPartial);
}
+ ///
+ /// Create the resource object for the operation.
+ ///
+ /// Settings to include in the resource object.
+ /// Optional parameter to indicate if the resource object is partial.
+ /// A JSON string representing the resource object.
private string CreateResourceObject(JObject settings, bool? isPartial)
{
var resourceObject = new SettingsResourceObject
@@ -192,7 +396,12 @@ private string CreateResourceObject(JObject settings, bool? isPartial)
return JObject.FromObject(resourceObject).ToString(Formatting.None);
}
- private void AssertSettingsRepoHasChanged(string name = null, string owner = null)
+ ///
+ /// Asserts that the current settings have changed based on the provided name and owner.
+ ///
+ /// Optional name for the repository.
+ /// Optional owner for the repository.
+ private void AssertSettingsHasChanged(string name = null, string owner = null)
{
var currentSettings = this.CurrentSettings;
var defaultSettings = this.DefaultSettings;
@@ -206,8 +415,46 @@ private void AssertSettingsRepoHasChanged(string name = null, string owner = nul
defaultSettings.WindowsPackageManagerRepository.Owner = owner;
}
- var currentSettingsJson = JToken.FromObject(currentSettings);
- var defaultSettingsJson = JToken.FromObject(defaultSettings);
- Assert.That(JToken.DeepEquals(currentSettingsJson, defaultSettingsJson), Is.True);
+ this.AssertSettingsAreEqual(defaultSettings, currentSettings);
+ }
+
+ ///
+ /// Asserts that the state and settings are equal.
+ ///
+ /// Settings manifest to compare against.
+ /// Output state to compare.
+ private void AssertStateAndSettingsAreEqual(SettingsManifest settings, SettingsResourceObject state)
+ {
+ var stateSettings = state.Settings.ToObject();
+ this.AssertSettingsAreEqual(settings, stateSettings);
+ }
+
+ ///
+ /// Asserts that the state action is as expected.
+ ///
+ /// Output state to check.
+ /// Optional parameter to indicate if the state is partial.
+ private void AssertStateAction(SettingsResourceObject state, bool? isPartial = null)
+ {
+ if (!isPartial.HasValue || isPartial.Value)
+ {
+ Assert.That(state.Action, Is.EqualTo(SettingsResourceObject.ActionPartial));
+ }
+ else
+ {
+ Assert.That(state.Action, Is.EqualTo(SettingsResourceObject.ActionFull));
+ }
+ }
+
+ ///
+ /// Asserts that two settings manifests are equal.
+ ///
+ /// Expected settings manifest.
+ /// Actual settings manifest.
+ private void AssertSettingsAreEqual(SettingsManifest expected, SettingsManifest actual)
+ {
+ var expectedJson = JToken.FromObject(expected);
+ var actualJson = JToken.FromObject(actual);
+ Assert.That(JToken.DeepEquals(expectedJson, actualJson), Is.True);
}
}
From bc41cde4f0bf16e3d04c08e1a3434ddd2e450659 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Mon, 16 Jun 2025 11:00:01 -0700
Subject: [PATCH 12/24] Assert InDesiredState for Test operation
---
.../WingetCreateTests/UnitTests/DscSettingsCommandTests.cs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
index a31f2feb..fda1f662 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
@@ -248,6 +248,7 @@ public async Task DscSettingsResource_TestWithDiff_Success(bool? isPartial)
this.AssertStateAndSettingsAreEqual(this.DefaultSettings, stateAndDiff.State);
Assert.That(stateAndDiff.Diff, Is.EqualTo(new List() { "settings" }));
this.AssertStateAction(stateAndDiff.State, isPartial);
+ Assert.That(stateAndDiff.State.InDesiredState, Is.False);
}
// Part 2: Now test settings repo owner only
@@ -262,6 +263,7 @@ public async Task DscSettingsResource_TestWithDiff_Success(bool? isPartial)
this.AssertStateAndSettingsAreEqual(this.DefaultSettings, stateAndDiff.State);
Assert.That(stateAndDiff.Diff, Is.EqualTo(new List() { "settings" }));
this.AssertStateAction(stateAndDiff.State, isPartial);
+ Assert.That(stateAndDiff.State.InDesiredState, Is.False);
}
}
@@ -293,6 +295,7 @@ public async Task DscSettingsResource_TestWithoutDiff_Success(bool? isPartial)
this.AssertStateAndSettingsAreEqual(this.CurrentSettings, stateAndDiff.State);
Assert.That(stateAndDiff.Diff, Is.Empty);
this.AssertStateAction(stateAndDiff.State, isPartial);
+ Assert.That(stateAndDiff.State.InDesiredState, Is.True);
}
// Part 2: Now test settings repo owner only
@@ -310,6 +313,7 @@ public async Task DscSettingsResource_TestWithoutDiff_Success(bool? isPartial)
this.AssertStateAndSettingsAreEqual(this.CurrentSettings, stateAndDiff.State);
Assert.That(stateAndDiff.Diff, Is.Empty);
this.AssertStateAction(stateAndDiff.State, isPartial);
+ Assert.That(stateAndDiff.State.InDesiredState, Is.True);
}
}
From 0e71f5aa4417a3edaa8f37658f52f04c564761bb Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Mon, 16 Jun 2025 15:46:54 -0700
Subject: [PATCH 13/24] Addressed comments
---
src/WingetCreateCLI/Commands/DscCommand.cs | 89 +++++++++----------
.../DscCommands/DscSettingsCommand.cs | 7 +-
.../Properties/Resources.Designer.cs | 26 +++++-
src/WingetCreateCLI/Properties/Resources.resx | 18 ++--
.../UnitTests/DscCommandTests.cs | 34 +++----
.../UnitTests/DscSettingsCommandTests.cs | 24 ++++-
6 files changed, 125 insertions(+), 73 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index c7bae00c..dd46c9e1 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -23,34 +23,40 @@ public class DscCommand : BaseCommand
public override bool RequiresGitHubToken => false;
///
- /// Gets or sets the unbound arguments that exist after the positional parameters.
+ /// Gets or sets the name of the resource to be managed by the dsc command.
///
- [Value(2, Hidden = true)]
- public IList UnboundArgs { get; set; } = new List();
+ [Value(0, MetaName = "ResourceName", Required = true, HelpText = "DscResourceName_HelpText", ResourceType = typeof(Resources))]
+ public string ResourceName { get; set; }
///
- /// Gets or sets the input for the dsc Get operation.
+ /// Gets or sets the input for the dsc command.
+ ///
+ [Value(1, MetaName = "Input", Required = false, HelpText = "DscInput_HelpText", ResourceType = typeof(Resources))]
+ public string Input { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to execute the dsc Get operation.
///
[Option('g', "get", SetName = "GetMethod", HelpText = "DscGet_HelpText", ResourceType = typeof(Resources))]
- public string Get { get; set; }
+ public bool Get { get; set; }
///
- /// Gets or sets the input for the dsc Set operation.
+ /// Gets or sets a value indicating whether to execute the dsc Set operation.
///
[Option('s', "set", SetName = "SetMethod", HelpText = "DscSet_HelpText", ResourceType = typeof(Resources))]
- public string Set { get; set; }
+ public bool Set { get; set; }
///
- /// Gets or sets the input for the dsc Test operation.
+ /// Gets or sets a value indicating whether to execute the dsc Test operation.
///
[Option('t', "test", SetName = "TestMethod", HelpText = "DscTest_HelpText", ResourceType = typeof(Resources))]
- public string Test { get; set; }
+ public bool Test { get; set; }
///
- /// Gets or sets the input for the dsc Export operation.
+ /// Gets or sets a value indicating whether to execute the dsc Export operation.
///
[Option('e', "export", SetName = "ExportMethod", HelpText = "DscExport_HelpText", ResourceType = typeof(Resources))]
- public string Export { get; set; }
+ public bool Export { get; set; }
///
/// Gets or sets a value indicating whether to execute the schema command.
@@ -67,51 +73,42 @@ public override async Task Execute()
await Task.CompletedTask;
List dscCommands = [new DscSettingsCommand()];
- var argCommandName = this.UnboundArgs.FirstOrDefault();
- if(string.IsNullOrWhiteSpace(argCommandName))
- {
- Logger.ErrorLocalized(nameof(Resources.DscResourceMissing_Message), string.Join(", ", dscCommands.Select(c => c.CommandName)));
- return false;
- }
-
- var dscCommand = dscCommands.FirstOrDefault(c => c.CommandName.Equals(argCommandName, StringComparison.OrdinalIgnoreCase));
+ var dscCommand = dscCommands.FirstOrDefault(c => c.CommandName.Equals(this.ResourceName, StringComparison.OrdinalIgnoreCase));
if (dscCommand == null)
{
- Logger.ErrorLocalized(nameof(Resources.DscResourceNotFound_Message), argCommandName);
- return false;
- }
-
- if (this.HandleOperation("Get", this.Get, (input) => dscCommand.Get(input)) ||
- this.HandleOperation("Set", this.Set, (input) => dscCommand.Set(input)) ||
- this.HandleOperation("Test", this.Test, (input) => dscCommand.Test(input)) ||
- this.HandleOperation("Export", this.Export, (input) => dscCommand.Export(input)) ||
- (this.Schema && dscCommand.Schema()))
- {
- return true;
- }
-
- Logger.ErrorLocalized(nameof(Resources.DscResourceOperationInvalid_Message));
- return false;
- }
-
- private bool HandleOperation(string name, string arg, Func op)
- {
- if (arg == null)
- {
- // If no argument is provided, then we assume another operation is being requested.
+ var availableResources = string.Join(", ", dscCommands.Select(c => c.CommandName));
+ Logger.ErrorLocalized(nameof(Resources.DscResourceNotFound_Message), this.ResourceName, availableResources);
return false;
}
try
{
- var input = string.IsNullOrWhiteSpace(arg) ? null : JToken.Parse(arg);
- if (!op(input))
+ var input = string.IsNullOrWhiteSpace(this.Input) ? null : JToken.Parse(this.Input);
+ var operations = new (bool, Func)[]
{
- Logger.ErrorLocalized(nameof(Resources.DscResourceOperationFailed_Message));
- return false;
+ (this.Get, () => dscCommand.Get(input)),
+ (this.Set, () => dscCommand.Set(input)),
+ (this.Test, () => dscCommand.Test(input)),
+ (this.Export, () => dscCommand.Export(input)),
+ (this.Schema, () => dscCommand.Schema()),
+ };
+
+ foreach (var (methodFlag, methodAction) in operations)
+ {
+ if (methodFlag)
+ {
+ if (!methodAction())
+ {
+ Logger.ErrorLocalized(nameof(Resources.DscResourceOperationFailed_Message));
+ return false;
+ }
+
+ return true;
+ }
}
- return true;
+ Logger.ErrorLocalized(nameof(Resources.DscResourceOperationInvalid_Message));
+ return false;
}
catch (Exception ex)
{
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
index 1241be4f..82eca4b6 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -3,7 +3,10 @@
namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
+using System;
+using Microsoft.WingetCreateCLI.Logging;
using Microsoft.WingetCreateCLI.Models.DscModels;
+using Microsoft.WingetCreateCLI.Properties;
using Newtonsoft.Json.Linq;
///
@@ -25,6 +28,7 @@ public override bool Set(JToken input)
{
if (input == null)
{
+ Logger.ErrorLocalized(nameof(Resources.DscInputRequired_Message), nameof(this.Set));
return false;
}
@@ -48,8 +52,9 @@ public override bool Set(JToken input)
///
public override bool Test(JToken input)
{
- if ( input == null)
+ if (input == null)
{
+ Logger.ErrorLocalized(nameof(Resources.DscInputRequired_Message), nameof(this.Test));
return false;
}
diff --git a/src/WingetCreateCLI/Properties/Resources.Designer.cs b/src/WingetCreateCLI/Properties/Resources.Designer.cs
index 79bf95fb..917befc3 100644
--- a/src/WingetCreateCLI/Properties/Resources.Designer.cs
+++ b/src/WingetCreateCLI/Properties/Resources.Designer.cs
@@ -763,16 +763,34 @@ public static string DscGet_HelpText {
}
///
- /// Looks up a localized string similar to DSC resource is missing: {0}.
+ /// Looks up a localized string similar to The input for the DSC resource.
///
- public static string DscResourceMissing_Message {
+ public static string DscInput_HelpText {
get {
- return ResourceManager.GetString("DscResourceMissing_Message", resourceCulture);
+ return ResourceManager.GetString("DscInput_HelpText", resourceCulture);
}
}
///
- /// Looks up a localized string similar to DSC resource not found: {0}.
+ /// Looks up a localized string similar to The input for the {0} DSC operation is required..
+ ///
+ public static string DscInputRequired_Message {
+ get {
+ return ResourceManager.GetString("DscInputRequired_Message", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The name of the DSC resource to manage.
+ ///
+ public static string DscResourceName_HelpText {
+ get {
+ return ResourceManager.GetString("DscResourceName_HelpText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to DSC resource not found: {0}. Valid resources: {1}.
///
public static string DscResourceNotFound_Message {
get {
diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx
index 288268a9..2224df3b 100644
--- a/src/WingetCreateCLI/Properties/Resources.resx
+++ b/src/WingetCreateCLI/Properties/Resources.resx
@@ -1424,13 +1424,19 @@ Warning: Using this argument may result in the token being logged. Consider an a
Execute the Schema command
-
- DSC resource not found: {0}
- {Locked='{0}'} {0} is replaced by the DSC resource name
+
+ The name of the DSC resource to manage
+
+
+ The input for the DSC resource
-
- DSC resource is missing: {0}
- {Locked='{0}'} {0} is replaced by the available DSC resource names
+
+ The input for the {0} DSC operation is required.
+ {Locked="{0}"} {0} is replaced by the DSC operation name
+
+
+ DSC resource not found: {0}. Valid resources: {1}
+ {Locked="{0}", "{1}"} {0} is replaced by the DSC resource name, {1} is replaced by the list of valid resourcesInvalid operation for the DSC resource
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
index e83c089c..26e63bf3 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
@@ -26,6 +26,10 @@ public void OneTimeSetUp()
Logger.Initialize();
}
+ ///
+ /// Tests the a successful execution of the dsc command.
+ ///
+ /// Async Task.
[Test]
public async Task DscSettingsResource_Success()
{
@@ -39,34 +43,30 @@ public async Task DscSettingsResource_Success()
Assert.That(result.Success, Is.True);
}
- [Test]
- public async Task DscResourceMissing_ErrorMessage()
- {
- // Arrange
- List dscCommands = [new DscSettingsCommand()];
-
- // Act
- var result = await TestUtils.ExecuteDscCommandAsync([]);
-
- // Assert
- Assert.That(result.Success, Is.False);
- Assert.That(result.Output, Does.Contain(string.Format(Resources.DscResourceMissing_Message, string.Join(", ", dscCommands.Select(c => c.CommandName)))));
- }
-
+ ///
+ /// Tests the error message when a DSC resource is not found.
+ ///
+ /// Async Task.
[Test]
public async Task DscResourceNotFound_ErrorMessage()
{
// Arrange
var dscResourceName = "ResourceNotFound";
+ List dscCommands = [new DscSettingsCommand()];
+ var availableResources = string.Join(", ", dscCommands.Select(c => c.CommandName));
// Act
var result = await TestUtils.ExecuteDscCommandAsync([dscResourceName]);
// Assert
Assert.That(result.Success, Is.False);
- Assert.That(result.Output, Does.Contain(string.Format(Resources.DscResourceNotFound_Message, dscResourceName)));
+ Assert.That(result.Output, Does.Contain(string.Format(Resources.DscResourceNotFound_Message, dscResourceName, availableResources)));
}
+ ///
+ /// Tests the error message when an invalid operation is attempted on a DSC resource.
+ ///
+ /// Async Task.
[Test]
public async Task DscResourceInvalidOperation_ErrorMessage()
{
@@ -81,6 +81,10 @@ public async Task DscResourceInvalidOperation_ErrorMessage()
Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationInvalid_Message));
}
+ ///
+ /// Tests the error message when a DSC resource operation fails.
+ ///
+ /// Async Task.
[Test]
public async Task DscSettingsResourceFailedOperation_ErrorMessage()
{
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
index fda1f662..9061d148 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
@@ -124,12 +124,31 @@ public async Task DscSettingsResource_SetEmpty_Fail()
// Assert
Assert.That(result.Success, Is.False);
- Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationFailed_Message));
+ Assert.That(result.Output, Does.Contain(string.Format(Resources.DscInputRequired_Message, nameof(DscSettingsCommand.Set))));
+ }
+
+ ///
+ /// Tests the Test operation with an empty input.
+ ///
+ /// Async task.
+ [Test]
+ public async Task DscSettingsResource_TestEmpty_Fail()
+ {
+ // Arrange
+ var command = new DscSettingsCommand();
+
+ // Act
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", string.Empty]);
+
+ // Assert
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Output, Does.Contain(string.Format(Resources.DscInputRequired_Message, nameof(DscSettingsCommand.Test))));
}
///
/// Tests the Set operation with diff.
///
+ /// Optional parameter to indicate if the operation is partial.
/// Async task.
[Test]
[TestCase(true)]
@@ -173,6 +192,7 @@ public async Task DscSettingsResource_SetWithDiff_Success(bool? isPartial)
///
/// Tests the Set operation without diff.
///
+ /// Optional parameter to indicate if the operation is partial.
/// Async task.
[Test]
[TestCase(true)]
@@ -225,6 +245,7 @@ public async Task DscSettingsResource_SetWithoutDiff_Success(bool? isPartial)
///
/// Tests the Test operation with diff.
///
+ /// Optional parameter to indicate if the operation is partial.
/// Async task.
[Test]
[TestCase(true)]
@@ -270,6 +291,7 @@ public async Task DscSettingsResource_TestWithDiff_Success(bool? isPartial)
///
/// Tests the Test operation without diff.
///
+ /// Optional parameter to indicate if the operation is partial.
/// Async task.
[Test]
[TestCase(true)]
From f44e819200bfbff77ee639514c8da7267f72735c Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Mon, 16 Jun 2025 15:56:25 -0700
Subject: [PATCH 14/24] Addressed comments
---
src/WingetCreateCLI/Commands/BaseCommand.cs | 4 ++--
src/WingetCreateCLI/Commands/CacheCommand.cs | 2 +-
src/WingetCreateCLI/Commands/DscCommand.cs | 2 +-
src/WingetCreateCLI/Commands/InfoCommand.cs | 2 +-
src/WingetCreateCLI/Commands/SettingsCommand.cs | 2 +-
.../microsoft.winget-create.settings.dsc.resource.json | 6 ++----
.../Models/DscModels/SettingsResourceObject.cs | 2 +-
src/WingetCreateCLI/Program.cs | 2 +-
src/WingetCreateCLI/Properties/Resources.Designer.cs | 10 +++++-----
src/WingetCreateCLI/Properties/Resources.resx | 10 +++++-----
10 files changed, 20 insertions(+), 22 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/BaseCommand.cs b/src/WingetCreateCLI/Commands/BaseCommand.cs
index 1099dd10..39284e4d 100644
--- a/src/WingetCreateCLI/Commands/BaseCommand.cs
+++ b/src/WingetCreateCLI/Commands/BaseCommand.cs
@@ -62,9 +62,9 @@ public abstract class BaseCommand
public static string Extension => Serialization.ManifestSerializer.AssociatedFileExtension;
///
- /// Gets a value indicating whether or not the command requires a GitHub token to be set.
+ /// Gets a value indicating whether or not the command accepts a GitHub token.
///
- public virtual bool RequiresGitHubToken { get; } = true;
+ public virtual bool AcceptsGitHubToken { get; } = true;
///
/// Gets or sets the GitHub token used to submit a pull request on behalf of the user.
diff --git a/src/WingetCreateCLI/Commands/CacheCommand.cs b/src/WingetCreateCLI/Commands/CacheCommand.cs
index dfbee690..a66f6408 100644
--- a/src/WingetCreateCLI/Commands/CacheCommand.cs
+++ b/src/WingetCreateCLI/Commands/CacheCommand.cs
@@ -39,7 +39,7 @@ public class CacheCommand : BaseCommand
public bool Open { get; set; }
///
- public override bool RequiresGitHubToken => false;
+ public override bool AcceptsGitHubToken => false;
///
/// Executes the cache command flow.
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index dd46c9e1..88f84477 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -20,7 +20,7 @@ namespace Microsoft.WingetCreateCLI.Commands;
public class DscCommand : BaseCommand
{
///
- public override bool RequiresGitHubToken => false;
+ public override bool AcceptsGitHubToken => false;
///
/// Gets or sets the name of the resource to be managed by the dsc command.
diff --git a/src/WingetCreateCLI/Commands/InfoCommand.cs b/src/WingetCreateCLI/Commands/InfoCommand.cs
index 2fe5f8fb..0f9a9c13 100644
--- a/src/WingetCreateCLI/Commands/InfoCommand.cs
+++ b/src/WingetCreateCLI/Commands/InfoCommand.cs
@@ -21,7 +21,7 @@ namespace Microsoft.WingetCreateCLI.Commands
public class InfoCommand : BaseCommand
{
///
- public override bool RequiresGitHubToken => false;
+ public override bool AcceptsGitHubToken => false;
///
/// Executes the info command flow.
diff --git a/src/WingetCreateCLI/Commands/SettingsCommand.cs b/src/WingetCreateCLI/Commands/SettingsCommand.cs
index 28025699..c3465143 100644
--- a/src/WingetCreateCLI/Commands/SettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/SettingsCommand.cs
@@ -23,7 +23,7 @@ namespace Microsoft.WingetCreateCLI.Commands
public class SettingsCommand : BaseCommand
{
///
- public override bool RequiresGitHubToken => false;
+ public override bool AcceptsGitHubToken => false;
///
/// Executes the token command flow.
diff --git a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
index bcb8d6b7..ee979604 100644
--- a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
+++ b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
@@ -7,8 +7,7 @@
"args": [
"dsc",
"settings",
- "--export",
- "'{}'"
+ "--export"
]
},
"get": {
@@ -17,8 +16,7 @@
"args": [
"dsc",
"settings",
- "--get",
- "'{}'"
+ "--get"
]
},
"schema": {
diff --git a/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
index a86b1fb5..47b4407d 100644
--- a/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
+++ b/src/WingetCreateCLI/Models/DscModels/SettingsResourceObject.cs
@@ -50,7 +50,7 @@ public override JObject GetProperties()
["type"] = "string",
["enum"] = new JArray(ActionFull, ActionPartial),
};
- return baseProperties; ;
+ return baseProperties;
}
///
diff --git a/src/WingetCreateCLI/Program.cs b/src/WingetCreateCLI/Program.cs
index f2871bdf..16351c4f 100644
--- a/src/WingetCreateCLI/Program.cs
+++ b/src/WingetCreateCLI/Program.cs
@@ -72,7 +72,7 @@ private static async Task Main(string[] args)
}
// Do not load github client for commands that do not deal with a GitHub token.
- if (command.RequiresGitHubToken)
+ if (command.AcceptsGitHubToken)
{
if (await command.LoadGitHubClient())
{
diff --git a/src/WingetCreateCLI/Properties/Resources.Designer.cs b/src/WingetCreateCLI/Properties/Resources.Designer.cs
index 917befc3..438cb910 100644
--- a/src/WingetCreateCLI/Properties/Resources.Designer.cs
+++ b/src/WingetCreateCLI/Properties/Resources.Designer.cs
@@ -736,7 +736,7 @@ public static string DownloadInstaller_Message {
}
///
- /// Looks up a localized string similar to DSC v3 resource commands..
+ /// Looks up a localized string similar to DSC v3 resource commands.
///
public static string DscCommand_HelpText {
get {
@@ -772,7 +772,7 @@ public static string DscInput_HelpText {
}
///
- /// Looks up a localized string similar to The input for the {0} DSC operation is required..
+ /// Looks up a localized string similar to The input for the {0} DSC operation is required.
///
public static string DscInputRequired_Message {
get {
@@ -817,7 +817,7 @@ public static string DscResourceOperationInvalid_Message {
}
///
- /// Looks up a localized string similar to The action used to apply the settings..
+ /// Looks up a localized string similar to The action used to apply the settings.
///
public static string DscResourcePropertyDescriptionAction {
get {
@@ -826,7 +826,7 @@ public static string DscResourcePropertyDescriptionAction {
}
///
- /// Looks up a localized string similar to Indicates whether an instance is in the desired state..
+ /// Looks up a localized string similar to Indicates whether an instance is in the desired state.
///
public static string DscResourcePropertyDescriptionInDesiredState {
get {
@@ -835,7 +835,7 @@ public static string DscResourcePropertyDescriptionInDesiredState {
}
///
- /// Looks up a localized string similar to The settings content..
+ /// Looks up a localized string similar to The settings content.
///
public static string DscResourcePropertyDescriptionSettings {
get {
diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx
index 2224df3b..213b9e06 100644
--- a/src/WingetCreateCLI/Properties/Resources.resx
+++ b/src/WingetCreateCLI/Properties/Resources.resx
@@ -1407,7 +1407,7 @@ Warning: Using this argument may result in the token being logged. Consider an a
The forked repository could not be synced with the upstream commits due to a merge conflict. Resolve conflicts manually and try again.
- DSC v3 resource commands.
+ DSC v3 resource commandsGet the resource state
@@ -1431,7 +1431,7 @@ Warning: Using this argument may result in the token being logged. Consider an a
The input for the DSC resource
- The input for the {0} DSC operation is required.
+ The input for the {0} DSC operation is required{Locked="{0}"} {0} is replaced by the DSC operation name
@@ -1445,12 +1445,12 @@ Warning: Using this argument may result in the token being logged. Consider an a
DSC resource operation failed
- Indicates whether an instance is in the desired state.
+ Indicates whether an instance is in the desired state
- The settings content.
+ The settings content
- The action used to apply the settings.
+ The action used to apply the settings
\ No newline at end of file
From 617ec32318613f789bf475aa4bba5aa7c9dbc461 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 17 Jun 2025 08:37:29 -0700
Subject: [PATCH 15/24] Addressed comments
---
src/WingetCreateCLI/Commands/DscCommand.cs | 4 ++--
src/WingetCreateCLI/Properties/Resources.Designer.cs | 10 +++++-----
src/WingetCreateCLI/Properties/Resources.resx | 6 +++---
.../WingetCreateTests/UnitTests/DscCommandTests.cs | 6 +++---
.../UnitTests/DscSettingsCommandTests.cs | 4 ++--
5 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index 88f84477..8080326f 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -77,7 +77,7 @@ public override async Task Execute()
if (dscCommand == null)
{
var availableResources = string.Join(", ", dscCommands.Select(c => c.CommandName));
- Logger.ErrorLocalized(nameof(Resources.DscResourceNotFound_Message), this.ResourceName, availableResources);
+ Logger.ErrorLocalized(nameof(Resources.DscResourceNameNotFound_Message), this.ResourceName, availableResources);
return false;
}
@@ -107,7 +107,7 @@ public override async Task Execute()
}
}
- Logger.ErrorLocalized(nameof(Resources.DscResourceOperationInvalid_Message));
+ Logger.ErrorLocalized(nameof(Resources.DscResourceOperationNotSpecified_Message));
return false;
}
catch (Exception ex)
diff --git a/src/WingetCreateCLI/Properties/Resources.Designer.cs b/src/WingetCreateCLI/Properties/Resources.Designer.cs
index 438cb910..a7d5e75f 100644
--- a/src/WingetCreateCLI/Properties/Resources.Designer.cs
+++ b/src/WingetCreateCLI/Properties/Resources.Designer.cs
@@ -792,9 +792,9 @@ public static string DscResourceName_HelpText {
///
/// Looks up a localized string similar to DSC resource not found: {0}. Valid resources: {1}.
///
- public static string DscResourceNotFound_Message {
+ public static string DscResourceNameNotFound_Message {
get {
- return ResourceManager.GetString("DscResourceNotFound_Message", resourceCulture);
+ return ResourceManager.GetString("DscResourceNameNotFound_Message", resourceCulture);
}
}
@@ -808,11 +808,11 @@ public static string DscResourceOperationFailed_Message {
}
///
- /// Looks up a localized string similar to Invalid operation for the DSC resource.
+ /// Looks up a localized string similar to No operation specified. Use --help to see available operations..
///
- public static string DscResourceOperationInvalid_Message {
+ public static string DscResourceOperationNotSpecified_Message {
get {
- return ResourceManager.GetString("DscResourceOperationInvalid_Message", resourceCulture);
+ return ResourceManager.GetString("DscResourceOperationNotSpecified_Message", resourceCulture);
}
}
diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx
index 213b9e06..24b89f3b 100644
--- a/src/WingetCreateCLI/Properties/Resources.resx
+++ b/src/WingetCreateCLI/Properties/Resources.resx
@@ -1434,12 +1434,12 @@ Warning: Using this argument may result in the token being logged. Consider an a
The input for the {0} DSC operation is required{Locked="{0}"} {0} is replaced by the DSC operation name
-
+
DSC resource not found: {0}. Valid resources: {1}{Locked="{0}", "{1}"} {0} is replaced by the DSC resource name, {1} is replaced by the list of valid resources
-
- Invalid operation for the DSC resource
+
+ No operation specified. Use --help to see available operations.DSC resource operation failed
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
index 26e63bf3..36792888 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
@@ -37,7 +37,7 @@ public async Task DscSettingsResource_Success()
var command = new DscSettingsCommand();
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get"]);
// Assert
Assert.That(result.Success, Is.True);
@@ -60,7 +60,7 @@ public async Task DscResourceNotFound_ErrorMessage()
// Assert
Assert.That(result.Success, Is.False);
- Assert.That(result.Output, Does.Contain(string.Format(Resources.DscResourceNotFound_Message, dscResourceName, availableResources)));
+ Assert.That(result.Output, Does.Contain(string.Format(Resources.DscResourceNameNotFound_Message, dscResourceName, availableResources)));
}
///
@@ -78,7 +78,7 @@ public async Task DscResourceInvalidOperation_ErrorMessage()
// Assert
Assert.That(result.Success, Is.False);
- Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationInvalid_Message));
+ Assert.That(result.Output, Does.Contain(Resources.DscResourceOperationNotSpecified_Message));
}
///
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
index 9061d148..0789dd6b 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
@@ -78,7 +78,7 @@ public async Task DscSettingsResource_Get_Success()
var command = new DscSettingsCommand();
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get"]);
var state = result.OutputState();
// Assert
@@ -99,7 +99,7 @@ public async Task DscSettingsResource_Export_Success()
var command = new DscSettingsCommand();
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--export", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--export"]);
var state = result.OutputState();
// Assert
From 15e6ffbaab04aaacf3ee1c7292168a44657819f3 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 17 Jun 2025 08:39:20 -0700
Subject: [PATCH 16/24] Addressed comments
---
.../WingetCreateTests/UnitTests/DscCommandTests.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
index 36792888..ffee0f15 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
@@ -64,11 +64,11 @@ public async Task DscResourceNotFound_ErrorMessage()
}
///
- /// Tests the error message when an invalid operation is attempted on a DSC resource.
+ /// Tests the error message when a DSC resource operation is not specified.
///
/// Async Task.
[Test]
- public async Task DscResourceInvalidOperation_ErrorMessage()
+ public async Task DscResourceOperationNotSpecified_ErrorMessage()
{
// Arrange
var command = new DscSettingsCommand();
From e8d496ed9ec3de6378a9d955b2053670d3ad2264 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 17 Jun 2025 08:57:44 -0700
Subject: [PATCH 17/24] Fix resource file
---
.../microsoft.winget-create.settings.dsc.resource.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
index ee979604..b62b4c25 100644
--- a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
+++ b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json",
- "description": "Allows management of settings state via the DSC v3 command line interface protocol. See the help link for details.",
+ "description": "Allows management of settings state via the DSC v3 command line interface protocol.",
"export": {
"executable": "wingetcreate",
"input": "stdin",
From d968ed705bd72875795862d96583d3c0ef1181f5 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 20 Jun 2025 10:44:38 -0700
Subject: [PATCH 18/24] Adding docs
---
README.md | 1 +
doc/dsc.md | 25 +++++++++++++++++++++++
doc/dsc/settings.md | 49 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 75 insertions(+)
create mode 100644 doc/dsc.md
create mode 100644 doc/dsc/settings.md
diff --git a/README.md b/README.md
index c6a69910..19dd4cb6 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@ choco install wingetcreate
| [Settings](doc/settings.md) | Command for editing the settings file configurations |
| [Cache](doc/cache.md) | Command for managing downloaded installers stored in cache
| [Info](doc/info.md) | Displays information about the client |
+| [Dsc](doc/dsc.md) | DSC v3 resource commands |
| [-?](doc/help.md) | Displays command line help |
Click on the individual commands to learn more.
diff --git a/doc/dsc.md b/doc/dsc.md
new file mode 100644
index 00000000..c1a5a814
--- /dev/null
+++ b/doc/dsc.md
@@ -0,0 +1,25 @@
+# dsc command (Winget-Create)
+
+The **dsc** command is the entry point for using DSC (Desired State Coonfiguration) with Winget-Create.
+
+## Usage
+
+`wingetcreate.exe dsc []`
+
+## Resources
+| Resource | Type | Description | --get | --set | --test | --export | --schema | Link |
+| ---- | ------ | ------------| ----- | ----- | ----- | ----- | ----- | ----- |
+| `settings` | `Microsoft.WinGetCreate/Settings` | Manage the settings for Winget-Create | ✅ | ✅ | ✅ | ✅ | ✅ | [Details](dsc/settings.md) |
+
+## Arguments
+
+The following arguments are available:
+
+|
Argument
| Description |
+| --------------------------------------- | ------------|
+| **-g, --get** | Get the resource state
+| **-s, --set** | Set the resource state |
+| **-t, --test** | Test the resource state |
+| **e, --export** | Get all state instances |
+| **--schema** | Execute the Schema command |
+| **-?, --help** | Gets additional help on this command. |
diff --git a/doc/dsc/settings.md b/doc/dsc/settings.md
new file mode 100644
index 00000000..2f43c2bb
--- /dev/null
+++ b/doc/dsc/settings.md
@@ -0,0 +1,49 @@
+# Settings resource
+Manage the settings for Winget-Create
+
+## 📄 Get
+```shell
+PS C:\> wingetcreate dsc settings --get
+{"settings":{"$schema":"https://aka.ms/wingetcreate-settings.schema.0.1.json","Telemetry":{"disable":true},"CleanUp":{"intervalInDays":7,"disable":false},"WindowsPackageManagerRepository":{"owner":"microsoft","name":"winget-pkgs"},"Manifest":{"format":"yaml"},"Visual":{"anonymizePaths":true}}}
+```
+
+## 🖨️ Export
+ℹ️ Settings resource Get and Export operation output states are identical.
+```shell
+PS C:\> wingetcreate dsc settings --export
+{"settings":{"$schema":"https://aka.ms/wingetcreate-settings.schema.0.1.json","Telemetry":{"disable":true},"CleanUp":{"intervalInDays":7,"disable":false},"WindowsPackageManagerRepository":{"owner":"microsoft","name":"winget-pkgs"},"Manifest":{"format":"yaml"},"Visual":{"anonymizePaths":true}}}
+```
+
+## 📝 Set
+- Action `Full`: When action is set to Full, the specified settings will be update accordingly and the remaining settings will be set to their default values.
+- Action `Partial`: When action is set to Partial, only the specified settings will be updated, and the remaining settings will remain unchanged.
+### 🌕 Full
+```shell
+PS C:\> wingetcreate dsc settings --set '{"settings": { "Telemetry": { "disable": false }}, "action": "Full"}'
+{"settings":{"$schema":"https://aka.ms/wingetcreate-settings.schema.0.1.json","Telemetry":{"disable":false},"CleanUp":{"intervalInDays":7,"disable":false},"WindowsPackageManagerRepository":{"owner":"microsoft","name":"winget-pkgs"},"Manifest":{"format":"yaml"},"Visual":{"anonymizePaths":true}},"action":"Full"}
+["settings"]
+```
+
+### 🌗 Partial
+```shell
+PS C:\> wingetcreate dsc settings --set '{"settings": { "Telemetry": { "disable": true }}, "action": "Partial"}'
+{"settings":{"$schema":"https://aka.ms/wingetcreate-settings.schema.0.1.json","Telemetry":{"disable":true},"CleanUp":{"intervalInDays":7,"disable":false},"WindowsPackageManagerRepository":{"owner":"microsoft","name":"winget-pkgs"},"Manifest":{"format":"yaml"},"Visual":{"anonymizePaths":true}},"action":"Partial"}
+["settings"]
+```
+
+## 🧪 Test
+- Action `Full`: When action is set to Full, the specified settings will be tested accordingly, and the remaining settings will be tested against their default values.
+- Action `Partial`: When action is set to Partial, only the specified settings will be tested, and the remaining settings will be omitted from the test.
+### 🌕 Full
+```shell
+PS C:\> wingetcreate dsc settings --test '{"settings": { "Telemetry": { "disable": false }}, "action": "Full"}'
+{"settings":{"$schema":"https://aka.ms/wingetcreate-settings.schema.0.1.json","Telemetry":{"disable":true},"CleanUp":{"intervalInDays":7,"disable":false},"WindowsPackageManagerRepository":{"owner":"microsoft","name":"winget-pkgs"},"Manifest":{"format":"yaml"},"Visual":{"anonymizePaths":true}},"action":"Full","_inDesiredState":false}
+["settings"]
+```
+
+### 🌗 Partial
+```shell
+PS C:\> wingetcreate dsc settings --test '{"settings": { "Telemetry": { "disable": false }}, "action": "Partial"}'
+{"settings":{"$schema":"https://aka.ms/wingetcreate-settings.schema.0.1.json","Telemetry":{"disable":true},"CleanUp":{"intervalInDays":7,"disable":false},"WindowsPackageManagerRepository":{"owner":"microsoft","name":"winget-pkgs"},"Manifest":{"format":"yaml"},"Visual":{"anonymizePaths":true}},"action":"Partial","_inDesiredState":false}
+["settings"]
+```
\ No newline at end of file
From 43fbd1d79539253c097162a1462814fc05868122 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 20 Jun 2025 10:50:05 -0700
Subject: [PATCH 19/24] No wrap
---
doc/dsc.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/dsc.md b/doc/dsc.md
index c1a5a814..d7efd522 100644
--- a/doc/dsc.md
+++ b/doc/dsc.md
@@ -7,7 +7,7 @@ The **dsc** command is the entry point for using DSC (Desired State Coonfigurati
`wingetcreate.exe dsc []`
## Resources
-| Resource | Type | Description | --get | --set | --test | --export | --schema | Link |
+| Resource | Type | Description |
--get
|
--set
|
--test
|
--export
|
--schema
| Link |
| ---- | ------ | ------------| ----- | ----- | ----- | ----- | ----- | ----- |
| `settings` | `Microsoft.WinGetCreate/Settings` | Manage the settings for Winget-Create | ✅ | ✅ | ✅ | ✅ | ✅ | [Details](dsc/settings.md) |
From 5180d132f6b796b8b2a7cd463ba1e5accd4f0ae9 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Mon, 23 Jun 2025 12:24:45 -0700
Subject: [PATCH 20/24] Typo
---
doc/dsc.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/dsc.md b/doc/dsc.md
index d7efd522..4fd05c64 100644
--- a/doc/dsc.md
+++ b/doc/dsc.md
@@ -20,6 +20,6 @@ The following arguments are available:
| **-g, --get** | Get the resource state
| **-s, --set** | Set the resource state |
| **-t, --test** | Test the resource state |
-| **e, --export** | Get all state instances |
+| **-e, --export** | Get all state instances |
| **--schema** | Execute the Schema command |
| **-?, --help** | Gets additional help on this command. |
From c6f0da22fa8a94bc1df530e1b2c30078e48dee51 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 24 Jun 2025 15:52:18 -0700
Subject: [PATCH 21/24] Update version
---
.../microsoft.winget-create.settings.dsc.resource.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
index b62b4c25..f1ee9671 100644
--- a/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
+++ b/src/WingetCreateCLI/DscResources/microsoft.winget-create.settings.dsc.resource.json
@@ -58,5 +58,5 @@
"WinGetCreate"
],
"type": "Microsoft.WinGetCreate/Settings",
- "version": "1.9.0"
+ "version": "1.10.0"
}
\ No newline at end of file
From 90753dead6fb160da8012efcc0e63c9c2256f6f3 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:14:59 -0700
Subject: [PATCH 22/24] Addressed comments
---
.../DscCommands/DscSettingsCommand.cs | 2 +-
.../Models/DscModels/BaseResourceObject.cs | 5 +-
.../Models/DscModels/SettingsFunctionData.cs | 46 +++++++++----------
src/WingetCreateCLI/Properties/Resources.resx | 2 +-
4 files changed, 26 insertions(+), 29 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
index 82eca4b6..8d3affe9 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -41,7 +41,7 @@ public override bool Set(JToken input)
if (!data.Test())
{
data.Output.Settings = data.GetResolvedInput();
- data.WriteOutput();
+ data.Set();
}
this.WriteJsonOutputLine(data.Output.ToJson());
diff --git a/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
index 5f7f90a9..9d214234 100644
--- a/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
+++ b/src/WingetCreateCLI/Models/DscModels/BaseResourceObject.cs
@@ -46,9 +46,6 @@ public virtual JObject GetProperties()
/// A Json object representing the current object.
public JObject ToJson()
{
- return JObject.FromObject(this, new JsonSerializer
- {
- NullValueHandling = NullValueHandling.Ignore,
- });
+ return JObject.FromObject(this);
}
}
diff --git a/src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs b/src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs
index eb11047b..8a75883d 100644
--- a/src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs
+++ b/src/WingetCreateCLI/Models/DscModels/SettingsFunctionData.cs
@@ -58,7 +58,16 @@ public void Get()
/// >true if the settings are equivalent; otherwise, false.
public bool Test()
{
- return JToken.DeepEquals(this.GetResolvedInput(), this.GetValidSettings(this.Output.Settings));
+ return JToken.DeepEquals(this.GetResolvedInput(), GetValidSettings(this.Output.Settings));
+ }
+
+ ///
+ /// Writes the current output settings to persistent storage.
+ ///
+ public void Set()
+ {
+ Debug.Assert(this.Output.Settings != null, "Output settings should not be null.");
+ UserSettings.SaveSettings(this.Output.Settings.ToObject());
}
///
@@ -88,47 +97,38 @@ public JObject GetResolvedInput()
if (SettingsResourceObject.ActionFull.Equals(this.Input.Action, StringComparison.OrdinalIgnoreCase))
{
this.Output.Action = SettingsResourceObject.ActionFull;
- this.resolvedInputUserSettings = this.GetValidSettings(this.Input.Settings);
+ this.resolvedInputUserSettings = GetValidSettings(this.Input.Settings);
}
else
{
this.Output.Action = SettingsResourceObject.ActionPartial;
var mergedSettings = this.GetUserSettings();
mergedSettings.Merge(this.Input.Settings);
- this.resolvedInputUserSettings = this.GetValidSettings(mergedSettings);
+ this.resolvedInputUserSettings = GetValidSettings(mergedSettings);
}
}
return this.resolvedInputUserSettings;
}
- ///
- /// Retrieves a deep-cloned JSON representation of the current user settings.
- ///
- /// A Json object representing the user settings.
- public JObject GetUserSettings()
- {
- this.userSettings ??= UserSettings.ToJson();
- return (JObject)this.userSettings.DeepClone();
- }
-
- ///
- /// Writes the current output settings to persistent storage.
- ///
- public void WriteOutput()
- {
- Debug.Assert(this.Output.Settings != null, "Output settings should not be null.");
- UserSettings.SaveSettings(this.Output.Settings.ToObject());
- }
-
///
/// Validates and converts the provided settings into a structured format.
///
/// An object containing settings to be validated.
/// An object representing the validated settings.
- public JObject GetValidSettings(JObject settings)
+ private static JObject GetValidSettings(JObject settings)
{
var settingsManifest = settings.ToObject();
return JObject.FromObject(settingsManifest);
}
+
+ ///
+ /// Retrieves a deep-cloned JSON representation of the current user settings.
+ ///
+ /// A Json object representing the user settings.
+ private JObject GetUserSettings()
+ {
+ this.userSettings ??= UserSettings.ToJson();
+ return (JObject)this.userSettings.DeepClone();
+ }
}
diff --git a/src/WingetCreateCLI/Properties/Resources.resx b/src/WingetCreateCLI/Properties/Resources.resx
index 5609dbbb..9f58f851 100644
--- a/src/WingetCreateCLI/Properties/Resources.resx
+++ b/src/WingetCreateCLI/Properties/Resources.resx
@@ -1422,7 +1422,7 @@ Warning: Using this argument may result in the token being logged. Consider an a
Get all state instances
- Execute the Schema command
+ Outputs schema of the resourceThe name of the DSC resource to manage
From 5d35f4133c5f4ff4351adc31361e61bd60f43803 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Wed, 25 Jun 2025 14:10:59 -0700
Subject: [PATCH 23/24] Addressed comments
---
src/WingetCreateCLI/Commands/DscCommand.cs | 7 ++--
.../Commands/DscCommands/BaseDscCommand.cs | 42 +++++++++++++++++--
.../DscCommands/DscSettingsCommand.cs | 9 ++--
.../WingetCreateTests/TestUtils.cs | 2 +-
.../UnitTests/DscCommandTests.cs | 20 +++------
.../UnitTests/DscSettingsCommandTests.cs | 40 ++++++------------
6 files changed, 64 insertions(+), 56 deletions(-)
diff --git a/src/WingetCreateCLI/Commands/DscCommand.cs b/src/WingetCreateCLI/Commands/DscCommand.cs
index 6ecb2f76..c703acd3 100644
--- a/src/WingetCreateCLI/Commands/DscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommand.cs
@@ -5,6 +5,7 @@ namespace Microsoft.WingetCreateCLI.Commands;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using CommandLine;
@@ -72,11 +73,9 @@ public override async Task Execute()
{
await Task.CompletedTask;
- List dscCommands = [new DscSettingsCommand()];
- var dscCommand = dscCommands.FirstOrDefault(c => c.CommandName.Equals(this.ResourceName, StringComparison.OrdinalIgnoreCase));
- if (dscCommand == null)
+ if (!BaseDscCommand.TryCreateInstance(this.ResourceName, out var dscCommand))
{
- var availableResources = string.Join(", ", dscCommands.Select(c => c.CommandName));
+ var availableResources = string.Join(", ", BaseDscCommand.GetAvailableCommands());
Logger.ErrorLocalized(nameof(Resources.DscResourceNameNotFound_Message), this.ResourceName, availableResources);
return false;
}
diff --git a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
index 89288706..5f25db86 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/BaseDscCommand.cs
@@ -4,6 +4,7 @@
namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
using System;
+using System.Collections.Generic;
using Microsoft.WingetCreateCLI.Models.DscModels;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -14,9 +15,41 @@ namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
public abstract class BaseDscCommand
{
///
- /// Gets the name of the command used to access the DSC functionality.
+ /// Tries to create an instance of a DSC command based on the command name.
///
- public virtual string CommandName { get; }
+ /// The name of the command to create an instance for.
+ /// The created command instance if successful; otherwise, null.
+ /// True if the command instance was created successfully; otherwise, false.
+ public static bool TryCreateInstance(string commandName, out BaseDscCommand commandInstance)
+ {
+ var formattedCommandName = commandName?.ToLowerInvariant() ?? string.Empty;
+ switch (formattedCommandName)
+ {
+ case DscSettingsCommand.CommandName:
+ commandInstance = new DscSettingsCommand();
+ return true;
+
+ // Add more cases here for other DSC commands as needed.
+
+ // Return false if no matching command is found.
+ default:
+ commandInstance = null;
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the list of available command names for DSC commands.
+ ///
+ /// The list of available command names.
+ public static List GetAvailableCommands()
+ {
+ return [
+ DscSettingsCommand.CommandName,
+
+ // Add more command names here as needed.
+ ];
+ }
///
/// DSC Get command.
@@ -55,15 +88,16 @@ public abstract class BaseDscCommand
///
/// Creates a Json schema for a DSC resource object.
///
+ /// The type of the resource object.
/// A Json object representing the schema.
- protected JObject CreateSchema()
+ protected JObject CreateSchema(string commandName)
where T : BaseResourceObject, new()
{
var resourceObject = new T();
return new JObject
{
["$schema"] = "http://json-schema.org/draft-07/schema#",
- ["title"] = this.CommandName,
+ ["title"] = commandName,
["type"] = "object",
["properties"] = resourceObject.GetProperties(),
["required"] = resourceObject.GetRequiredProperties(),
diff --git a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
index 8d3affe9..fd6538da 100644
--- a/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
+++ b/src/WingetCreateCLI/Commands/DscCommands/DscSettingsCommand.cs
@@ -3,7 +3,6 @@
namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
-using System;
using Microsoft.WingetCreateCLI.Logging;
using Microsoft.WingetCreateCLI.Models.DscModels;
using Microsoft.WingetCreateCLI.Properties;
@@ -14,8 +13,10 @@ namespace Microsoft.WingetCreateCLI.Commands.DscCommands;
///
public class DscSettingsCommand : BaseDscCommand
{
- ///
- public override string CommandName => "settings";
+ ///
+ /// Represents the name of the settings command used to access the DSC functionality.
+ ///
+ public const string CommandName = "settings";
///
public override bool Get(JToken input)
@@ -80,7 +81,7 @@ public override bool Export(JToken input)
///
public override bool Schema()
{
- this.WriteJsonOutputLine(this.CreateSchema());
+ this.WriteJsonOutputLine(this.CreateSchema(CommandName));
return true;
}
}
diff --git a/src/WingetCreateTests/WingetCreateTests/TestUtils.cs b/src/WingetCreateTests/WingetCreateTests/TestUtils.cs
index 6099f3ba..5a97585b 100644
--- a/src/WingetCreateTests/WingetCreateTests/TestUtils.cs
+++ b/src/WingetCreateTests/WingetCreateTests/TestUtils.cs
@@ -222,7 +222,7 @@ public static void DeleteCachedFiles(List testFileNames)
///
/// The arguments to pass to the DSC command.
/// Result of executing the DSC command.
- public static async Task ExecuteDscCommandAsync(List args)
+ public static async Task ExecuteDscCommandAsync(params string[] args)
{
var sw = new StringWriter();
Console.SetOut(sw);
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
index ffee0f15..46783c86 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscCommandTests.cs
@@ -33,11 +33,8 @@ public void OneTimeSetUp()
[Test]
public async Task DscSettingsResource_Success()
{
- // Arrange
- var command = new DscSettingsCommand();
-
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get"]);
+ var result = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--get");
// Assert
Assert.That(result.Success, Is.True);
@@ -52,11 +49,10 @@ public async Task DscResourceNotFound_ErrorMessage()
{
// Arrange
var dscResourceName = "ResourceNotFound";
- List dscCommands = [new DscSettingsCommand()];
- var availableResources = string.Join(", ", dscCommands.Select(c => c.CommandName));
+ var availableResources = string.Join(", ", BaseDscCommand.GetAvailableCommands());
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([dscResourceName]);
+ var result = await TestUtils.ExecuteDscCommandAsync(dscResourceName);
// Assert
Assert.That(result.Success, Is.False);
@@ -70,11 +66,8 @@ public async Task DscResourceNotFound_ErrorMessage()
[Test]
public async Task DscResourceOperationNotSpecified_ErrorMessage()
{
- // Arrange
- var command = new DscSettingsCommand();
-
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName]);
+ var result = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName);
// Assert
Assert.That(result.Success, Is.False);
@@ -88,11 +81,8 @@ public async Task DscResourceOperationNotSpecified_ErrorMessage()
[Test]
public async Task DscSettingsResourceFailedOperation_ErrorMessage()
{
- // Arrange
- var command = new DscSettingsCommand();
-
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--set", string.Empty);
// Assert
Assert.That(result.Success, Is.False);
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
index 0789dd6b..b9d170c8 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/DscSettingsCommandTests.cs
@@ -74,11 +74,8 @@ public void TearDown()
[Test]
public async Task DscSettingsResource_Get_Success()
{
- // Arrange
- var command = new DscSettingsCommand();
-
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--get"]);
+ var result = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--get");
var state = result.OutputState();
// Assert
@@ -95,11 +92,8 @@ public async Task DscSettingsResource_Get_Success()
[Test]
public async Task DscSettingsResource_Export_Success()
{
- // Arrange
- var command = new DscSettingsCommand();
-
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--export"]);
+ var result = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--export");
var state = result.OutputState();
// Assert
@@ -116,11 +110,8 @@ public async Task DscSettingsResource_Export_Success()
[Test]
public async Task DscSettingsResource_SetEmpty_Fail()
{
- // Arrange
- var command = new DscSettingsCommand();
-
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--set", string.Empty);
// Assert
Assert.That(result.Success, Is.False);
@@ -134,11 +125,8 @@ public async Task DscSettingsResource_SetEmpty_Fail()
[Test]
public async Task DscSettingsResource_TestEmpty_Fail()
{
- // Arrange
- var command = new DscSettingsCommand();
-
// Act
- var result = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", string.Empty]);
+ var result = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--test", string.Empty);
// Assert
Assert.That(result.Success, Is.False);
@@ -158,12 +146,11 @@ public async Task DscSettingsResource_SetWithDiff_Success(bool? isPartial)
{
// Arrange
this.ResetSettingsToDefaultValues();
- var command = new DscSettingsCommand();
// Part 1: Update settings repo name only
{
// Act
- var setRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var setRepoName = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial));
var stateAndDiff = setRepoName.OutputStateAndDiff();
// Assert
@@ -177,7 +164,7 @@ public async Task DscSettingsResource_SetWithDiff_Success(bool? isPartial)
// Part 2: Now update settings repo owner only
{
// Act
- var setRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var setRepoOwner = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial));
var stateAndDiff = setRepoOwner.OutputStateAndDiff();
// Assert
@@ -202,7 +189,6 @@ public async Task DscSettingsResource_SetWithoutDiff_Success(bool? isPartial)
{
// Arrange
this.ResetSettingsToDefaultValues();
- var command = new DscSettingsCommand();
// Part 1: Update settings repo name only
{
@@ -211,7 +197,7 @@ public async Task DscSettingsResource_SetWithoutDiff_Success(bool? isPartial)
var currentSettingsBeforeExecute = this.CurrentSettings;
// Act
- var setRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var setRepoName = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--set", this.CreateInput(name: MockName, isPartial: isPartial));
var stateAndDiff = setRepoName.OutputStateAndDiff();
// Assert
@@ -230,7 +216,7 @@ public async Task DscSettingsResource_SetWithoutDiff_Success(bool? isPartial)
var currentSettingsBeforeExecute = this.CurrentSettings;
// Act
- var setRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var setRepoOwner = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--set", this.CreateInput(owner: MockOwner, isPartial: isPartial));
var stateAndDiff = setRepoOwner.OutputStateAndDiff();
// Assert
@@ -255,12 +241,11 @@ public async Task DscSettingsResource_TestWithDiff_Success(bool? isPartial)
{
// Arrange
this.ResetSettingsToDefaultValues();
- var command = new DscSettingsCommand();
// Part 1: Test settings repo name only
{
// Act
- var testRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var testRepoName = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--test", this.CreateInput(name: MockName, isPartial: isPartial));
var stateAndDiff = testRepoName.OutputStateAndDiff();
// Assert
@@ -275,7 +260,7 @@ public async Task DscSettingsResource_TestWithDiff_Success(bool? isPartial)
// Part 2: Now test settings repo owner only
{
// Act
- var testRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var testRepoOwner = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--test", this.CreateInput(owner: MockOwner, isPartial: isPartial));
var stateAndDiff = testRepoOwner.OutputStateAndDiff();
// Assert
@@ -301,7 +286,6 @@ public async Task DscSettingsResource_TestWithoutDiff_Success(bool? isPartial)
{
// Arrange
this.ResetSettingsToDefaultValues();
- var command = new DscSettingsCommand();
// Part 1: Test settings repo name only
{
@@ -309,7 +293,7 @@ public async Task DscSettingsResource_TestWithoutDiff_Success(bool? isPartial)
this.UpdateSettings(name: MockName);
// Act
- var testRepoName = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(name: MockName, isPartial: isPartial)]);
+ var testRepoName = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--test", this.CreateInput(name: MockName, isPartial: isPartial));
var stateAndDiff = testRepoName.OutputStateAndDiff();
// Assert
@@ -327,7 +311,7 @@ public async Task DscSettingsResource_TestWithoutDiff_Success(bool? isPartial)
this.UpdateSettings(name: (isPartial ?? true) ? null : defaultRepoName, owner: MockOwner);
// Act
- var testRepoOwner = await TestUtils.ExecuteDscCommandAsync([command.CommandName, "--test", this.CreateInput(owner: MockOwner, isPartial: isPartial)]);
+ var testRepoOwner = await TestUtils.ExecuteDscCommandAsync(DscSettingsCommand.CommandName, "--test", this.CreateInput(owner: MockOwner, isPartial: isPartial));
var stateAndDiff = testRepoOwner.OutputStateAndDiff();
// Assert
From f38aa7d8b39ebb2c69ffe7e4abc1eb2c59187b47 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Wed, 25 Jun 2025 15:16:55 -0700
Subject: [PATCH 24/24] Addressed comments
---
src/WingetCreatePackage/Package.appxmanifest | 2 +-
src/WingetCreatePackage/WingetCreatePackage.wapproj | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/WingetCreatePackage/Package.appxmanifest b/src/WingetCreatePackage/Package.appxmanifest
index 218a3cd2..540a344b 100644
--- a/src/WingetCreatePackage/Package.appxmanifest
+++ b/src/WingetCreatePackage/Package.appxmanifest
@@ -11,7 +11,7 @@
+ Version="0.0.1.0" />
Windows Package Manager Manifest Creator
diff --git a/src/WingetCreatePackage/WingetCreatePackage.wapproj b/src/WingetCreatePackage/WingetCreatePackage.wapproj
index e05e55ac..57a3028b 100644
--- a/src/WingetCreatePackage/WingetCreatePackage.wapproj
+++ b/src/WingetCreatePackage/WingetCreatePackage.wapproj
@@ -37,8 +37,6 @@
FalseTrue0
- SHA256
- C:\win\tmp\wingetcreate-package\