From ecb0a20cbb805aacfe3e2234c2115489ec57452a Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 31 Jul 2026 02:15:16 +0000 Subject: [PATCH] feat: add Cake build to produce powershell module (#2) - Add Cake build project - Change to CPM - Package bumps for security bumps - Reorganise solution structure to group harness projects --- .gitignore | 2 +- Directory.Packages.props | 14 +++++ PowershellModule.slnx | 19 +++++-- PowershellModule/build/build.ps1 | 48 ---------------- build.ps1 | 2 + build.sh | 1 + build/Build.csproj | 13 +++++ build/Program.cs | 55 +++++++++++++++++++ build/Scripts/CreateModuleManifest.ps1 | 24 ++++++++ build/Tasks/BuildTask.cs | 36 ++++++++++++ build/Tasks/CleanTask.cs | 23 ++++++++ build/Tasks/CopyOutputTask.cs | 49 +++++++++++++++++ build/Tasks/DefaultTask.cs | 16 ++++++ .../ModuleCore}/Calendar/Calendar.cs | 2 +- src/ModuleCore/Directory.Build.props | 6 ++ .../ModuleCore}/ModuleCore.csproj | 0 .../ModuleHarness/ModuleHarness.csproj | 0 {Harness => src/ModuleHarness}/Program.cs | 2 +- .../PowershellHarness}/CustomHost.cs | 0 .../PowershellHarness.csproj | 3 +- .../PowershellHarness}/Program.cs | 0 .../Calendar/GetCalendarCommand.cs | 0 src/PowershellModule/Directory.Build.props | 6 ++ .../PowershellModule}/PowershellModule.csproj | 4 +- .../PowershellModule}/manifest.json | 0 .../meta/PowershellModule.psd1 | 0 .../meta/PowershellModule.psm1 | 0 27 files changed, 267 insertions(+), 58 deletions(-) create mode 100644 Directory.Packages.props delete mode 100644 PowershellModule/build/build.ps1 create mode 100644 build.ps1 create mode 100644 build.sh create mode 100644 build/Build.csproj create mode 100644 build/Program.cs create mode 100644 build/Scripts/CreateModuleManifest.ps1 create mode 100644 build/Tasks/BuildTask.cs create mode 100644 build/Tasks/CleanTask.cs create mode 100644 build/Tasks/CopyOutputTask.cs create mode 100644 build/Tasks/DefaultTask.cs rename {ModuleCore => src/ModuleCore}/Calendar/Calendar.cs (99%) create mode 100644 src/ModuleCore/Directory.Build.props rename {ModuleCore => src/ModuleCore}/ModuleCore.csproj (100%) rename Harness/Harness.csproj => src/ModuleHarness/ModuleHarness.csproj (100%) rename {Harness => src/ModuleHarness}/Program.cs (94%) rename {PowershellHarness => src/PowershellHarness}/CustomHost.cs (100%) rename {PowershellHarness => src/PowershellHarness}/PowershellHarness.csproj (75%) rename {PowershellHarness => src/PowershellHarness}/Program.cs (100%) rename {PowershellModule => src/PowershellModule}/Calendar/GetCalendarCommand.cs (100%) create mode 100644 src/PowershellModule/Directory.Build.props rename {PowershellModule => src/PowershellModule}/PowershellModule.csproj (72%) rename {PowershellModule => src/PowershellModule}/manifest.json (100%) rename {PowershellModule => src/PowershellModule}/meta/PowershellModule.psd1 (100%) rename {PowershellModule => src/PowershellModule}/meta/PowershellModule.psm1 (100%) diff --git a/.gitignore b/.gitignore index 8ffca3f..2fe225e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ riderModule.iml /.idea *.DotSettings.user # Don't care about files created from the build script -PowershellModule/build/PowershellModule +output/ \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..1fe7672 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,14 @@ + + + true + + + + + + + + + + + \ No newline at end of file diff --git a/PowershellModule.slnx b/PowershellModule.slnx index 4331297..858fae2 100644 --- a/PowershellModule.slnx +++ b/PowershellModule.slnx @@ -1,6 +1,17 @@ - - - - + + + + + + + + + + + + + + + diff --git a/PowershellModule/build/build.ps1 b/PowershellModule/build/build.ps1 deleted file mode 100644 index 1097330..0000000 --- a/PowershellModule/build/build.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -param ( - [switch] $release -) - -$wdir = $PSScriptRoot -$module = "PowershellModule" -$output = "$wdir/$module" - -if (Test-Path $output) -{ - Remove-Item $output -Recurse -} - -New-Item $output -ItemType Directory - -# copy the built dlls to this directory -if($release){ - # For release we'll get a bunch of dependencies that we honestly do not need. - # This module is built to run assuming the dotnet 10 runtime exists, and powershell is 7.6 or higher, - # because of this we really don't need the system.* dlls that come with the publish, or the random ass - # newtonsoft.json.dll that comes from ???? publish fucking sucks for libraries even with framework-dependent - # selected as the publish option - Copy-Item -Path "$( $wdir )\..\bin\Release\net10.0\publish\$module.dll" -Destination $output - Copy-Item -Path "$( $wdir )\..\bin\Release\net10.0\publish\ModuleCore.dll" -Destination $output -} else -{ - Copy-Item -Path "$( $wdir )\..\bin\Debug\net10.0\*.dll" -Destination $output -} -#Copy-Item -Path "$( $wdir )\..\bin\Debug\netstandard2.1\*.dll" -Destination $output - -$manifestSplat = @{ - Path = "$output/$module.psd1" - Author = 'Me' - # technically we only need the base module here and it'll locate any dlls it needs - NestedModules = @("$module.dll")#(Get-ChildItem "$output/*.dll" | Select-Object { $_.Name } -ExpandProperty Name) - RootModule = "$module.psm1" -# FunctionsToExport = @('Get-Calendar') - CmdletsToExport = @('Get-Calendar') - # These should all be empty arrays - otherwise they default to wildcard which the generated psd1 will helpfully - # tell you not to do for performance reasons, as the fallback if these are left out is to use wildcard... - FunctionsToExport = @() - VariablesToExport = @() - AliasesToExport = @() -} - -New-ModuleManifest @manifestSplat -# We don't actually need content here, it's just so PowerShell has an entry point -New-Item "$output/$module.psm1" -ItemType File diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..a707719 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,2 @@ +dotnet run --project build/Build.csproj -- $args +exit $LASTEXITCODE; \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..dfd6b85 --- /dev/null +++ b/build.sh @@ -0,0 +1 @@ +dotnet run --project ./build/Build.csproj -- "$@" diff --git a/build/Build.csproj b/build/Build.csproj new file mode 100644 index 0000000..0d0955f --- /dev/null +++ b/build/Build.csproj @@ -0,0 +1,13 @@ + + + Exe + net10.0 + $(MSBuildProjectDirectory) + + + + + + + + \ No newline at end of file diff --git a/build/Program.cs b/build/Program.cs new file mode 100644 index 0000000..ffb3738 --- /dev/null +++ b/build/Program.cs @@ -0,0 +1,55 @@ +using Cake.Common; +using Cake.Common.IO; +using Cake.Common.IO.Paths; +using Cake.Core; +using Cake.Frosting; +using Cake.Powershell; + +public static class Program +{ + public static int Main(string[] args) + { + return new CakeHost() + .UseContext() + .Run(args); + } +} + +public class BuildContext : FrostingContext +{ + /// + /// Base source directory + /// + public ConvertableDirectoryPath BaseSourceLocation { get; set; } + + /// + /// Powershell module project folder + /// + public ConvertableDirectoryPath PowershellModuleProjectDirectory { get; set; } + + /// + /// Powershell module csproj location + /// + public ConvertableFilePath PowershellModuleCsproj { get; set; } + + /// + /// Output directory for built DLLs and powershell module manifest files + /// + public ConvertableDirectoryPath PowershellModuleOutputDir { get; set; } + + /// + /// Path to powershell script that creates the module manifest files + /// + public ConvertableFilePath CreateModuleManifestScript { get; set; } + + public BuildContext(ICakeContext context) + : base(context) + { + BaseSourceLocation = context.Directory("../src"); + PowershellModuleProjectDirectory = BaseSourceLocation + context.Directory("PowershellModule"); + PowershellModuleCsproj = PowershellModuleProjectDirectory + context.File("PowershellModule.csproj"); + PowershellModuleOutputDir = context.Directory("../") + context.Directory("output") + context.Directory("PowershellModule"); + var buildScriptDirectory = context.Directory("./Scripts"); + CreateModuleManifestScript = buildScriptDirectory + context.File("CreateModuleManifest.ps1"); + } +} \ No newline at end of file diff --git a/build/Scripts/CreateModuleManifest.ps1 b/build/Scripts/CreateModuleManifest.ps1 new file mode 100644 index 0000000..4e3d920 --- /dev/null +++ b/build/Scripts/CreateModuleManifest.ps1 @@ -0,0 +1,24 @@ +param ( + [string]$path, + [string]$guid, + [string]$author, + [string[]]$nestedModules, + [string]$rootModule, + [string[]]$cmdletsToExport, + [string]$manifestFileLocation +) + +$manifestSplat = @{ + Path = "$path" + GUID = "$guid" + Author = "$author" + NestedModules = @($nestedModules) + RootModule = "$rootModule" + CmdletsToExport = @($cmdletsToExport) + FunctionsToExport = @() + VariablesToExport = @() + AliasesToExport = @() +} + +New-ModuleManifest @manifestSplat +New-Item "$manifestFileLocation" -ItemType File \ No newline at end of file diff --git a/build/Tasks/BuildTask.cs b/build/Tasks/BuildTask.cs new file mode 100644 index 0000000..c731bcb --- /dev/null +++ b/build/Tasks/BuildTask.cs @@ -0,0 +1,36 @@ +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.Build; +using Cake.Common.Tools.DotNet.MSBuild; +using Cake.Core.Diagnostics; +using Cake.Frosting; + +namespace Build.Tasks; + +[TaskName("Build")] +[IsDependentOn(typeof(CleanTask))] +[IsDependeeOf(typeof(CopyOutputTask))] +public class BuildTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.Log.Information($"Building: {context.PowershellModuleCsproj}"); + + var buildSettings = new DotNetBuildSettings() + { + MSBuildSettings = new DotNetMSBuildSettings() + { + VersionSuffix = "cake" + }, + OutputDirectory = context.PowershellModuleOutputDir, + DiagnosticOutput = true + }; + + // /p:DebugSymbols=false + buildSettings.MSBuildSettings.Properties.Add("DebugSymbols", ["false"]); + // /p:DebugType=None + buildSettings.MSBuildSettings.Properties.Add("DebugType", ["None"]); + + context.DotNetBuild(context.PowershellModuleCsproj, buildSettings); + base.Run(context); + } +} \ No newline at end of file diff --git a/build/Tasks/CleanTask.cs b/build/Tasks/CleanTask.cs new file mode 100644 index 0000000..36ab859 --- /dev/null +++ b/build/Tasks/CleanTask.cs @@ -0,0 +1,23 @@ +using Cake.Common; +using Cake.Common.Tools.DotNet; +using Cake.Core.Diagnostics; +using Cake.Frosting; + +namespace Build.Tasks; + +[TaskName("Clean")] +public class CleanTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.DotNetClean(context.PowershellModuleProjectDirectory); + + var outDir = context.FileSystem.GetDirectory(context.PowershellModuleOutputDir); + if (outDir.Exists) + { + outDir.Delete(true); + } + + base.Run(context); + } +} \ No newline at end of file diff --git a/build/Tasks/CopyOutputTask.cs b/build/Tasks/CopyOutputTask.cs new file mode 100644 index 0000000..175f82a --- /dev/null +++ b/build/Tasks/CopyOutputTask.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Frosting; +using Cake.Powershell; + +namespace Build.Tasks; + +[TaskName("CopyOutput")] +public class CopyOutputTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var powershellModuleName = "PowershellModule"; + var scriptParams = new + { + Path = $"{context.PowershellModuleOutputDir}/{powershellModuleName}.psd1", + Guid = Guid.Parse("5cdf4635-edb0-428c-8d9b-92d0bcd47443"), + Author = "Me", + NestedModules = new[] { $"{powershellModuleName}.dll" }, + RootModule = $"{powershellModuleName}.psm1", + CmdletsToExport = new[] { "Get-Calendar" }, + ManifestFileLocation = $"{context.PowershellModuleOutputDir}/{powershellModuleName}.psm1" + }; + + var psSettings = new PowershellSettings() + { + Arguments = new ProcessArgumentBuilder(), + }; + + // This feels a bit ugly/redundant seeing as I've defined the scriptParams above, but I'm leaving it as is + // until I want to come back and clean this up properly + psSettings.Arguments.Append("path", ToPowershellSafeString(scriptParams.Path)); + psSettings.Arguments.Append("guid", ToPowershellSafeString(scriptParams.Guid.ToString())); + psSettings.Arguments.Append("author", ToPowershellSafeString(scriptParams.Author)); + psSettings.Arguments.Append("nestedModules", $"@({string.Join(",", scriptParams.NestedModules.Select(ToPowershellSafeString))})"); + psSettings.Arguments.Append("rootModule", ToPowershellSafeString(scriptParams.RootModule)); + psSettings.Arguments.Append("cmdletsToExport", $"@({string.Join(",", scriptParams.CmdletsToExport.Select(ToPowershellSafeString))})"); + psSettings.Arguments.Append("manifestFileLocation", ToPowershellSafeString(scriptParams.ManifestFileLocation)); + + context.StartPowershellFile(context.CreateModuleManifestScript, psSettings); + context.Log.Information($"Module output to: {context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment)}"); + + base.Run(context); + } + + private static string ToPowershellSafeString(string unescapedString) => $"'{unescapedString}'"; +} \ No newline at end of file diff --git a/build/Tasks/DefaultTask.cs b/build/Tasks/DefaultTask.cs new file mode 100644 index 0000000..200768c --- /dev/null +++ b/build/Tasks/DefaultTask.cs @@ -0,0 +1,16 @@ +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Frosting; + +namespace Build.Tasks; + +// Consider this the "entry" point for builds, task order is defined by a chain of IsDependentOn +[TaskName("Default")] +[IsDependentOn(typeof(CopyOutputTask))] +public class DefaultTask : FrostingTask +{ + public override void Run(ICakeContext context) + { + base.Run(context); + } +} \ No newline at end of file diff --git a/ModuleCore/Calendar/Calendar.cs b/src/ModuleCore/Calendar/Calendar.cs similarity index 99% rename from ModuleCore/Calendar/Calendar.cs rename to src/ModuleCore/Calendar/Calendar.cs index 138f394..019d748 100644 --- a/ModuleCore/Calendar/Calendar.cs +++ b/src/ModuleCore/Calendar/Calendar.cs @@ -254,6 +254,6 @@ public sealed class Calendar public class MonthCalendar { - public List Calendar { get; set; } + public List Calendar { get; set; } = []; public int WeeksInMonth { get; set; } } \ No newline at end of file diff --git a/src/ModuleCore/Directory.Build.props b/src/ModuleCore/Directory.Build.props new file mode 100644 index 0000000..afa6e9c --- /dev/null +++ b/src/ModuleCore/Directory.Build.props @@ -0,0 +1,6 @@ + + + 0.0.0 + dev + + \ No newline at end of file diff --git a/ModuleCore/ModuleCore.csproj b/src/ModuleCore/ModuleCore.csproj similarity index 100% rename from ModuleCore/ModuleCore.csproj rename to src/ModuleCore/ModuleCore.csproj diff --git a/Harness/Harness.csproj b/src/ModuleHarness/ModuleHarness.csproj similarity index 100% rename from Harness/Harness.csproj rename to src/ModuleHarness/ModuleHarness.csproj diff --git a/Harness/Program.cs b/src/ModuleHarness/Program.cs similarity index 94% rename from Harness/Program.cs rename to src/ModuleHarness/Program.cs index 59e7805..a01a1db 100644 --- a/Harness/Program.cs +++ b/src/ModuleHarness/Program.cs @@ -1,7 +1,7 @@ using System.Text; using ModuleCore.Calendar; -namespace Harness; +namespace ModuleHarness; class Program { diff --git a/PowershellHarness/CustomHost.cs b/src/PowershellHarness/CustomHost.cs similarity index 100% rename from PowershellHarness/CustomHost.cs rename to src/PowershellHarness/CustomHost.cs diff --git a/PowershellHarness/PowershellHarness.csproj b/src/PowershellHarness/PowershellHarness.csproj similarity index 75% rename from PowershellHarness/PowershellHarness.csproj rename to src/PowershellHarness/PowershellHarness.csproj index 250d874..80cdb76 100644 --- a/PowershellHarness/PowershellHarness.csproj +++ b/src/PowershellHarness/PowershellHarness.csproj @@ -8,7 +8,8 @@ - + + diff --git a/PowershellHarness/Program.cs b/src/PowershellHarness/Program.cs similarity index 100% rename from PowershellHarness/Program.cs rename to src/PowershellHarness/Program.cs diff --git a/PowershellModule/Calendar/GetCalendarCommand.cs b/src/PowershellModule/Calendar/GetCalendarCommand.cs similarity index 100% rename from PowershellModule/Calendar/GetCalendarCommand.cs rename to src/PowershellModule/Calendar/GetCalendarCommand.cs diff --git a/src/PowershellModule/Directory.Build.props b/src/PowershellModule/Directory.Build.props new file mode 100644 index 0000000..afa6e9c --- /dev/null +++ b/src/PowershellModule/Directory.Build.props @@ -0,0 +1,6 @@ + + + 0.0.0 + dev + + \ No newline at end of file diff --git a/PowershellModule/PowershellModule.csproj b/src/PowershellModule/PowershellModule.csproj similarity index 72% rename from PowershellModule/PowershellModule.csproj rename to src/PowershellModule/PowershellModule.csproj index d127d89..c7f5801 100644 --- a/PowershellModule/PowershellModule.csproj +++ b/src/PowershellModule/PowershellModule.csproj @@ -8,10 +8,10 @@ - + All - + diff --git a/PowershellModule/manifest.json b/src/PowershellModule/manifest.json similarity index 100% rename from PowershellModule/manifest.json rename to src/PowershellModule/manifest.json diff --git a/PowershellModule/meta/PowershellModule.psd1 b/src/PowershellModule/meta/PowershellModule.psd1 similarity index 100% rename from PowershellModule/meta/PowershellModule.psd1 rename to src/PowershellModule/meta/PowershellModule.psd1 diff --git a/PowershellModule/meta/PowershellModule.psm1 b/src/PowershellModule/meta/PowershellModule.psm1 similarity index 100% rename from PowershellModule/meta/PowershellModule.psm1 rename to src/PowershellModule/meta/PowershellModule.psm1