[DSC]Improve module generation for versioned release install (#32196)

* [DSC]Generate psd1 file to include version

* Don't expose build paths on release builds

* Stop-Process with -Force to stop first run process
This commit is contained in:
Jaime Bernardo 2024-04-03 10:58:44 +01:00 committed by GitHub
parent 7b89482b94
commit a24ffb3168
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 156 additions and 102 deletions

View File

@ -47,7 +47,7 @@ PowerToys.Settings.exe set FancyZones.FancyzonesEditorHotkey "Shift+Ctrl+Alt+F"
# How DSC is implemented
We use `PowerToys.Settings.DSC.Schema.Generator` to generate the bulk of `PowerToysConfigure.psm1` file. It also uses dotnet reflection capabilities to inspect `PowerToys.Settings.UI.Lib.dll` assembly and generate properties for the modules we have. The actual generation is done as a `PowerToys.Settings.DSC.Schema.Generator.csproj` post-build action.
We use `PowerToys.Settings.DSC.Schema.Generator` to generate the bulk of `PowerToysConfigure.psm1` and `PowerToysConfigure.psd1` files. It also uses dotnet reflection capabilities to inspect `PowerToys.Settings.UI.Lib.dll` assembly and generate properties for the modules we have. The actual generation is done as a `PowerToys.Settings.DSC.Schema.Generator.csproj` post-build action.
# Debugging DSC resources
@ -62,7 +62,7 @@ After that, start a new `pwsh` session and `cd` to `src\dsc\Microsoft.PowerToys.
$env:PSModulePath += ";$pwd"
```
Now build `PowerToys.sln` and **move** `src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1` temporarily to `src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\0.0.1\` folder, so it's located alongside with the generated `Microsoft.PowerToys.Configure.psm1`.
You should have the generated `Microsoft.PowerToys.Configure.psm1` and `Microsoft.PowerToys.Configure.psd1` files inside the `src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\0.0.1\` folder.
This will allow DSC to discover our DSC Resource module. See [PSModulePath](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_psmodulepath?view=powershell-7.4#long-description) for more info.

View File

@ -59,7 +59,7 @@
Type="integer"
Value="1"
KeyPath="yes"/>
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
<RemoveFolder Id="RemoveThisFolder" On="uninstall" />
<RemoveFolder Id="RemovePowerToysDscVerFolder" Directory="PowerToysDscVerFolder" On="uninstall" />
@ -79,7 +79,7 @@
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
<Component Id="PowerToysDSC" Win64="yes" Guid="C52AECA0-DA73-49B8-BB49-31EF6640FF1F">
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
</Component>
</Directory>

View File

@ -1,83 +0,0 @@
#
# Module manifest for module 'Microsoft.PowerToys.Configure'
#
# Generated by: Microsoft Corporation
#
# Generated on: 20.11.2023
#
@{
# Script module or binary module file associated with this manifest.
RootModule = 'Microsoft.PowerToys.Configure.psm1'
# Version number of this module.
ModuleVersion = '0.0.1'
# ID used to uniquely identify this module
GUID = '778ed7a1-489d-4dc9-b0f2-2da3b1fe14cb'
# Author of this module
Author = 'Microsoft Corporation'
# Company or vendor of this module
CompanyName = 'Microsoft'
# Copyright statement for this module
Copyright = '(c) Microsoft Corporation. All rights reserved.'
# Description of the functionality provided by this module
Description = 'The module enables settings configuration for an installed PowerToys application.'
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = '*'
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
# Variables to export from this module
VariablesToExport = @()
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()
# DSC resources to export from this module
DscResourcesToExport = @(
'PowerToysConfigure'
)
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
# Tags = @()
# A URL to the license for this module.
# LicenseUri = ''
# A URL to the main website for this project.
# ProjectUri = ''
# A URL to an icon representing this module.
# IconUri = ''
# ReleaseNotes of this module
# ReleaseNotes = ''
# Prerelease string of this module
# Prerelease = ''
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
# RequireLicenseAcceptance = $false
# External dependent modules of this module
# ExternalModuleDependencies = @()
} # End of PSData hashtable
} # End of PrivateData hashtable
}

View File

@ -188,7 +188,6 @@ class {{module.Name}} {
}
}
""";
}
@ -223,8 +222,9 @@ class {{module.Name}} {
var enumsBlock = string.Join(DoubleNewLine, enumsToEmit.Select(EmitEnumDefinition));
var version = interop.CommonManaged.GetProductVersion().Replace("v", string.Empty);
var outputResult = string.Empty;
return $$"""
outputResult += $$"""
#region enums
enum PowerToysConfigureEnsure {
Absent
@ -244,8 +244,13 @@ class {{module.Name}} {
[bool] $Debug = $false
{{modulesResourcePropertiesBlock}}
""";
#if DEBUG
// Only output PowerToysSettings local build for debug builds. No need to expose release build locations.
outputResult += $$"""
[string] GetPowerToysSettingsPath() {
# Obtain PowerToys install location
if ($this.Debug -eq $true) {
$SettingsExePath = "{{debugSettingsPath}}"
} else {
@ -253,7 +258,6 @@ class {{module.Name}} {
if ($installation) {
$SettingsExePath = Join-Path (Join-Path $installation.InstallLocation WinUI3Apps) PowerToys.Settings.exe
# Handle spaces in the path
$SettingsExePath = "`"$SettingsExePath`""
} else {
throw "PowerToys installation wasn't found."
@ -263,6 +267,27 @@ class {{module.Name}} {
return $SettingsExePath
}
""";
#else
outputResult += $$"""
[string] GetPowerToysSettingsPath() {
$installation = Get-CimInstance Win32_Product | Where-Object {$_.Name -eq "PowerToys (Preview)" -and $_.Version -eq "{{version}}"}
if ($installation) {
$SettingsExePath = Join-Path (Join-Path $installation.InstallLocation WinUI3Apps) PowerToys.Settings.exe
$SettingsExePath = "`"$SettingsExePath`""
} else {
throw "PowerToys installation wasn't found."
}
return $SettingsExePath
}
""";
#endif
outputResult += $$"""
[PowerToysConfigure] Get() {
$CurrentState = [PowerToysConfigure]::new()
$SettingsExePath = $this.GetPowerToysSettingsPath()
@ -337,15 +362,15 @@ class {{module.Name}} {
}
# Stop any running PowerToys instances
Stop-Process -Name "PowerToys.Settings" -PassThru | Wait-Process
$PowerToysProcessStopped = Stop-Process -Name "PowerToys" -PassThru
Stop-Process -Name "PowerToys.Settings" -Force -PassThru | Wait-Process
$PowerToysProcessStopped = Stop-Process -Name "PowerToys" -Force -PassThru
$PowerToysProcessStopped | Wait-Process
foreach ($change in $ChangesToApply) {
Start-Process -FilePath $SettingsExePath -Wait -Args "$change"
}
# If the PowerToys was stopped, restart it.
# If the PowerToys process was stopped, restart it.
if ($PowerToysProcessStopped -ne $null) {
Start-Process -FilePath $SettingsExePath
}
@ -353,5 +378,100 @@ class {{module.Name}} {
}
#endregion DscResources
""";
return outputResult;
}
public static string EmitManifestFileContents()
{
var version = interop.CommonManaged.GetProductVersion().Replace("v", string.Empty);
var generatedDate = DateTime.Now.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture);
return $$"""
#
# Module manifest for module 'Microsoft.PowerToys.Configure'
#
# Generated by: Microsoft Corporation
#
# Generated on: {{generatedDate}}
#
@{
# Script module or binary module file associated with this manifest.
RootModule = 'Microsoft.PowerToys.Configure.psm1'
# Version number of this module.
ModuleVersion = '{{version}}'
# ID used to uniquely identify this module
GUID = '778ed7a1-489d-4dc9-b0f2-2da3b1fe14cb'
# Author of this module
Author = 'Microsoft Corporation'
# Company or vendor of this module
CompanyName = 'Microsoft'
# Copyright statement for this module
Copyright = '(c) Microsoft Corporation. All rights reserved.'
# Description of the functionality provided by this module
Description = 'The module enables settings configuration for an installed PowerToys application.'
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = '*'
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
# Variables to export from this module
VariablesToExport = @()
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()
# DSC resources to export from this module
DscResourcesToExport = @(
'PowerToysConfigure'
)
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
# Tags = @()
# A URL to the license for this module.
# LicenseUri = ''
# A URL to the main website for this project.
# ProjectUri = ''
# A URL to an icon representing this module.
# IconUri = ''
# ReleaseNotes of this module
# ReleaseNotes = ''
# Prerelease string of this module
# Prerelease = ''
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
# RequireLicenseAcceptance = $false
# External dependent modules of this module
# ExternalModuleDependencies = @()
} # End of PSData hashtable
} # End of PrivateData hashtable
}
""";
}
}

View File

@ -50,12 +50,13 @@
<PropertyGroup>
<GeneratedDSCModule>"$(ProjectDir)..\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(Version)\Microsoft.PowerToys.Configure.psm1"</GeneratedDSCModule>
<GeneratedDSCManifest>"$(ProjectDir)..\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(Version)\Microsoft.PowerToys.Configure.psd1"</GeneratedDSCManifest>
</PropertyGroup>
<!-- The following sections assume that the machine we're building on is always x64. That means we won't be able to run/inspect arm64 executables, therefore we must always execute x64 generator. -->
<Target Name="PostBuildAction" AfterTargets="Build" Outputs="$(GeneratedDSCModule)" Condition="'$(Platform)'!='ARM64'">
<Exec Command="&quot;$(OutDir)$(AssemblyName).exe&quot; &quot;$(SolutionDir)x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll&quot; $(GeneratedDSCModule)" />
<Exec Command="&quot;$(OutDir)$(AssemblyName).exe&quot; &quot;$(SolutionDir)x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll&quot; $(GeneratedDSCModule) $(GeneratedDSCManifest)" />
</Target>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(Platform)'=='ARM64'">

View File

@ -13,21 +13,35 @@ internal sealed class Program
{
public static int Main(string[] args)
{
if (args.Length != 2)
if (args.Length < 2)
{
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <output path>");
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <module output path> <manifest output path>");
return 1;
}
var dllPath = args[0];
var outputPath = args[1];
var moduleOutputPath = args[1];
var manifestOutputPath = string.Empty;
bool documentationMode = Path.GetExtension(outputPath) == ".md";
bool sampleMode = Path.GetExtension(outputPath) == ".yaml";
bool documentationMode = Path.GetExtension(moduleOutputPath) == ".md";
bool sampleMode = Path.GetExtension(moduleOutputPath) == ".yaml";
if (!documentationMode && !sampleMode)
{
if (args.Length < 3)
{
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <module output path> <manifest output path>");
return 1;
}
else
{
manifestOutputPath = args[2];
}
}
try
{
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
Directory.CreateDirectory(Path.GetDirectoryName(moduleOutputPath));
var assembly = Assembly.LoadFrom(dllPath);
var moduleSettings = Introspection.ParseModuleSettings(assembly);
@ -46,11 +60,13 @@ internal sealed class Program
}
else
{
var manifestFileContents = DSCGeneration.EmitManifestFileContents();
File.WriteAllText(manifestOutputPath, manifestFileContents);
var debugSettingsPath = Path.Combine(Directory.GetParent(dllPath).FullName, "PowerToys.Settings.exe");
outputFileContents = DSCGeneration.EmitModuleFileContents(moduleSettings, generalSettings, debugSettingsPath);
}
File.WriteAllText(outputPath, outputFileContents);
File.WriteAllText(moduleOutputPath, outputFileContents);
}
catch (Exception ex)
{