mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 00:24:42 +01:00
Initial DSC v3 support for PowerToys (#41132)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Tasks checklist - [X] Implement DSC infra in PowerToys - [X] Implement Settings DSC resource - [X] Implement Get, Set, Test, Export, Schema - [X] Generate manifest (DSC resource JSON) - [X] Added Unit Tests - [x] Add `NJsonSchema` v11.4.0 to the stream - [x] Package the manifest files so dsc.exe can discover them - [x] Add `PowerToys.DSC.exe` to the PATH (maybe?) - [x] Add `InstallLocation` in the registry key so `winget configue export` can export the PowerToys DSC resources <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [X] Closes: #37276 - [X] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [X] **Tests:** Added/updated and all pass - [X] **Localization:** All end-user-facing strings can be localized - [x] **Dev docs:** Added/updated - [x] **New binaries:** Added on the required places - [x] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [x] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [x] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [x] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed --------- Co-authored-by: vanzue <vanzue@outlook.com> Co-authored-by: Kai Tao (from Dev Box) <kaitao@microsoft.com> Co-authored-by: Leilei Zhang <leilzh@microsoft.com>
This commit is contained in:
68
src/dsc/v3/PowerToys.DSC.UnitTests/BaseDscTest.cs
Normal file
68
src/dsc/v3/PowerToys.DSC.UnitTests/BaseDscTest.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Resources;
|
||||
using PowerToys.DSC.UnitTests.Models;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests;
|
||||
|
||||
public class BaseDscTest
|
||||
{
|
||||
private readonly ResourceManager _resourceManager;
|
||||
|
||||
public BaseDscTest()
|
||||
{
|
||||
_resourceManager = new ResourceManager("PowerToys.DSC.Properties.Resources", typeof(PowerToys.DSC.Program).Assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string resource for the given name, formatted with the provided arguments.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the resource string.</param>
|
||||
/// <param name="args">The arguments to format the resource string with.</param>
|
||||
/// <returns></returns>
|
||||
public string GetResourceString(string name, params string[] args)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, _resourceManager.GetString(name, CultureInfo.InvariantCulture), args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute a dsc command with the provided arguments.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
protected DscExecuteResult ExecuteDscCommand<T>(params string[] args)
|
||||
where T : Command, new()
|
||||
{
|
||||
var originalOut = Console.Out;
|
||||
var originalErr = Console.Error;
|
||||
|
||||
var outSw = new StringWriter();
|
||||
var errSw = new StringWriter();
|
||||
|
||||
try
|
||||
{
|
||||
Console.SetOut(outSw);
|
||||
Console.SetError(errSw);
|
||||
|
||||
var executeResult = new T().Invoke(args);
|
||||
var output = outSw.ToString();
|
||||
var errorOutput = errSw.ToString();
|
||||
return new(executeResult == 0, output, errorOutput);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.SetOut(originalOut);
|
||||
Console.SetError(originalErr);
|
||||
outSw.Dispose();
|
||||
errSw.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/dsc/v3/PowerToys.DSC.UnitTests/CommandTest.cs
Normal file
37
src/dsc/v3/PowerToys.DSC.UnitTests/CommandTest.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using PowerToys.DSC.Commands;
|
||||
using PowerToys.DSC.DSCResources;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class CommandTest : BaseDscTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetResource_Found_Success()
|
||||
{
|
||||
// Act
|
||||
var result = ExecuteDscCommand<GetCommand>("--resource", SettingsResource.ResourceName);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetResource_NotFound_Fail()
|
||||
{
|
||||
// Arrange
|
||||
var availableResources = string.Join(", ", BaseCommand.AvailableResources);
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<GetCommand>("--resource", "ResourceNotFound");
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.Contains(GetResourceString("InvalidResourceNameError", availableResources), result.Error);
|
||||
}
|
||||
}
|
||||
103
src/dsc/v3/PowerToys.DSC.UnitTests/Models/DscExecuteResult.cs
Normal file
103
src/dsc/v3/PowerToys.DSC.UnitTests/Models/DscExecuteResult.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using PowerToys.DSC.Models;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Result of executing a DSC command.
|
||||
/// </summary>
|
||||
public class DscExecuteResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DscExecuteResult"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">Value indicating whether the command execution was successful.</param>
|
||||
/// <param name="output">Output stream content.</param>
|
||||
/// <param name="error">Error stream content.</param>
|
||||
public DscExecuteResult(bool success, string output, string error)
|
||||
{
|
||||
Success = success;
|
||||
Output = output;
|
||||
Error = error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command execution was successful.
|
||||
/// </summary>
|
||||
public bool Success { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output stream content of the operation.
|
||||
/// </summary>
|
||||
public string Output { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error stream content of the operation.
|
||||
/// </summary>
|
||||
public string Error { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the messages from the error stream.
|
||||
/// </summary>
|
||||
/// <returns>List of messages with their levels.</returns>
|
||||
public List<(DscMessageLevel Level, string Message)> Messages()
|
||||
{
|
||||
var lines = Error.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||
return lines.SelectMany(line =>
|
||||
{
|
||||
var map = JsonSerializer.Deserialize<Dictionary<string, string>>(line);
|
||||
return map.Select(v => (GetMessageLevel(v.Key), v.Value)).ToList();
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output as state.
|
||||
/// </summary>
|
||||
/// <returns>State.</returns>
|
||||
public T OutputState<T>()
|
||||
{
|
||||
var lines = Output.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||
Debug.Assert(lines.Length == 1, "Output should contain exactly one line.");
|
||||
return JsonSerializer.Deserialize<T>(lines[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output as state and diff.
|
||||
/// </summary>
|
||||
/// <returns>State and diff.</returns>
|
||||
public (T State, List<string> Diff) OutputStateAndDiff<T>()
|
||||
{
|
||||
var lines = Output.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||
Debug.Assert(lines.Length == 2, "Output should contain exactly two lines.");
|
||||
var obj = JsonSerializer.Deserialize<T>(lines[0]);
|
||||
var diff = JsonSerializer.Deserialize<List<string>>(lines[1]);
|
||||
return (obj, diff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message level from a string representation.
|
||||
/// </summary>
|
||||
/// <param name="level">The string representation of the message level.</param>
|
||||
/// <returns>The level as <see cref="DscMessageLevel"/>.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the level is unknown.</exception>
|
||||
private DscMessageLevel GetMessageLevel(string level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
"error" => DscMessageLevel.Error,
|
||||
"warn" => DscMessageLevel.Warning,
|
||||
"info" => DscMessageLevel.Info,
|
||||
"debug" => DscMessageLevel.Debug,
|
||||
"trace" => DscMessageLevel.Trace,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, "Unknown message level"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OutputPath>..\..\..\..\$(Configuration)\$(Platform)\tests\PowerToys.DSC.Tests\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PowerToys.DSC\PowerToys.DSC.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class SettingsResourceAdvancedPasteModuleTest : SettingsResourceModuleTest<AdvancedPasteSettings>
|
||||
{
|
||||
public SettingsResourceAdvancedPasteModuleTest()
|
||||
: base(nameof(ModuleType.AdvancedPaste))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Action<AdvancedPasteSettings> GetSettingsModifier()
|
||||
{
|
||||
return s =>
|
||||
{
|
||||
s.Properties.ShowCustomPreview = !s.Properties.ShowCustomPreview;
|
||||
s.Properties.CloseAfterLosingFocus = !s.Properties.CloseAfterLosingFocus;
|
||||
s.Properties.IsAdvancedAIEnabled = !s.Properties.IsAdvancedAIEnabled;
|
||||
s.Properties.AdvancedPasteUIShortcut = new HotkeySettings
|
||||
{
|
||||
Key = "mock",
|
||||
Alt = true,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class SettingsResourceAlwaysOnTopModuleTest : SettingsResourceModuleTest<AlwaysOnTopSettings>
|
||||
{
|
||||
public SettingsResourceAlwaysOnTopModuleTest()
|
||||
: base(nameof(ModuleType.AlwaysOnTop))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Action<AlwaysOnTopSettings> GetSettingsModifier()
|
||||
{
|
||||
return s =>
|
||||
{
|
||||
s.Properties.RoundCornersEnabled.Value = !s.Properties.RoundCornersEnabled.Value;
|
||||
s.Properties.FrameEnabled.Value = !s.Properties.FrameEnabled.Value;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using PowerToys.DSC.DSCResources;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class SettingsResourceAppModuleTest : SettingsResourceModuleTest<GeneralSettings>
|
||||
{
|
||||
public SettingsResourceAppModuleTest()
|
||||
: base(SettingsResource.AppModule)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Action<GeneralSettings> GetSettingsModifier()
|
||||
{
|
||||
return s =>
|
||||
{
|
||||
s.Startup = !s.Startup;
|
||||
s.ShowSysTrayIcon = !s.ShowSysTrayIcon;
|
||||
s.Enabled.Awake = !s.Enabled.Awake;
|
||||
s.Enabled.ColorPicker = !s.Enabled.ColorPicker;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class SettingsResourceAwakeModuleTest : SettingsResourceModuleTest<AwakeSettings>
|
||||
{
|
||||
public SettingsResourceAwakeModuleTest()
|
||||
: base(nameof(ModuleType.Awake))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Action<AwakeSettings> GetSettingsModifier()
|
||||
{
|
||||
return s =>
|
||||
{
|
||||
s.Properties.ExpirationDateTime = DateTimeOffset.MinValue;
|
||||
s.Properties.IntervalHours = DefaultSettings.Properties.IntervalHours + 1;
|
||||
s.Properties.IntervalMinutes = DefaultSettings.Properties.IntervalMinutes + 1;
|
||||
s.Properties.Mode = s.Properties.Mode == AwakeMode.PASSIVE ? AwakeMode.TIMED : AwakeMode.PASSIVE;
|
||||
s.Properties.KeepDisplayOn = !s.Properties.KeepDisplayOn;
|
||||
s.Properties.CustomTrayTimes = new Dictionary<string, uint>
|
||||
{
|
||||
{ "08:00", 1 },
|
||||
{ "12:00", 2 },
|
||||
{ "16:00", 3 },
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class SettingsResourceColorPickerModuleTest : SettingsResourceModuleTest<ColorPickerSettings>
|
||||
{
|
||||
public SettingsResourceColorPickerModuleTest()
|
||||
: base(nameof(ModuleType.ColorPicker))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Action<ColorPickerSettings> GetSettingsModifier()
|
||||
{
|
||||
return s =>
|
||||
{
|
||||
s.Properties.ShowColorName = !s.Properties.ShowColorName;
|
||||
s.Properties.ColorHistoryLimit = s.Properties.ColorHistoryLimit == 0 ? 10 : 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ManagedCommon;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using PowerToys.DSC.Commands;
|
||||
using PowerToys.DSC.DSCResources;
|
||||
using PowerToys.DSC.Models;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class SettingsResourceCommandTest : BaseDscTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void Modules_ListAllSupportedModules()
|
||||
{
|
||||
// Arrange
|
||||
var expectedModules = new List<string>()
|
||||
{
|
||||
SettingsResource.AppModule,
|
||||
nameof(ModuleType.AdvancedPaste),
|
||||
nameof(ModuleType.AlwaysOnTop),
|
||||
nameof(ModuleType.Awake),
|
||||
nameof(ModuleType.ColorPicker),
|
||||
nameof(ModuleType.CropAndLock),
|
||||
nameof(ModuleType.EnvironmentVariables),
|
||||
nameof(ModuleType.FancyZones),
|
||||
nameof(ModuleType.FileLocksmith),
|
||||
nameof(ModuleType.FindMyMouse),
|
||||
nameof(ModuleType.Hosts),
|
||||
nameof(ModuleType.ImageResizer),
|
||||
nameof(ModuleType.KeyboardManager),
|
||||
nameof(ModuleType.MouseHighlighter),
|
||||
nameof(ModuleType.MouseJump),
|
||||
nameof(ModuleType.MousePointerCrosshairs),
|
||||
nameof(ModuleType.Peek),
|
||||
nameof(ModuleType.PowerRename),
|
||||
nameof(ModuleType.PowerAccent),
|
||||
nameof(ModuleType.RegistryPreview),
|
||||
nameof(ModuleType.MeasureTool),
|
||||
nameof(ModuleType.ShortcutGuide),
|
||||
nameof(ModuleType.PowerOCR),
|
||||
nameof(ModuleType.Workspaces),
|
||||
nameof(ModuleType.ZoomIt),
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<ModulesCommand>("--resource", SettingsResource.ResourceName);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(string.Join(Environment.NewLine, expectedModules.Order()), result.Output.Trim());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Set_EmptyInput_Fail()
|
||||
{
|
||||
// Act
|
||||
var result = ExecuteDscCommand<SetCommand>("--resource", SettingsResource.ResourceName, "--module", "Awake");
|
||||
var messages = result.Messages();
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(1, messages.Count);
|
||||
Assert.AreEqual(DscMessageLevel.Error, messages[0].Level);
|
||||
Assert.AreEqual(GetResourceString("InputEmptyOrNullError"), messages[0].Message);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_EmptyInput_Fail()
|
||||
{
|
||||
// Act
|
||||
var result = ExecuteDscCommand<TestCommand>("--resource", SettingsResource.ResourceName, "--module", "Awake");
|
||||
var messages = result.Messages();
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(1, messages.Count);
|
||||
Assert.AreEqual(DscMessageLevel.Error, messages[0].Level);
|
||||
Assert.AreEqual(GetResourceString("InputEmptyOrNullError"), messages[0].Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class SettingsResourceCropAndLockModuleTest : SettingsResourceModuleTest<CropAndLockSettings>
|
||||
{
|
||||
public SettingsResourceCropAndLockModuleTest()
|
||||
: base(nameof(ModuleType.CropAndLock))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Action<CropAndLockSettings> GetSettingsModifier()
|
||||
{
|
||||
return s =>
|
||||
{
|
||||
s.Properties.ThumbnailHotkey = new KeyboardKeysProperty()
|
||||
{
|
||||
Value = new HotkeySettings
|
||||
{
|
||||
Key = "mock",
|
||||
Alt = true,
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using PowerToys.DSC.Commands;
|
||||
using PowerToys.DSC.DSCResources;
|
||||
using PowerToys.DSC.Models.ResourceObjects;
|
||||
|
||||
namespace PowerToys.DSC.UnitTests.SettingsResourceTests;
|
||||
|
||||
public abstract class SettingsResourceModuleTest<TSettingsConfig> : BaseDscTest
|
||||
where TSettingsConfig : ISettingsConfig, new()
|
||||
{
|
||||
private readonly SettingsUtils _settingsUtils = new();
|
||||
private TSettingsConfig _originalSettings;
|
||||
|
||||
protected TSettingsConfig DefaultSettings => new();
|
||||
|
||||
protected string Module { get; }
|
||||
|
||||
protected List<string> DiffSettings { get; } = [SettingsResourceObject<AwakeSettings>.SettingsJsonPropertyName];
|
||||
|
||||
protected List<string> DiffEmpty { get; } = [];
|
||||
|
||||
public SettingsResourceModuleTest(string module)
|
||||
{
|
||||
Module = module;
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
_originalSettings = GetSettings();
|
||||
ResetSettingsToDefaultValues();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void TestCleanup()
|
||||
{
|
||||
SaveSettings(_originalSettings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Get_Success()
|
||||
{
|
||||
// Arrange
|
||||
var settingsBeforeExecute = GetSettings();
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<GetCommand>("--resource", SettingsResource.ResourceName, "--module", Module);
|
||||
var state = result.OutputState<SettingsResourceObject<TSettingsConfig>>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
AssertSettingsAreEqual(settingsBeforeExecute, GetSettings());
|
||||
AssertStateAndSettingsAreEqual(settingsBeforeExecute, state);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Export_Success()
|
||||
{
|
||||
// Arrange
|
||||
var settingsBeforeExecute = GetSettings();
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<ExportCommand>("--resource", SettingsResource.ResourceName, "--module", Module);
|
||||
var state = result.OutputState<SettingsResourceObject<TSettingsConfig>>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
AssertSettingsAreEqual(settingsBeforeExecute, GetSettings());
|
||||
AssertStateAndSettingsAreEqual(settingsBeforeExecute, state);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SetWithDiff_Success()
|
||||
{
|
||||
// Arrange
|
||||
var settingsModifier = GetSettingsModifier();
|
||||
var input = CreateInputResourceObject(settingsModifier);
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<SetCommand>("--resource", SettingsResource.ResourceName, "--module", Module, "--input", input);
|
||||
var (state, diff) = result.OutputStateAndDiff<SettingsResourceObject<TSettingsConfig>>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
AssertSettingsHasChanged(settingsModifier);
|
||||
AssertStateAndSettingsAreEqual(GetSettings(), state);
|
||||
CollectionAssert.AreEqual(DiffSettings, diff);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SetWithoutDiff_Success()
|
||||
{
|
||||
// Arrange
|
||||
var settingsModifier = GetSettingsModifier();
|
||||
UpdateSettings(settingsModifier);
|
||||
var settingsBeforeExecute = GetSettings();
|
||||
var input = CreateInputResourceObject(settingsModifier);
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<SetCommand>("--resource", SettingsResource.ResourceName, "--module", Module, "--input", input);
|
||||
var (state, diff) = result.OutputStateAndDiff<SettingsResourceObject<TSettingsConfig>>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
AssertSettingsAreEqual(settingsBeforeExecute, GetSettings());
|
||||
AssertStateAndSettingsAreEqual(settingsBeforeExecute, state);
|
||||
CollectionAssert.AreEqual(DiffEmpty, diff);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestWithDiff_Success()
|
||||
{
|
||||
// Arrange
|
||||
var settingsModifier = GetSettingsModifier();
|
||||
var settingsBeforeExecute = GetSettings();
|
||||
var input = CreateInputResourceObject(settingsModifier);
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<TestCommand>("--resource", SettingsResource.ResourceName, "--module", Module, "--input", input);
|
||||
var (state, diff) = result.OutputStateAndDiff<SettingsResourceObject<TSettingsConfig>>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
AssertSettingsAreEqual(settingsBeforeExecute, GetSettings());
|
||||
AssertStateAndSettingsAreEqual(settingsBeforeExecute, state);
|
||||
CollectionAssert.AreEqual(DiffSettings, diff);
|
||||
Assert.IsFalse(state.InDesiredState);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestWithoutDiff_Success()
|
||||
{
|
||||
// Arrange
|
||||
var settingsModifier = GetSettingsModifier();
|
||||
UpdateSettings(settingsModifier);
|
||||
var settingsBeforeExecute = GetSettings();
|
||||
var input = CreateInputResourceObject(settingsModifier);
|
||||
|
||||
// Act
|
||||
var result = ExecuteDscCommand<TestCommand>("--resource", SettingsResource.ResourceName, "--module", Module, "--input", input);
|
||||
var (state, diff) = result.OutputStateAndDiff<SettingsResourceObject<TSettingsConfig>>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.Success);
|
||||
AssertSettingsAreEqual(settingsBeforeExecute, GetSettings());
|
||||
AssertStateAndSettingsAreEqual(settingsBeforeExecute, state);
|
||||
CollectionAssert.AreEqual(DiffEmpty, diff);
|
||||
Assert.IsTrue(state.InDesiredState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings modifier action for the specific settings configuration.
|
||||
/// </summary>
|
||||
/// <returns>An action that modifies the settings configuration.</returns>
|
||||
protected abstract Action<TSettingsConfig> GetSettingsModifier();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the settings to default values.
|
||||
/// </summary>
|
||||
private void ResetSettingsToDefaultValues()
|
||||
{
|
||||
SaveSettings(DefaultSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the settings for the specified module.
|
||||
/// </summary>
|
||||
/// <returns>An instance of the settings type with the current configuration.</returns>
|
||||
private TSettingsConfig GetSettings()
|
||||
{
|
||||
return _settingsUtils.GetSettingsOrDefault<TSettingsConfig>(DefaultSettings.GetModuleName());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the settings for the specified module.
|
||||
/// </summary>
|
||||
/// <param name="settings">Settings to save.</param>
|
||||
private void SaveSettings(TSettingsConfig settings)
|
||||
{
|
||||
_settingsUtils.SaveSettings(JsonSerializer.Serialize(settings), DefaultSettings.GetModuleName());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the resource object for the operation.
|
||||
/// </summary>
|
||||
/// <param name="settings">Settings to include in the resource object.</param>
|
||||
/// <returns>A JSON string representing the resource object.</returns>
|
||||
private string CreateResourceObject(TSettingsConfig settings)
|
||||
{
|
||||
var resourceObject = new SettingsResourceObject<TSettingsConfig>
|
||||
{
|
||||
Settings = settings,
|
||||
};
|
||||
return JsonSerializer.Serialize(resourceObject);
|
||||
}
|
||||
|
||||
private string CreateInputResourceObject(Action<TSettingsConfig> settingsModifier)
|
||||
{
|
||||
var settings = DefaultSettings;
|
||||
settingsModifier(settings);
|
||||
return CreateResourceObject(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the response for the Get operation.
|
||||
/// </summary>
|
||||
/// <returns>A JSON string representing the response.</returns>
|
||||
private string CreateGetResponse()
|
||||
{
|
||||
return CreateResourceObject(GetSettings());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the state and settings are equal.
|
||||
/// </summary>
|
||||
/// <param name="settings">Settings manifest to compare against.</param>
|
||||
/// <param name="state">Output state to compare.</param>
|
||||
private void AssertStateAndSettingsAreEqual(TSettingsConfig settings, SettingsResourceObject<TSettingsConfig> state)
|
||||
{
|
||||
AssertSettingsAreEqual(settings, state.Settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that two settings manifests are equal.
|
||||
/// </summary>
|
||||
/// <param name="expected">Expected settings.</param>
|
||||
/// <param name="actual">Actual settings.</param>
|
||||
private void AssertSettingsAreEqual(TSettingsConfig expected, TSettingsConfig actual)
|
||||
{
|
||||
var expectedJson = JsonSerializer.SerializeToNode(expected) as JsonObject;
|
||||
var actualJson = JsonSerializer.SerializeToNode(actual) as JsonObject;
|
||||
Assert.IsTrue(JsonNode.DeepEquals(expectedJson, actualJson));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the current settings have changed.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to prepare the default settings.</param>
|
||||
private void AssertSettingsHasChanged(Action<TSettingsConfig> action)
|
||||
{
|
||||
var currentSettings = GetSettings();
|
||||
var defaultSettings = DefaultSettings;
|
||||
action(defaultSettings);
|
||||
AssertSettingsAreEqual(defaultSettings, currentSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the settings.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to modify the settings.</param>
|
||||
private void UpdateSettings(Action<TSettingsConfig> action)
|
||||
{
|
||||
var settings = GetSettings();
|
||||
action(settings);
|
||||
SaveSettings(settings);
|
||||
}
|
||||
}
|
||||
120
src/dsc/v3/PowerToys.DSC/Commands/BaseCommand.cs
Normal file
120
src/dsc/v3/PowerToys.DSC/Commands/BaseCommand.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using PowerToys.DSC.DSCResources;
|
||||
using PowerToys.DSC.Options;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all DSC commands.
|
||||
/// </summary>
|
||||
public abstract class BaseCommand : Command
|
||||
{
|
||||
private static readonly CompositeFormat ModuleNotSupportedByResource = CompositeFormat.Parse(Resources.ModuleNotSupportedByResource);
|
||||
|
||||
// Shared options for all commands
|
||||
private readonly ModuleOption _moduleOption;
|
||||
private readonly ResourceOption _resourceOption;
|
||||
private readonly InputOption _inputOption;
|
||||
|
||||
// The dictionary of available resources and their factories.
|
||||
private static readonly Dictionary<string, Func<string?, BaseResource>> _resourceFactories = new()
|
||||
{
|
||||
{ SettingsResource.ResourceName, module => new SettingsResource(module) },
|
||||
|
||||
// Add other resources here
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of available DSC resources that can be used with the command.
|
||||
/// </summary>
|
||||
public static List<string> AvailableResources => [.._resourceFactories.Keys];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DSC resource to be used by the command.
|
||||
/// </summary>
|
||||
protected BaseResource? Resource { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input JSON provided by the user.
|
||||
/// </summary>
|
||||
protected string? Input { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PowerToys module to be used by the command.
|
||||
/// </summary>
|
||||
protected string? Module { get; private set; }
|
||||
|
||||
public BaseCommand(string name, string description)
|
||||
: base(name, description)
|
||||
{
|
||||
// Register the common options for all commands
|
||||
_moduleOption = new ModuleOption();
|
||||
AddOption(_moduleOption);
|
||||
|
||||
_resourceOption = new ResourceOption(AvailableResources);
|
||||
AddOption(_resourceOption);
|
||||
|
||||
_inputOption = new InputOption();
|
||||
AddOption(_inputOption);
|
||||
|
||||
// Register the command handler
|
||||
this.SetHandler(CommandHandler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the command invocation.
|
||||
/// </summary>
|
||||
/// <param name="context">The invocation context containing the parsed command options.</param>
|
||||
public void CommandHandler(InvocationContext context)
|
||||
{
|
||||
Input = context.ParseResult.GetValueForOption(_inputOption);
|
||||
Module = context.ParseResult.GetValueForOption(_moduleOption);
|
||||
Resource = ResolvedResource(context);
|
||||
|
||||
// Validate the module against the resource's supported modules
|
||||
var supportedModules = Resource.GetSupportedModules();
|
||||
if (!string.IsNullOrEmpty(Module) && !supportedModules.Contains(Module))
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.InvariantCulture, ModuleNotSupportedByResource, Module, Resource.Name);
|
||||
context.Console.Error.WriteLine(errorMessage);
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue with the command handler logic
|
||||
CommandHandlerInternal(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the command logic internally.
|
||||
/// </summary>
|
||||
/// <param name="context">Invocation context containing the parsed command options.</param>
|
||||
public abstract void CommandHandlerInternal(InvocationContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the resource from the provided resource name in the context.
|
||||
/// </summary>
|
||||
/// <param name="context">Invocation context containing the parsed command options.</param>
|
||||
/// <returns>The resolved <see cref="BaseResource"/> instance.</returns>
|
||||
private BaseResource ResolvedResource(InvocationContext context)
|
||||
{
|
||||
// Resource option has already been validated before the command
|
||||
// handler is invoked.
|
||||
var resourceName = context.ParseResult.GetValueForOption(_resourceOption);
|
||||
Debug.Assert(!string.IsNullOrEmpty(resourceName), "Resource name must not be null or empty.");
|
||||
Debug.Assert(_resourceFactories.ContainsKey(resourceName), $"Resource '{resourceName}' is not registered.");
|
||||
return _resourceFactories[resourceName](Module);
|
||||
}
|
||||
}
|
||||
25
src/dsc/v3/PowerToys.DSC/Commands/ExportCommand.cs
Normal file
25
src/dsc/v3/PowerToys.DSC/Commands/ExportCommand.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine.Invocation;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to export all state instances.
|
||||
/// </summary>
|
||||
public sealed class ExportCommand : BaseCommand
|
||||
{
|
||||
public ExportCommand()
|
||||
: base("export", Resources.ExportCommandDescription)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CommandHandlerInternal(InvocationContext context)
|
||||
{
|
||||
context.ExitCode = Resource!.ExportState(Input) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
25
src/dsc/v3/PowerToys.DSC/Commands/GetCommand.cs
Normal file
25
src/dsc/v3/PowerToys.DSC/Commands/GetCommand.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine.Invocation;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to get the resource state.
|
||||
/// </summary>
|
||||
public sealed class GetCommand : BaseCommand
|
||||
{
|
||||
public GetCommand()
|
||||
: base("get", Resources.GetCommandDescription)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CommandHandlerInternal(InvocationContext context)
|
||||
{
|
||||
context.ExitCode = Resource!.GetState(Input) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
34
src/dsc/v3/PowerToys.DSC/Commands/ManifestCommand.cs
Normal file
34
src/dsc/v3/PowerToys.DSC/Commands/ManifestCommand.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine.Invocation;
|
||||
using PowerToys.DSC.Options;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to get the manifest of the DSC resource.
|
||||
/// </summary>
|
||||
public sealed class ManifestCommand : BaseCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Option to specify the output directory for the manifest.
|
||||
/// </summary>
|
||||
private readonly OutputDirectoryOption _outputDirectoryOption;
|
||||
|
||||
public ManifestCommand()
|
||||
: base("manifest", Resources.ManifestCommandDescription)
|
||||
{
|
||||
_outputDirectoryOption = new OutputDirectoryOption();
|
||||
AddOption(_outputDirectoryOption);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CommandHandlerInternal(InvocationContext context)
|
||||
{
|
||||
var outputDir = context.ParseResult.GetValueForOption(_outputDirectoryOption);
|
||||
context.ExitCode = Resource!.Manifest(outputDir) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
47
src/dsc/v3/PowerToys.DSC/Commands/ModulesCommand.cs
Normal file
47
src/dsc/v3/PowerToys.DSC/Commands/ModulesCommand.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Diagnostics;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to get all supported modules for a specific resource.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is primarily used for debugging purposes and for build scripts.
|
||||
/// </remarks>
|
||||
public sealed class ModulesCommand : BaseCommand
|
||||
{
|
||||
public ModulesCommand()
|
||||
: base("modules", Resources.ModulesCommandDescription)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CommandHandlerInternal(InvocationContext context)
|
||||
{
|
||||
// Module is optional, if not provided, all supported modules for the
|
||||
// resource will be printed. If provided, it must be one of the
|
||||
// supported modules since it has been validated before this command is
|
||||
// executed.
|
||||
if (!string.IsNullOrEmpty(Module))
|
||||
{
|
||||
Debug.Assert(Resource!.GetSupportedModules().Contains(Module), "Module must be present in the list of supported modules.");
|
||||
context.Console.WriteLine(Module);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Print the supported modules for the specified resource
|
||||
foreach (var module in Resource!.GetSupportedModules())
|
||||
{
|
||||
context.Console.WriteLine(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/dsc/v3/PowerToys.DSC/Commands/SchemaCommand.cs
Normal file
25
src/dsc/v3/PowerToys.DSC/Commands/SchemaCommand.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine.Invocation;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to output the schema of the resource.
|
||||
/// </summary>
|
||||
public sealed class SchemaCommand : BaseCommand
|
||||
{
|
||||
public SchemaCommand()
|
||||
: base("schema", Resources.SchemaCommandDescription)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CommandHandlerInternal(InvocationContext context)
|
||||
{
|
||||
context.ExitCode = Resource!.Schema() ? 0 : 1;
|
||||
}
|
||||
}
|
||||
25
src/dsc/v3/PowerToys.DSC/Commands/SetCommand.cs
Normal file
25
src/dsc/v3/PowerToys.DSC/Commands/SetCommand.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine.Invocation;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to set the resource state.
|
||||
/// </summary>
|
||||
public sealed class SetCommand : BaseCommand
|
||||
{
|
||||
public SetCommand()
|
||||
: base("set", Resources.SetCommandDescription)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CommandHandlerInternal(InvocationContext context)
|
||||
{
|
||||
context.ExitCode = Resource!.SetState(Input) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
25
src/dsc/v3/PowerToys.DSC/Commands/TestCommand.cs
Normal file
25
src/dsc/v3/PowerToys.DSC/Commands/TestCommand.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine.Invocation;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to test the resource state.
|
||||
/// </summary>
|
||||
public sealed class TestCommand : BaseCommand
|
||||
{
|
||||
public TestCommand()
|
||||
: base("test", Resources.TestCommandDescription)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CommandHandlerInternal(InvocationContext context)
|
||||
{
|
||||
context.ExitCode = Resource!.TestState(Input) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
134
src/dsc/v3/PowerToys.DSC/DSCResources/BaseResource.cs
Normal file
134
src/dsc/v3/PowerToys.DSC/DSCResources/BaseResource.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Nodes;
|
||||
using PowerToys.DSC.Models;
|
||||
|
||||
namespace PowerToys.DSC.DSCResources;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all DSC resources.
|
||||
/// </summary>
|
||||
public abstract class BaseResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the resource.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module being used by the resource, if provided.
|
||||
/// </summary>
|
||||
public string? Module { get; }
|
||||
|
||||
public BaseResource(string name, string? module)
|
||||
{
|
||||
Name = name;
|
||||
Module = module;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the get method on the resource.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string, if any.</param>
|
||||
/// <returns>True if the operation was successful; otherwise false.</returns>
|
||||
public abstract bool GetState(string? input);
|
||||
|
||||
/// <summary>
|
||||
/// Calls the set method on the resource.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string, if any.</param>
|
||||
/// <returns>True if the operation was successful; otherwise false.</returns>
|
||||
public abstract bool SetState(string? input);
|
||||
|
||||
/// <summary>
|
||||
/// Calls the test method on the resource.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string, if any.</param>
|
||||
/// <returns>True if the operation was successful; otherwise false.</returns>
|
||||
public abstract bool TestState(string? input);
|
||||
|
||||
/// <summary>
|
||||
/// Calls the export method on the resource.
|
||||
/// </summary>
|
||||
/// <param name="input"> The input string, if any.</param>
|
||||
/// <returns>True if the operation was successful; otherwise false.</returns>
|
||||
public abstract bool ExportState(string? input);
|
||||
|
||||
/// <summary>
|
||||
/// Calls the schema method on the resource.
|
||||
/// </summary>
|
||||
/// <returns>True if the operation was successful; otherwise false.</returns>
|
||||
public abstract bool Schema();
|
||||
|
||||
/// <summary>
|
||||
/// Generates a DSC resource JSON manifest for the resource. If the
|
||||
/// outputDir is not provided, the manifest will be printed to the console.
|
||||
/// </summary>
|
||||
/// <param name="outputDir"> The directory where the manifest should be
|
||||
/// saved. If null, the manifest will be printed to the console.</param>
|
||||
/// <returns>True if the manifest was successfully generated and saved,otherwise false.</returns>
|
||||
public abstract bool Manifest(string? outputDir);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of supported modules for the resource.
|
||||
/// </summary>
|
||||
/// <returns>Gets a list of supported modules.</returns>
|
||||
public abstract IList<string> GetSupportedModules();
|
||||
|
||||
/// <summary>
|
||||
/// Writes a JSON output line to the console.
|
||||
/// </summary>
|
||||
/// <param name="output">The JSON output to write.</param>
|
||||
protected void WriteJsonOutputLine(JsonNode output)
|
||||
{
|
||||
var json = output.ToJsonString(new() { WriteIndented = false });
|
||||
WriteJsonOutputLine(json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a JSON output line to the console.
|
||||
/// </summary>
|
||||
/// <param name="output">The JSON output to write.</param>
|
||||
protected void WriteJsonOutputLine(string output)
|
||||
{
|
||||
Console.WriteLine(output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a message output line to the console with the specified message level.
|
||||
/// </summary>
|
||||
/// <param name="level">The level of the message.</param>
|
||||
/// <param name="message">The message to write.</param>
|
||||
protected void WriteMessageOutputLine(DscMessageLevel level, string message)
|
||||
{
|
||||
var messageObj = new Dictionary<string, string>
|
||||
{
|
||||
[GetMessageLevel(level)] = message,
|
||||
};
|
||||
var messageJson = System.Text.Json.JsonSerializer.Serialize(messageObj);
|
||||
Console.Error.WriteLine(messageJson);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message level as a string based on the provided dsc message level enum value.
|
||||
/// </summary>
|
||||
/// <param name="level">The dsc message level.</param>
|
||||
/// <returns>A string representation of the message level.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the provided message level is not recognized.</exception>
|
||||
private static string GetMessageLevel(DscMessageLevel level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
DscMessageLevel.Error => "error",
|
||||
DscMessageLevel.Warning => "warn",
|
||||
DscMessageLevel.Info => "info",
|
||||
DscMessageLevel.Debug => "debug",
|
||||
DscMessageLevel.Trace => "trace",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
|
||||
};
|
||||
}
|
||||
}
|
||||
248
src/dsc/v3/PowerToys.DSC/DSCResources/SettingsResource.cs
Normal file
248
src/dsc/v3/PowerToys.DSC/DSCResources/SettingsResource.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using PowerToys.DSC.Models;
|
||||
using PowerToys.DSC.Models.FunctionData;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.DSCResources;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the DSC resource for managing PowerToys settings.
|
||||
/// </summary>
|
||||
public sealed class SettingsResource : BaseResource
|
||||
{
|
||||
private static readonly CompositeFormat FailedToWriteManifests = CompositeFormat.Parse(Resources.FailedToWriteManifests);
|
||||
|
||||
public const string AppModule = "App";
|
||||
public const string ResourceName = "settings";
|
||||
|
||||
private readonly Dictionary<string, Func<string?, ISettingsFunctionData>> _moduleFunctionData;
|
||||
|
||||
public string ModuleOrDefault => string.IsNullOrEmpty(Module) ? AppModule : Module;
|
||||
|
||||
public SettingsResource(string? module)
|
||||
: base(ResourceName, module)
|
||||
{
|
||||
_moduleFunctionData = new()
|
||||
{
|
||||
{ AppModule, CreateModuleFunctionData<GeneralSettings> },
|
||||
{ nameof(ModuleType.AdvancedPaste), CreateModuleFunctionData<AdvancedPasteSettings> },
|
||||
{ nameof(ModuleType.AlwaysOnTop), CreateModuleFunctionData<AlwaysOnTopSettings> },
|
||||
{ nameof(ModuleType.Awake), CreateModuleFunctionData<AwakeSettings> },
|
||||
{ nameof(ModuleType.ColorPicker), CreateModuleFunctionData<ColorPickerSettings> },
|
||||
{ nameof(ModuleType.CropAndLock), CreateModuleFunctionData<CropAndLockSettings> },
|
||||
{ nameof(ModuleType.EnvironmentVariables), CreateModuleFunctionData<EnvironmentVariablesSettings> },
|
||||
{ nameof(ModuleType.FancyZones), CreateModuleFunctionData<FancyZonesSettings> },
|
||||
{ nameof(ModuleType.FileLocksmith), CreateModuleFunctionData<FileLocksmithSettings> },
|
||||
{ nameof(ModuleType.FindMyMouse), CreateModuleFunctionData<FindMyMouseSettings> },
|
||||
{ nameof(ModuleType.Hosts), CreateModuleFunctionData<HostsSettings> },
|
||||
{ nameof(ModuleType.ImageResizer), CreateModuleFunctionData<ImageResizerSettings> },
|
||||
{ nameof(ModuleType.KeyboardManager), CreateModuleFunctionData<KeyboardManagerSettings> },
|
||||
{ nameof(ModuleType.MouseHighlighter), CreateModuleFunctionData<MouseHighlighterSettings> },
|
||||
{ nameof(ModuleType.MouseJump), CreateModuleFunctionData<MouseJumpSettings> },
|
||||
{ nameof(ModuleType.MousePointerCrosshairs), CreateModuleFunctionData<MousePointerCrosshairsSettings> },
|
||||
{ nameof(ModuleType.Peek), CreateModuleFunctionData<PeekSettings> },
|
||||
{ nameof(ModuleType.PowerRename), CreateModuleFunctionData<PowerRenameSettings> },
|
||||
{ nameof(ModuleType.PowerAccent), CreateModuleFunctionData<PowerAccentSettings> },
|
||||
{ nameof(ModuleType.RegistryPreview), CreateModuleFunctionData<RegistryPreviewSettings> },
|
||||
{ nameof(ModuleType.MeasureTool), CreateModuleFunctionData<MeasureToolSettings> },
|
||||
{ nameof(ModuleType.ShortcutGuide), CreateModuleFunctionData<ShortcutGuideSettings> },
|
||||
{ nameof(ModuleType.PowerOCR), CreateModuleFunctionData<PowerOcrSettings> },
|
||||
{ nameof(ModuleType.Workspaces), CreateModuleFunctionData<WorkspacesSettings> },
|
||||
{ nameof(ModuleType.ZoomIt), CreateModuleFunctionData<ZoomItSettings> },
|
||||
|
||||
// The following modules are not currently supported:
|
||||
// - MouseWithoutBorders Contains sensitive configuration values, making export/import potentially insecure.
|
||||
// - PowerLauncher Uses absolute file paths in its settings, which are not portable across systems.
|
||||
// - NewPlus Uses absolute file paths in its settings, which are not portable across systems.
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool ExportState(string? input)
|
||||
{
|
||||
var data = CreateFunctionData();
|
||||
data.GetState();
|
||||
WriteJsonOutputLine(data.Output.ToJson());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool GetState(string? input)
|
||||
{
|
||||
return ExportState(input);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool SetState(string? input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
WriteMessageOutputLine(DscMessageLevel.Error, Resources.InputEmptyOrNullError);
|
||||
return false;
|
||||
}
|
||||
|
||||
var data = CreateFunctionData(input);
|
||||
data.GetState();
|
||||
|
||||
// Capture the diff before updating the output
|
||||
var diff = data.GetDiffJson();
|
||||
|
||||
// Only call Set if the desired state is different from the current state
|
||||
if (!data.TestState())
|
||||
{
|
||||
var inputSettings = data.Input.SettingsInternal;
|
||||
data.Output.SettingsInternal = inputSettings;
|
||||
data.SetState();
|
||||
}
|
||||
|
||||
WriteJsonOutputLine(data.Output.ToJson());
|
||||
WriteJsonOutputLine(diff);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool TestState(string? input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
WriteMessageOutputLine(DscMessageLevel.Error, Resources.InputEmptyOrNullError);
|
||||
return false;
|
||||
}
|
||||
|
||||
var data = CreateFunctionData(input);
|
||||
data.GetState();
|
||||
data.Output.InDesiredState = data.TestState();
|
||||
|
||||
WriteJsonOutputLine(data.Output.ToJson());
|
||||
WriteJsonOutputLine(data.GetDiffJson());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Schema()
|
||||
{
|
||||
var data = CreateFunctionData();
|
||||
WriteJsonOutputLine(data.Schema());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// If an output directory is specified, write the manifests to files,
|
||||
/// otherwise output them to the console.
|
||||
/// </remarks>
|
||||
public override bool Manifest(string? outputDir)
|
||||
{
|
||||
var manifests = GenerateManifests();
|
||||
|
||||
if (!string.IsNullOrEmpty(outputDir))
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var (name, manifest) in manifests)
|
||||
{
|
||||
File.WriteAllText(Path.Combine(outputDir, $"microsoft.powertoys.{name}.settings.dsc.resource.json"), manifest);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.InvariantCulture, FailedToWriteManifests, outputDir, ex.Message);
|
||||
WriteMessageOutputLine(DscMessageLevel.Error, errorMessage);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (_, manifest) in manifests)
|
||||
{
|
||||
WriteJsonOutputLine(manifest);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates manifests for the specified module or all supported modules
|
||||
/// if no module is specified.
|
||||
/// </summary>
|
||||
/// <returns>A list of tuples containing the module name and its corresponding manifest JSON.</returns>
|
||||
private List<(string Name, string Manifest)> GenerateManifests()
|
||||
{
|
||||
List<(string Name, string Manifest)> manifests = [];
|
||||
if (!string.IsNullOrEmpty(Module))
|
||||
{
|
||||
manifests.Add((Module, GenerateManifest(Module)));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var module in GetSupportedModules())
|
||||
{
|
||||
manifests.Add((module, GenerateManifest(module)));
|
||||
}
|
||||
}
|
||||
|
||||
return manifests;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a DSC resource JSON manifest for the specified module.
|
||||
/// </summary>
|
||||
/// <param name="module">The name of the module for which to generate the manifest.</param>
|
||||
/// <returns>A JSON string representing the DSC resource manifest.</returns>
|
||||
private string GenerateManifest(string module)
|
||||
{
|
||||
// Note: The description is not localized because the generated
|
||||
// manifest file will be part of the package
|
||||
return new DscManifest($"{module}Settings", "0.1.0")
|
||||
.AddDescription($"Allows management of {module} settings state via the DSC v3 command line interface protocol.")
|
||||
.AddStdinMethod("export", ["export", "--module", module, "--resource", "settings"])
|
||||
.AddStdinMethod("get", ["get", "--module", module, "--resource", "settings"])
|
||||
.AddJsonInputMethod("set", "--input", ["set", "--module", module, "--resource", "settings"], implementsPretest: true, stateAndDiff: true)
|
||||
.AddJsonInputMethod("test", "--input", ["test", "--module", module, "--resource", "settings"], stateAndDiff: true)
|
||||
.AddCommandMethod("schema", ["schema", "--module", module, "--resource", "settings"])
|
||||
.ToJson();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IList<string> GetSupportedModules()
|
||||
{
|
||||
return [.. _moduleFunctionData.Keys.Order()];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the function data for the specified module or the default module if none is specified.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string, if any.</param>
|
||||
/// <returns>An instance of <see cref="ISettingsFunctionData"/> for the specified module.</returns>
|
||||
public ISettingsFunctionData CreateFunctionData(string? input = null)
|
||||
{
|
||||
Debug.Assert(_moduleFunctionData.ContainsKey(ModuleOrDefault), "Module should be supported by the resource.");
|
||||
return _moduleFunctionData[ModuleOrDefault](input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the function data for a specific settings configuration type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSettingsConfig">The type of settings configuration to create function data for.</typeparam>
|
||||
/// <param name="input">The input string, if any.</param>
|
||||
/// <returns>An instance of <see cref="ISettingsFunctionData"/> for the specified settings configuration type.</returns>
|
||||
private ISettingsFunctionData CreateModuleFunctionData<TSettingsConfig>(string? input)
|
||||
where TSettingsConfig : ISettingsConfig, new()
|
||||
{
|
||||
return new SettingsFunctionData<TSettingsConfig>(input);
|
||||
}
|
||||
}
|
||||
152
src/dsc/v3/PowerToys.DSC/Models/DscManifest.cs
Normal file
152
src/dsc/v3/PowerToys.DSC/Models/DscManifest.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace PowerToys.DSC.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Class for building a DSC manifest for PowerToys resources.
|
||||
/// </summary>
|
||||
public sealed class DscManifest
|
||||
{
|
||||
private const string Schema = "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.vscode.json";
|
||||
private const string Executable = @"PowerToys.DSC.exe";
|
||||
|
||||
private readonly string _type;
|
||||
private readonly string _version;
|
||||
private readonly JsonObject _manifest;
|
||||
|
||||
public DscManifest(string type, string version)
|
||||
{
|
||||
_type = type;
|
||||
_version = version;
|
||||
_manifest = new JsonObject
|
||||
{
|
||||
["$schema"] = Schema,
|
||||
["type"] = $"Microsoft.PowerToys/{_type}",
|
||||
["version"] = _version,
|
||||
["tags"] = new JsonArray("PowerToys"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a description to the manifest.
|
||||
/// </summary>
|
||||
/// <param name="description">The description to add.</param>
|
||||
/// <returns>Returns the current instance of <see cref="DscManifest"/>.</returns>
|
||||
public DscManifest AddDescription(string description)
|
||||
{
|
||||
_manifest["description"] = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a method to the manifest with the specified executable and arguments.
|
||||
/// </summary>
|
||||
/// <param name="method">The name of the method to add.</param>
|
||||
/// <param name="inputArg">The input argument for the method</param>
|
||||
/// <param name="args">The list of arguments for the method.</param>
|
||||
/// <param name="implementsPretest">Whether the method implements a pretest.</param>
|
||||
/// <param name="stateAndDiff">Whether the method returns state and diff.</param>
|
||||
/// <returns>Returns the current instance of <see cref="DscManifest"/>.</returns>
|
||||
public DscManifest AddJsonInputMethod(string method, string inputArg, List<string> args, bool? implementsPretest = null, bool? stateAndDiff = null)
|
||||
{
|
||||
var argsJson = CreateJsonArray(args);
|
||||
argsJson.Add(new JsonObject
|
||||
{
|
||||
["jsonInputArg"] = inputArg,
|
||||
["mandatory"] = true,
|
||||
});
|
||||
var methodObject = AddMethod(argsJson, implementsPretest, stateAndDiff);
|
||||
_manifest[method] = methodObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a method to the manifest that reads from standard input (stdin).
|
||||
/// </summary>
|
||||
/// <param name="method">The name of the method to add.</param>
|
||||
/// <param name="args">The list of arguments for the method.</param>
|
||||
/// <param name="implementsPretest">Whether the method implements a pretest.</param>
|
||||
/// <param name="stateAndDiff">Whether the method returns state and diff.</param>
|
||||
/// <returns>Returns the current instance of <see cref="DscManifest"/>.</returns>
|
||||
public DscManifest AddStdinMethod(string method, List<string> args, bool? implementsPretest = null, bool? stateAndDiff = null)
|
||||
{
|
||||
var argsJson = CreateJsonArray(args);
|
||||
var methodObject = AddMethod(argsJson, implementsPretest, stateAndDiff);
|
||||
methodObject["input"] = "stdin";
|
||||
_manifest[method] = methodObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command method to the manifest.
|
||||
/// </summary>
|
||||
/// <param name="method">The name of the method to add.</param>
|
||||
/// <param name="args">The list of arguments for the method.</param>
|
||||
/// <returns>Returns the current instance of <see cref="DscManifest"/>.</returns>
|
||||
public DscManifest AddCommandMethod(string method, List<string> args)
|
||||
{
|
||||
_manifest[method] = new JsonObject
|
||||
{
|
||||
["command"] = AddMethod(CreateJsonArray(args)),
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the JSON representation of the manifest.
|
||||
/// </summary>
|
||||
/// <returns>Returns the JSON string of the manifest.</returns>
|
||||
public string ToJson()
|
||||
{
|
||||
return _manifest.ToJsonString(new() { WriteIndented = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a method to the manifest with the specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The list of arguments for the method.</param>
|
||||
/// <param name="implementsPretest">Whether the method implements a pretest.</param>
|
||||
/// <param name="stateAndDiff">Whether the method returns state and diff.</param>
|
||||
/// <returns>Returns the method object.</returns>
|
||||
private JsonObject AddMethod(JsonArray args, bool? implementsPretest = null, bool? stateAndDiff = null)
|
||||
{
|
||||
var methodObject = new JsonObject
|
||||
{
|
||||
["executable"] = Executable,
|
||||
["args"] = args,
|
||||
};
|
||||
|
||||
if (implementsPretest.HasValue)
|
||||
{
|
||||
methodObject["implementsPretest"] = implementsPretest.Value;
|
||||
}
|
||||
|
||||
if (stateAndDiff.HasValue)
|
||||
{
|
||||
methodObject["return"] = stateAndDiff.Value ? "stateAndDiff" : "state";
|
||||
}
|
||||
|
||||
return methodObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a JSON array from a list of strings.
|
||||
/// </summary>
|
||||
/// <param name="args">The list of strings to convert.</param>
|
||||
/// <returns>Returns the JSON array.</returns>
|
||||
private JsonArray CreateJsonArray(List<string> args)
|
||||
{
|
||||
var jsonArray = new JsonArray();
|
||||
foreach (var arg in args)
|
||||
{
|
||||
jsonArray.Add(arg);
|
||||
}
|
||||
|
||||
return jsonArray;
|
||||
}
|
||||
}
|
||||
36
src/dsc/v3/PowerToys.DSC/Models/DscMessageLevel.cs
Normal file
36
src/dsc/v3/PowerToys.DSC/Models/DscMessageLevel.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace PowerToys.DSC.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the severity level of a message.
|
||||
/// </summary>
|
||||
public enum DscMessageLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an error message.
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a warning message.
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// Represents an informational message.
|
||||
/// </summary>
|
||||
Info,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a debug message.
|
||||
/// </summary>
|
||||
Debug,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a trace message.
|
||||
/// </summary>
|
||||
Trace,
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using NJsonSchema.Generation;
|
||||
using PowerToys.DSC.Models.ResourceObjects;
|
||||
|
||||
namespace PowerToys.DSC.Models.FunctionData;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for function data objects.
|
||||
/// </summary>
|
||||
public class BaseFunctionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a JSON schema for the specified resource object type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the resource object.</typeparam>
|
||||
/// <returns>A JSON schema string.</returns>
|
||||
protected static string GenerateSchema<T>()
|
||||
where T : BaseResourceObject
|
||||
{
|
||||
var settings = new SystemTextJsonSchemaGeneratorSettings()
|
||||
{
|
||||
FlattenInheritanceHierarchy = true,
|
||||
SerializerOptions =
|
||||
{
|
||||
IgnoreReadOnlyFields = true,
|
||||
},
|
||||
};
|
||||
var generator = new JsonSchemaGenerator(settings);
|
||||
var schema = generator.Generate(typeof(T));
|
||||
return schema.ToJson(Formatting.None);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Nodes;
|
||||
using PowerToys.DSC.Models.ResourceObjects;
|
||||
|
||||
namespace PowerToys.DSC.Models.FunctionData;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for function data related to settings.
|
||||
/// </summary>
|
||||
public interface ISettingsFunctionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the input settings resource object.
|
||||
/// </summary>
|
||||
public ISettingsResourceObject Input { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output settings resource object.
|
||||
/// </summary>
|
||||
public ISettingsResourceObject Output { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current settings.
|
||||
/// </summary>
|
||||
public void GetState();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current settings.
|
||||
/// </summary>
|
||||
public void SetState();
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the current settings and the desired state are valid.
|
||||
/// </summary>
|
||||
/// <returns>True if the current settings match the desired state; otherwise false.</returns>
|
||||
public bool TestState();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the difference between the current settings and the desired state in JSON format.
|
||||
/// </summary>
|
||||
/// <returns>A JSON array representing the differences.</returns>
|
||||
public JsonArray GetDiffJson();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the schema for the settings resource object.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string Schema();
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using PowerToys.DSC.Models.ResourceObjects;
|
||||
|
||||
namespace PowerToys.DSC.Models.FunctionData;
|
||||
|
||||
/// <summary>
|
||||
/// Represents function data for the settings DSC resource.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSettingsConfig">The module settings configuration type.</typeparam>
|
||||
public sealed class SettingsFunctionData<TSettingsConfig> : BaseFunctionData, ISettingsFunctionData
|
||||
where TSettingsConfig : ISettingsConfig, new()
|
||||
{
|
||||
private static readonly SettingsUtils _settingsUtils = new();
|
||||
private static readonly TSettingsConfig _settingsConfig = new();
|
||||
|
||||
private readonly SettingsResourceObject<TSettingsConfig> _input;
|
||||
private readonly SettingsResourceObject<TSettingsConfig> _output;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISettingsResourceObject Input => _input;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISettingsResourceObject Output => _output;
|
||||
|
||||
public SettingsFunctionData(string? input = null)
|
||||
{
|
||||
_output = new();
|
||||
_input = string.IsNullOrEmpty(input) ? new() : JsonSerializer.Deserialize<SettingsResourceObject<TSettingsConfig>>(input) ?? new();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void GetState()
|
||||
{
|
||||
_output.Settings = GetSettings();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetState()
|
||||
{
|
||||
Debug.Assert(_output.Settings != null, "Output settings should not be null");
|
||||
SaveSettings(_output.Settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TestState()
|
||||
{
|
||||
var input = JsonSerializer.SerializeToNode(_input.Settings);
|
||||
var output = JsonSerializer.SerializeToNode(_output.Settings);
|
||||
return JsonNode.DeepEquals(input, output);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public JsonArray GetDiffJson()
|
||||
{
|
||||
var diff = new JsonArray();
|
||||
if (!TestState())
|
||||
{
|
||||
diff.Add(SettingsResourceObject<TSettingsConfig>.SettingsJsonPropertyName);
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Schema()
|
||||
{
|
||||
return GenerateSchema<SettingsResourceObject<TSettingsConfig>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings configuration from the settings utils for a specific module.
|
||||
/// </summary>
|
||||
/// <returns>The settings configuration for the module.</returns>
|
||||
private static TSettingsConfig GetSettings()
|
||||
{
|
||||
return _settingsUtils.GetSettingsOrDefault<TSettingsConfig>(_settingsConfig.GetModuleName());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the settings configuration to the settings utils for a specific module.
|
||||
/// </summary>
|
||||
/// <param name="settings">Settings of a specific module</param>
|
||||
private static void SaveSettings(TSettingsConfig settings)
|
||||
{
|
||||
var inputJson = JsonSerializer.Serialize(settings);
|
||||
_settingsUtils.SaveSettings(inputJson, _settingsConfig.GetModuleName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace PowerToys.DSC.Models.ResourceObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all resource objects.
|
||||
/// </summary>
|
||||
public class BaseResourceObject
|
||||
{
|
||||
private readonly JsonSerializerOptions _options;
|
||||
|
||||
public BaseResourceObject()
|
||||
{
|
||||
_options = new()
|
||||
{
|
||||
WriteIndented = false,
|
||||
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether an instance is in the desired state.
|
||||
/// </summary>
|
||||
[JsonPropertyName("_inDesiredState")]
|
||||
[Description("Indicates whether an instance is in the desired state")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public bool? InDesiredState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Generates a JSON representation of the resource object.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public JsonNode ToJson()
|
||||
{
|
||||
return JsonSerializer.SerializeToNode(this, GetType(), _options) ?? new JsonObject();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
|
||||
namespace PowerToys.DSC.Models.ResourceObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for settings resource objects.
|
||||
/// </summary>
|
||||
public interface ISettingsResourceObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the settings configuration.
|
||||
/// </summary>
|
||||
public ISettingsConfig SettingsInternal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether an instance is in the desired state.
|
||||
/// </summary>
|
||||
public bool? InDesiredState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Generates a JSON representation of the resource object.
|
||||
/// </summary>
|
||||
/// <returns>String representation of the resource object in JSON format.</returns>
|
||||
public JsonNode ToJson();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using NJsonSchema.Annotations;
|
||||
|
||||
namespace PowerToys.DSC.Models.ResourceObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a settings resource object for a module's settings configuration.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSettingsConfig">The type of the settings configuration.</typeparam>
|
||||
public sealed class SettingsResourceObject<TSettingsConfig> : BaseResourceObject, ISettingsResourceObject
|
||||
where TSettingsConfig : ISettingsConfig, new()
|
||||
{
|
||||
public const string SettingsJsonPropertyName = "settings";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the settings content for the module.
|
||||
/// </summary>
|
||||
[JsonPropertyName(SettingsJsonPropertyName)]
|
||||
[Required]
|
||||
[Description("The settings content for the module.")]
|
||||
[JsonSchemaType(typeof(object))]
|
||||
public TSettingsConfig Settings { get; set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public ISettingsConfig SettingsInternal { get => Settings; set => Settings = (TSettingsConfig)value; }
|
||||
}
|
||||
51
src/dsc/v3/PowerToys.DSC/Options/InputOption.cs
Normal file
51
src/dsc/v3/PowerToys.DSC/Options/InputOption.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an option for specifying JSON input for the dsc command.
|
||||
/// </summary>
|
||||
public sealed class InputOption : Option<string>
|
||||
{
|
||||
private static readonly CompositeFormat InvalidJsonInputError = CompositeFormat.Parse(Resources.InvalidJsonInputError);
|
||||
|
||||
public InputOption()
|
||||
: base("--input", Resources.InputOptionDescription)
|
||||
{
|
||||
AddValidator(OptionValidator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the JSON input provided to the option.
|
||||
/// </summary>
|
||||
/// <param name="result">The option result to validate.</param>
|
||||
private void OptionValidator(OptionResult result)
|
||||
{
|
||||
var value = result.GetValueOrDefault<string>() ?? string.Empty;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
result.ErrorMessage = Resources.InputEmptyOrNullError;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
JsonDocument.Parse(value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.ErrorMessage = string.Format(CultureInfo.InvariantCulture, InvalidJsonInputError, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/dsc/v3/PowerToys.DSC/Options/ModuleOption.cs
Normal file
19
src/dsc/v3/PowerToys.DSC/Options/ModuleOption.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an option for specifying the module name for the dsc command.
|
||||
/// </summary>
|
||||
public sealed class ModuleOption : Option<string?>
|
||||
{
|
||||
public ModuleOption()
|
||||
: base("--module", Resources.ModuleOptionDescription)
|
||||
{
|
||||
}
|
||||
}
|
||||
43
src/dsc/v3/PowerToys.DSC/Options/OutputDirectoryOption.cs
Normal file
43
src/dsc/v3/PowerToys.DSC/Options/OutputDirectoryOption.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an option for specifying the output directory for the dsc command.
|
||||
/// </summary>
|
||||
public sealed class OutputDirectoryOption : Option<string>
|
||||
{
|
||||
private static readonly CompositeFormat InvalidOutputDirectoryError = CompositeFormat.Parse(Resources.InvalidOutputDirectoryError);
|
||||
|
||||
public OutputDirectoryOption()
|
||||
: base("--outputDir", Resources.OutputDirectoryOptionDescription)
|
||||
{
|
||||
AddValidator(OptionValidator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the output directory option.
|
||||
/// </summary>
|
||||
/// <param name="result">The option result to validate.</param>
|
||||
private void OptionValidator(OptionResult result)
|
||||
{
|
||||
var value = result.GetValueOrDefault<string>() ?? string.Empty;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
result.ErrorMessage = Resources.OutputDirectoryEmptyOrNullError;
|
||||
}
|
||||
else if (!Directory.Exists(value))
|
||||
{
|
||||
result.ErrorMessage = string.Format(CultureInfo.InvariantCulture, InvalidOutputDirectoryError, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/dsc/v3/PowerToys.DSC/Options/ResourceOption.cs
Normal file
43
src/dsc/v3/PowerToys.DSC/Options/ResourceOption.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using PowerToys.DSC.Properties;
|
||||
|
||||
namespace PowerToys.DSC.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an option for specifying the resource name for the dsc command.
|
||||
/// </summary>
|
||||
public sealed class ResourceOption : Option<string>
|
||||
{
|
||||
private static readonly CompositeFormat InvalidResourceNameError = CompositeFormat.Parse(Resources.InvalidResourceNameError);
|
||||
|
||||
private readonly IList<string> _resources = [];
|
||||
|
||||
public ResourceOption(IList<string> resources)
|
||||
: base("--resource", Resources.ResourceOptionDescription)
|
||||
{
|
||||
_resources = resources;
|
||||
IsRequired = true;
|
||||
AddValidator(OptionValidator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the resource option to ensure that the specified resource name is valid.
|
||||
/// </summary>
|
||||
/// <param name="result">The option result to validate.</param>
|
||||
private void OptionValidator(OptionResult result)
|
||||
{
|
||||
var value = result.GetValueOrDefault<string>() ?? string.Empty;
|
||||
if (!_resources.Contains(value))
|
||||
{
|
||||
result.ErrorMessage = string.Format(CultureInfo.InvariantCulture, InvalidResourceNameError, string.Join(", ", _resources));
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/dsc/v3/PowerToys.DSC/PowerToys.DSC.csproj
Normal file
48
src/dsc/v3/PowerToys.DSC/PowerToys.DSC.csproj
Normal file
@@ -0,0 +1,48 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<AssemblyName>PowerToys.DSC</AssemblyName>
|
||||
<AssemblyDescription>PowerToys DSC</AssemblyDescription>
|
||||
<RootNamespace>PowerToys.DSC</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- Ensure WindowsDesktop runtime pack is included for consistent WindowsBase.dll version -->
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NJsonSchema" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- In debug mode, generate the DSC resource JSON files -->
|
||||
<Target Name="GenerateDscResourceJsonFiles" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
|
||||
<Message Text="Generating DSC resource JSON files inside ..." Importance="high" />
|
||||
<Exec Command="dotnet "$(TargetPath)" manifest --resource settings --outputDir "$(TargetDir)\"" />
|
||||
</Target>
|
||||
</Project>
|
||||
29
src/dsc/v3/PowerToys.DSC/Program.cs
Normal file
29
src/dsc/v3/PowerToys.DSC/Program.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.Threading.Tasks;
|
||||
using PowerToys.DSC.Commands;
|
||||
|
||||
namespace PowerToys.DSC;
|
||||
|
||||
/// <summary>
|
||||
/// Main entry point for the PowerToys Desired State Configuration CLI application.
|
||||
/// </summary>
|
||||
public class Program
|
||||
{
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
var rootCommand = new RootCommand(Properties.Resources.PowerToysDSC);
|
||||
rootCommand.AddCommand(new GetCommand());
|
||||
rootCommand.AddCommand(new SetCommand());
|
||||
rootCommand.AddCommand(new ExportCommand());
|
||||
rootCommand.AddCommand(new TestCommand());
|
||||
rootCommand.AddCommand(new SchemaCommand());
|
||||
rootCommand.AddCommand(new ManifestCommand());
|
||||
rootCommand.AddCommand(new ModulesCommand());
|
||||
return await rootCommand.InvokeAsync(args);
|
||||
}
|
||||
}
|
||||
234
src/dsc/v3/PowerToys.DSC/Properties/Resources.Designer.cs
generated
Normal file
234
src/dsc/v3/PowerToys.DSC/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,234 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PowerToys.DSC.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PowerToys.DSC.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Get all state instances.
|
||||
/// </summary>
|
||||
internal static string ExportCommandDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportCommandDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to write manifests to directory '{0}': {1}.
|
||||
/// </summary>
|
||||
internal static string FailedToWriteManifests {
|
||||
get {
|
||||
return ResourceManager.GetString("FailedToWriteManifests", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Get the resource state.
|
||||
/// </summary>
|
||||
internal static string GetCommandDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("GetCommandDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Input cannot be empty or null.
|
||||
/// </summary>
|
||||
internal static string InputEmptyOrNullError {
|
||||
get {
|
||||
return ResourceManager.GetString("InputEmptyOrNullError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The JSON input.
|
||||
/// </summary>
|
||||
internal static string InputOptionDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("InputOptionDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid JSON input: {0}.
|
||||
/// </summary>
|
||||
internal static string InvalidJsonInputError {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidJsonInputError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid output directory: {0}.
|
||||
/// </summary>
|
||||
internal static string InvalidOutputDirectoryError {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidOutputDirectoryError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid resource name. Valid resource names are: {0}.
|
||||
/// </summary>
|
||||
internal static string InvalidResourceNameError {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidResourceNameError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Get the manifest of the dsc resource.
|
||||
/// </summary>
|
||||
internal static string ManifestCommandDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ManifestCommandDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Module '{0}' is not supported for the resource {1}. Use the 'module' command to list available modules..
|
||||
/// </summary>
|
||||
internal static string ModuleNotSupportedByResource {
|
||||
get {
|
||||
return ResourceManager.GetString("ModuleNotSupportedByResource", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The module name.
|
||||
/// </summary>
|
||||
internal static string ModuleOptionDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ModuleOptionDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Get all supported modules for a specific resource.
|
||||
/// </summary>
|
||||
internal static string ModulesCommandDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ModulesCommandDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Output directory cannot be empty or null.
|
||||
/// </summary>
|
||||
internal static string OutputDirectoryEmptyOrNullError {
|
||||
get {
|
||||
return ResourceManager.GetString("OutputDirectoryEmptyOrNullError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The output directory.
|
||||
/// </summary>
|
||||
internal static string OutputDirectoryOptionDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("OutputDirectoryOptionDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to PowerToys Desired State Configuration commands.
|
||||
/// </summary>
|
||||
internal static string PowerToysDSC {
|
||||
get {
|
||||
return ResourceManager.GetString("PowerToysDSC", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The resource name.
|
||||
/// </summary>
|
||||
internal static string ResourceOptionDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ResourceOptionDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Outputs schema of the resource.
|
||||
/// </summary>
|
||||
internal static string SchemaCommandDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("SchemaCommandDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Set the resource state.
|
||||
/// </summary>
|
||||
internal static string SetCommandDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("SetCommandDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Test the resource state.
|
||||
/// </summary>
|
||||
internal static string TestCommandDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("TestCommandDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
183
src/dsc/v3/PowerToys.DSC/Properties/Resources.resx
Normal file
183
src/dsc/v3/PowerToys.DSC/Properties/Resources.resx
Normal file
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="PowerToysDSC" xml:space="preserve">
|
||||
<value>PowerToys Desired State Configuration commands</value>
|
||||
<comment>{Locked="PowerToys Desired State Configuration"}</comment>
|
||||
</data>
|
||||
<data name="ModuleNotSupportedByResource" xml:space="preserve">
|
||||
<value>Module '{0}' is not supported for the resource {1}. Use the 'module' command to list available modules.</value>
|
||||
<comment>{Locked="'module'","{0}","{1}"}</comment>
|
||||
</data>
|
||||
<data name="ExportCommandDescription" xml:space="preserve">
|
||||
<value>Get all state instances</value>
|
||||
</data>
|
||||
<data name="GetCommandDescription" xml:space="preserve">
|
||||
<value>Get the resource state</value>
|
||||
</data>
|
||||
<data name="ManifestCommandDescription" xml:space="preserve">
|
||||
<value>Get the manifest of the dsc resource</value>
|
||||
</data>
|
||||
<data name="ModulesCommandDescription" xml:space="preserve">
|
||||
<value>Get all supported modules for a specific resource</value>
|
||||
</data>
|
||||
<data name="SchemaCommandDescription" xml:space="preserve">
|
||||
<value>Outputs schema of the resource</value>
|
||||
</data>
|
||||
<data name="SetCommandDescription" xml:space="preserve">
|
||||
<value>Set the resource state</value>
|
||||
</data>
|
||||
<data name="TestCommandDescription" xml:space="preserve">
|
||||
<value>Test the resource state</value>
|
||||
</data>
|
||||
<data name="InputEmptyOrNullError" xml:space="preserve">
|
||||
<value>Input cannot be empty or null</value>
|
||||
</data>
|
||||
<data name="FailedToWriteManifests" xml:space="preserve">
|
||||
<value>Failed to write manifests to directory '{0}': {1}</value>
|
||||
<comment>{Locked="{0}","{1}"}</comment>
|
||||
</data>
|
||||
<data name="InputOptionDescription" xml:space="preserve">
|
||||
<value>The JSON input</value>
|
||||
</data>
|
||||
<data name="ModuleOptionDescription" xml:space="preserve">
|
||||
<value>The module name</value>
|
||||
</data>
|
||||
<data name="OutputDirectoryOptionDescription" xml:space="preserve">
|
||||
<value>The output directory</value>
|
||||
</data>
|
||||
<data name="ResourceOptionDescription" xml:space="preserve">
|
||||
<value>The resource name</value>
|
||||
</data>
|
||||
<data name="InvalidJsonInputError" xml:space="preserve">
|
||||
<value>Invalid JSON input: {0}</value>
|
||||
<comment>{Locked="{0}"}</comment>
|
||||
</data>
|
||||
<data name="OutputDirectoryEmptyOrNullError" xml:space="preserve">
|
||||
<value>Output directory cannot be empty or null</value>
|
||||
</data>
|
||||
<data name="InvalidOutputDirectoryError" xml:space="preserve">
|
||||
<value>Invalid output directory: {0}</value>
|
||||
<comment>{Locked="{0}"}</comment>
|
||||
</data>
|
||||
<data name="InvalidResourceNameError" xml:space="preserve">
|
||||
<value>Invalid resource name. Valid resource names are: {0}</value>
|
||||
<comment>{Locked="{0}"}</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -48,7 +48,6 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<None Include="KeyboardManager.base.rc" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@@ -66,6 +65,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files\KeyboardManager.rc" />
|
||||
<None Include="KeyboardManager.base.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources.resx" />
|
||||
@@ -82,4 +82,7 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h KeyboardManager.base.rc KeyboardManager.rc" />
|
||||
</Target>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user