From 479c433765e8b7a0f74871a1617e63eece4ca0aa Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 4 Sep 2026 09:49:01 +1000 Subject: [PATCH] build(git-provider): Split bundle archive creation into separate task, update default task chain --- build/Tasks/CopyOutputTask.cs | 116 +--------------------- build/Tasks/CreateBundleArchiveTask.cs | 130 +++++++++++++++++++++++++ build/Tasks/DefaultTask.cs | 2 + 3 files changed, 134 insertions(+), 114 deletions(-) create mode 100644 build/Tasks/CreateBundleArchiveTask.cs diff --git a/build/Tasks/CopyOutputTask.cs b/build/Tasks/CopyOutputTask.cs index 8bccce5..f51763a 100644 --- a/build/Tasks/CopyOutputTask.cs +++ b/build/Tasks/CopyOutputTask.cs @@ -1,17 +1,15 @@ using System; using System.Collections.Generic; -using System.IO; -using System.IO.Compression; using System.Linq; using Cake.Core.Diagnostics; using Cake.Core.IO; using Cake.Frosting; using Cake.Powershell; -using Path = System.IO.Path; namespace Build.Tasks; [TaskName("CopyOutput")] +[IsDependeeOf(typeof(CreateBundleArchiveTask))] public class CopyOutputTask : FrostingTask { public override void Run(BuildContext context) @@ -48,9 +46,7 @@ public class CopyOutputTask : FrostingTask psSettings.Arguments.Append("outputDir", ToPowershellSafeString(scriptParams.OutputLocation)); context.StartPowershellFile(context.CreateModuleManifestScript, psSettings); - context.Log.Information($"Module output to: {context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment)}"); - - BundleMinimalFiles(context); + context.Log.Information($"Module files output to: {context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment)}"); base.Run(context); } @@ -71,112 +67,4 @@ public class CopyOutputTask : FrostingTask var commandName = "GitRepoRegistration"; return [.. verbs.Select(x => $"{x}-{commandName}")]; } - - private static void BundleMinimalFiles(BuildContext context) - { - // We're effectively replicating PostBuild.ps1 with all of this as Remove-Item at the end of it does not remove files the same way. - // TODO: See if that's something I can fix - var powershellModuleBuildLocation = context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment).FullPath; - // PowershellModule is here so that the script that adds to the users $Env:PSModulePath correctly resolves when using - // Import-Module "PowershellModule" - // TODO: Generate the script to add to the PSModulePath in the bundleOutputLocation directory - - // Top level bundle output location - var bundleRootLocation = Directory.CreateDirectory(Path.Combine(powershellModuleBuildLocation, "bundle")).FullName; - // Module folder location that will be zipped up and will contain any setup scripts/readme etc - var moduleFolderLocation = Directory.CreateDirectory(Path.Combine(bundleRootLocation, "NulahModule")).FullName; - // Location to put all the module dlls and other files - var powershelModuleOutputLocation = Directory.CreateDirectory(Path.Combine(moduleFolderLocation, "PowershellModule")).FullName; - - context.Log.Information($"Bundle location: {bundleRootLocation}"); - context.Log.Information($"Built file location: {powershellModuleBuildLocation}"); - - // PostBuild.ps1 for PowershellModule should have already been run at this point - it won't remove files, but it will copy the - // correct e_sqlite3.dll that we need. - // TODO: Investigate this later as I'm not entirely happy with this now - // // Get the location for the specific version of e_sqlite3.dll we need - PowerShell binary modules resolve dependent dlls - // // from the executing assembly directory first, and the built output of the module includes a lot of other dlls that - // // are already available with the dotnet runtime so there's no need for us to include them. - // // I mean, we could, but we'd also be terrible software engineers if we couldn't do something as basic as reducing files we need. - // // TODO: Account for other runtimes such as linux based ones where the dotnet runtime might _not_ provide these files for free. - // // It'd require looking into how the module loads for PowerShell on those operating systems, and I might never - // // get around to doing that because I use windows (currently). - // // TODO: Update this later to handle other runtimes, for now we hardcode to win-x64 because it's what I use - // var nativeSqliteDllLocation = System.IO.Path.Combine(powershellModuleBuildLocation, "runtimes", "win-x64", "native", "e_sqlite3.dll"); - // - // if (!File.Exists(nativeSqliteDllLocation)) - // { - // context.Log.Error($"BUNDLE FAILED: Unable to locate ${nativeSqliteDllLocation}"); - // return; - // } - // - // context.Log.Information($"Copying '${nativeSqliteDllLocation}' to '${bundleOutputLocation}'"); - // - // File.Copy(nativeSqliteDllLocation, System.IO.Path.Combine(bundleOutputLocation, System.IO.Path.GetFileName(nativeSqliteDllLocation))); - - var moduleCoreFiles = Directory.GetFiles(powershellModuleBuildLocation, "ModuleCore*", searchOption: SearchOption.TopDirectoryOnly); - var powershellModuleFiles = Directory.GetFiles(powershellModuleBuildLocation, "PowershellModule*", searchOption: SearchOption.TopDirectoryOnly); - var sqliteFiles = Directory.GetFiles(powershellModuleBuildLocation, "*SQLite*", searchOption: SearchOption.TopDirectoryOnly); - - string[] allFiles = [.. moduleCoreFiles, .. powershellModuleFiles, .. sqliteFiles]; - - context.Log.Information($"Copying {allFiles.Length} files to the module folder {powershelModuleOutputLocation}"); - foreach (var file in allFiles) - { - context.Log.Information($"Copying {Path.GetFileName(file)}"); - File.Copy(file, Path.Combine(powershelModuleOutputLocation, Path.GetFileName(file))); - } - - context.Log.Information($"Bundle files copied"); - - GenerateModuleImportScript(moduleFolderLocation, context); - CreateBundleZip(bundleRootLocation, moduleFolderLocation, context); - } - - private static void GenerateModuleImportScript(string bundleLocation, BuildContext context) - { - context.Log.Information($"Generating module import script"); - - using var scriptFile = File.Create(Path.Combine(bundleLocation, "NulahPowershell.ps1")); - using var fileWriter = new StreamWriter(scriptFile); - // TODO: Document the set up function - // TODO: Create a ticket for fleshing out the set up function (or just do that work later) - fileWriter.Write( - """ - # To use, first copy all files to the same location as your $profile under ./NulahModule, then open your powershell profile located at $profile and add the following: - - # Script setup style: Using the setup script to import as needed (this method will also set custom prompts and other alias functions) - # . "$PSScriptRoot/NulahModule/NulahPowershell.ps1" - # SetupNulahPowershell - # Just the module: Use the following to just import just the powershell module - # Import-Module -Name "$PSScriptRoot/NulahModule/PowershellModule" - - function SetupNulahPowershell - { - # only add to our module path once - # The intent for this is that a user will have copied this bundle folder alongside their $profile - # location, eg, they'll have a folder called NulahModule that will contain everything within the bundle zip - $testBundleDirectory = $PSScriptRoot - $bundleInPath = ($Env:PSModulePath -split ';').TrimEnd('\') -contains $testBundleDirectory; - - if ($false -eq $bundleInPath) - { - $env:PSModulePath = @( - $env:PSModulePath - $testBundleDirectory - ) -Join [System.IO.Path]::PathSeparator - } - - Import-Module "PowershellModule" - } - """ - ); - context.Log.Information($"Module import script created"); - } - - private static void CreateBundleZip(string bundleLocation, string moduleFolderLocation, BuildContext context) - { - var bundleZipFileLocaiton = Path.Combine(bundleLocation, "NulahModule.zip"); - ZipFile.CreateFromDirectory(moduleFolderLocation, bundleZipFileLocaiton, CompressionLevel.Fastest, true); - } } \ No newline at end of file diff --git a/build/Tasks/CreateBundleArchiveTask.cs b/build/Tasks/CreateBundleArchiveTask.cs new file mode 100644 index 0000000..54413b1 --- /dev/null +++ b/build/Tasks/CreateBundleArchiveTask.cs @@ -0,0 +1,130 @@ +using System.IO; +using System.IO.Compression; +using Cake.Core.Diagnostics; +using Cake.Frosting; + +namespace Build.Tasks; + +[TaskName("CreateBundleArchive")] +[IsDependentOn(typeof(CopyOutputTask))] +public class CreateBundleArchiveTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.Log.Information($"Creating bundle archive"); + BundleMinimalFiles(context); + base.Run(context); + } + + private static void BundleMinimalFiles(BuildContext context) + { + // We're effectively replicating PostBuild.ps1 with all of this as Remove-Item at the end of it does not remove files the same way. + // TODO: See if that's something I can fix + var powershellModuleBuildLocation = context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment).FullPath; + // PowershellModule is here so that the script that adds to the users $Env:PSModulePath correctly resolves when using + // Import-Module "PowershellModule" + // TODO: Generate the script to add to the PSModulePath in the bundleOutputLocation directory + + // Top level bundle output location + var bundleRootLocation = Directory.CreateDirectory(Path.Combine(powershellModuleBuildLocation, "bundle")).FullName; + // Module folder location that will be zipped up and will contain any setup scripts/readme etc + var moduleFolderLocation = Directory.CreateDirectory(Path.Combine(bundleRootLocation, "NulahModule")).FullName; + // Location to put all the module dlls and other files + var powershelModuleOutputLocation = Directory.CreateDirectory(Path.Combine(moduleFolderLocation, "PowershellModule")).FullName; + + context.Log.Information($"Bundle location: {bundleRootLocation}"); + context.Log.Information($"Built file location: {powershellModuleBuildLocation}"); + + // PostBuild.ps1 for PowershellModule should have already been run at this point - it won't remove files, but it will copy the + // correct e_sqlite3.dll that we need. + // This code is all commented out because I'm not entirely satisfied with PostBuild.ps1 doing some things but + // this build doing slightly different. + // // Get the location for the specific version of e_sqlite3.dll we need - PowerShell binary modules resolve dependent dlls + // // from the executing assembly directory first, and the built output of the module includes a lot of other dlls that + // // are already available with the dotnet runtime so there's no need for us to include them. + // // I mean, we could, but we'd also be terrible software engineers if we couldn't do something as basic as reducing files we need. + // // TODO: Account for other runtimes such as linux based ones where the dotnet runtime might _not_ provide these files for free. + // // It'd require looking into how the module loads for PowerShell on those operating systems, and I might never + // // get around to doing that because I use windows (currently). + // // TODO: Update this later to handle other runtimes, for now we hardcode to win-x64 because it's what I use + // var nativeSqliteDllLocation = System.IO.Path.Combine(powershellModuleBuildLocation, "runtimes", "win-x64", "native", "e_sqlite3.dll"); + // + // if (!File.Exists(nativeSqliteDllLocation)) + // { + // context.Log.Error($"BUNDLE FAILED: Unable to locate ${nativeSqliteDllLocation}"); + // return; + // } + // + // context.Log.Information($"Copying '${nativeSqliteDllLocation}' to '${bundleOutputLocation}'"); + // + // File.Copy(nativeSqliteDllLocation, System.IO.Path.Combine(bundleOutputLocation, System.IO.Path.GetFileName(nativeSqliteDllLocation))); + + var moduleCoreFiles = Directory.GetFiles(powershellModuleBuildLocation, "ModuleCore*", searchOption: SearchOption.TopDirectoryOnly); + var powershellModuleFiles = Directory.GetFiles(powershellModuleBuildLocation, "PowershellModule*", searchOption: SearchOption.TopDirectoryOnly); + var sqliteFiles = Directory.GetFiles(powershellModuleBuildLocation, "*SQLite*", searchOption: SearchOption.TopDirectoryOnly); + + string[] allFiles = [.. moduleCoreFiles, .. powershellModuleFiles, .. sqliteFiles]; + + context.Log.Information($"Copying {allFiles.Length} files to the module folder {powershelModuleOutputLocation}"); + foreach (var file in allFiles) + { + context.Log.Information($"Copying {Path.GetFileName(file)}"); + File.Copy(file, Path.Combine(powershelModuleOutputLocation, Path.GetFileName(file))); + } + + context.Log.Information($"Bundle files copied"); + + GenerateModuleImportScript(moduleFolderLocation, context); + CreateBundleZip(bundleRootLocation, moduleFolderLocation, context); + } + + private static void GenerateModuleImportScript(string bundleLocation, BuildContext context) + { + context.Log.Information($"Generating module import script"); + var scriptFileName = Path.Combine(bundleLocation, "NulahPowershell.ps1"); + + using var scriptFile = File.Create(scriptFileName); + using var fileWriter = new StreamWriter(scriptFile); + // TODO: Document the set up function + // TODO: Create a ticket for fleshing out the set up function (or just do that work later) + fileWriter.Write( + """ + # To use, first copy all files to the same location as your $profile under ./NulahModule, then open your powershell profile located at $profile and add the following: + + # Script setup style: Using the setup script to import as needed (this method will also set custom prompts and other alias functions) + # . "$PSScriptRoot/NulahModule/NulahPowershell.ps1" + # SetupNulahPowershell + # Just the module: Use the following to just import just the powershell module + # Import-Module -Name "$PSScriptRoot/NulahModule/PowershellModule" + + function SetupNulahPowershell + { + # only add to our module path once + # The intent for this is that a user will have copied this bundle folder alongside their $profile + # location, eg, they'll have a folder called NulahModule that will contain everything within the bundle zip + $testBundleDirectory = $PSScriptRoot + $bundleInPath = ($Env:PSModulePath -split ';').TrimEnd('\') -contains $testBundleDirectory; + + if ($false -eq $bundleInPath) + { + $env:PSModulePath = @( + $env:PSModulePath + $testBundleDirectory + ) -Join [System.IO.Path]::PathSeparator + } + + Import-Module "PowershellModule" + } + """ + ); + context.Log.Information($"Module import script created at '{scriptFileName}'"); + } + + private static void CreateBundleZip(string bundleLocation, string moduleFolderLocation, BuildContext context) + { + var bundleZipFileLocaiton = Path.Combine(bundleLocation, "NulahModule.zip"); + context.Log.Information($"Bundling module into archive '{bundleZipFileLocaiton}'"); + ZipFile.CreateFromDirectory(moduleFolderLocation, bundleZipFileLocaiton, CompressionLevel.Fastest, true); + context.Log.Information($"Archive created at '{bundleZipFileLocaiton}'"); + } +} \ No newline at end of file diff --git a/build/Tasks/DefaultTask.cs b/build/Tasks/DefaultTask.cs index 200768c..7f4bf67 100644 --- a/build/Tasks/DefaultTask.cs +++ b/build/Tasks/DefaultTask.cs @@ -6,7 +6,9 @@ namespace Build.Tasks; // Consider this the "entry" point for builds, task order is defined by a chain of IsDependentOn [TaskName("Default")] +[IsDependentOn(typeof(BuildTask))] [IsDependentOn(typeof(CopyOutputTask))] +[IsDependentOn(typeof(CreateBundleArchiveTask))] public class DefaultTask : FrostingTask { public override void Run(ICakeContext context)