From c6d17d3d8526fae3d5175d358a159a1ca4e10748 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 11:13:10 +0200 Subject: [PATCH 01/32] Improve gitignore --- .gitignore | 387 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f51ed7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,387 @@ +Ignore/* +.vs/* +.vscode/* +Examples/Output/* +Releases/* +Artefacts/* +ReleasedUnpacked/* +Sources/.vs +Sources/*/.vs +Sources/*/obj +Sources/*/bin +Sources/*/*/obj +Sources/*/*/bin +Sources/packages/* +Sources/*.DotSettings.user +Sources/*.DotSettings +Lib/Default/* +Lib/Standard/* +Lib/Core/* + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +.vscode/* + +/.idea/ From 0a6ddc22b6d2b32686e1415db6498088e40b4b10 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 11:14:10 +0200 Subject: [PATCH 02/32] Add dummy solution to get up to date dlls --- Sources/PSPublishModule/PSPublishModule.sln | 25 +++++++++ .../PSPublishModule/OnImportAndRemove.cs | 54 +++++++++++++++++++ .../PSPublishModule/PSPublishModule.csproj | 33 ++++++++++++ .../PSPublishModule/PublishModule.cs | 8 +++ 4 files changed, 120 insertions(+) create mode 100644 Sources/PSPublishModule/PSPublishModule.sln create mode 100644 Sources/PSPublishModule/PSPublishModule/OnImportAndRemove.cs create mode 100644 Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj create mode 100644 Sources/PSPublishModule/PSPublishModule/PublishModule.cs diff --git a/Sources/PSPublishModule/PSPublishModule.sln b/Sources/PSPublishModule/PSPublishModule.sln new file mode 100644 index 0000000..90ccb10 --- /dev/null +++ b/Sources/PSPublishModule/PSPublishModule.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35027.167 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSPublishModule", "PSPublishModule\PSPublishModule.csproj", "{319706C7-84F2-48FE-98A8-F89CABAD50FA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {319706C7-84F2-48FE-98A8-F89CABAD50FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {319706C7-84F2-48FE-98A8-F89CABAD50FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {319706C7-84F2-48FE-98A8-F89CABAD50FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {319706C7-84F2-48FE-98A8-F89CABAD50FA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FBCF2ABB-4092-4C5F-98E4-8E4B05BB6029} + EndGlobalSection +EndGlobal diff --git a/Sources/PSPublishModule/PSPublishModule/OnImportAndRemove.cs b/Sources/PSPublishModule/PSPublishModule/OnImportAndRemove.cs new file mode 100644 index 0000000..2d0de44 --- /dev/null +++ b/Sources/PSPublishModule/PSPublishModule/OnImportAndRemove.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using System.Management.Automation; +using System.Reflection; + +public class OnModuleImportAndRemove : IModuleAssemblyInitializer, IModuleAssemblyCleanup { + public void OnImport() { + if (IsNetFramework()) { + AppDomain.CurrentDomain.AssemblyResolve += MyResolveEventHandler; + } + } + + public void OnRemove(PSModuleInfo module) { + if (IsNetFramework()) { + AppDomain.CurrentDomain.AssemblyResolve -= MyResolveEventHandler; + } + } + + private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args) { + //This code is used to resolve the assemblies + //Console.WriteLine($"Resolving {args.Name}"); + var directoryPath = Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location); + var filesInDirectory = Directory.GetFiles(directoryPath); + + foreach (var file in filesInDirectory) { + var fileName = Path.GetFileName(file); + var assemblyName = Path.GetFileNameWithoutExtension(file); + + if (args.Name.StartsWith(assemblyName)) { + //Console.WriteLine($"Loading {args.Name} assembly {fileName}"); + return Assembly.LoadFile(file); + } + } + return null; + } + + private bool IsNetFramework() { + // Get the version of the CLR + Version clrVersion = System.Environment.Version; + // Check if the CLR version is 4.x.x.x + return clrVersion.Major == 4; + } + + private bool IsNetCore() { + return System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); + } + + private bool IsNet5OrHigher() { + return System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET 5", StringComparison.OrdinalIgnoreCase) || + System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET 6", StringComparison.OrdinalIgnoreCase) || + System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET 7", StringComparison.OrdinalIgnoreCase) || + System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET 8", StringComparison.OrdinalIgnoreCase); + } +} \ No newline at end of file diff --git a/Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj b/Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj new file mode 100644 index 0000000..f8bf986 --- /dev/null +++ b/Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj @@ -0,0 +1,33 @@ + + + + net472;netstandard2.0;net6.0;net7.0;net8.0 + PSPublishModule + PSPublishModule + PSPublishModule + 0.1.0 + false + Evotec + Przemyslaw Klys + latest + true + + + + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sources/PSPublishModule/PSPublishModule/PublishModule.cs b/Sources/PSPublishModule/PSPublishModule/PublishModule.cs new file mode 100644 index 0000000..0474c69 --- /dev/null +++ b/Sources/PSPublishModule/PSPublishModule/PublishModule.cs @@ -0,0 +1,8 @@ +namespace PSPublishModule; + +/// +/// Dummy class to make the module visible to the module manager. +/// +public class PublishModule { + +} From 776e40dfaca5d5512e1ff6139afd407f3c83fd94 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 11:14:15 +0200 Subject: [PATCH 03/32] Bump version --- PSPublishModule.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PSPublishModule.psd1 b/PSPublishModule.psd1 index 5b6e331..24ea665 100644 --- a/PSPublishModule.psd1 +++ b/PSPublishModule.psd1 @@ -9,7 +9,7 @@ DotNetFrameworkVersion = '4.5.2' FunctionsToExport = @('Convert-CommandsToList', 'Get-MissingFunctions', 'Initialize-PortableModule', 'Initialize-PortableScript', 'Initialize-ProjectManager', 'Invoke-ModuleBuild', 'New-ConfigurationArtefact', 'New-ConfigurationBuild', 'New-ConfigurationCommand', 'New-ConfigurationDocumentation', 'New-ConfigurationExecute', 'New-ConfigurationFormat', 'New-ConfigurationImportModule', 'New-ConfigurationInformation', 'New-ConfigurationManifest', 'New-ConfigurationModule', 'New-ConfigurationModuleSkip', 'New-ConfigurationPublish', 'New-ConfigurationTest', 'Register-Certificate', 'Remove-Comments', 'Send-GitHubRelease', 'Test-BasicModule', 'Test-ScriptFile', 'Test-ScriptModule') GUID = 'eb76426a-1992-40a5-82cd-6480f883ef4d' - ModuleVersion = '2.0.13' + ModuleVersion = '2.0.14' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ From 2837d8293e01a0b708f7b87934117db7c9e24f67 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 11:16:22 +0200 Subject: [PATCH 04/32] Update module builders --- Build/Build-Module.ps1 | 15 ++++++++++++++- Build/Build-ModuleSimplified.ps1 | 17 +++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Build/Build-Module.ps1 b/Build/Build-Module.ps1 index 37adfa6..6288c3c 100644 --- a/Build/Build-Module.ps1 +++ b/Build/Build-Module.ps1 @@ -1,7 +1,9 @@ -# please notice I may be using PSM1 here, as the module may not be built or PSD1 may be broken +# please notice I may be using PSM1 here (not always), as the module may not be built or PSD1 may be broken # since PSD1 is not required for proper rebuilding, we use PSM1 for this module only # most modules should be run via PSD1 or by it's name (which in the background uses PD1) +# This version is used for GitHub Actions and is used to build the module + Import-Module "$PSScriptRoot\..\PSPublishModule.psd1" -Force Build-Module -ModuleName 'PSPublishModule' { @@ -108,6 +110,17 @@ Build-Module -ModuleName 'PSPublishModule' { #CertificatePFXBase64 = $BasePfx #CertificatePFXPassword = "zGT" DoNotAttemptToFixRelativePaths = $false + + # required for Cmdlet/Alias functionality + NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" + ResolveBinaryConflicts = $true + ResolveBinaryConflictsName = 'PSPublishModule' + NETProjectName = 'PSPublishModule' + NETConfiguration = 'Release' + NETFramework = 'net6.0', 'net472' + NETHandleAssemblyWithSameName = $true + DotSourceLibraries = $true + DotSourceClasses = $true } New-ConfigurationBuild @newConfigurationBuildSplat diff --git a/Build/Build-ModuleSimplified.ps1 b/Build/Build-ModuleSimplified.ps1 index 8c5e9e7..6bbe6b9 100644 --- a/Build/Build-ModuleSimplified.ps1 +++ b/Build/Build-ModuleSimplified.ps1 @@ -1,9 +1,11 @@ Clear-Host -# please notice I may be using PSM1 here, as the module may not be built or PSD1 may be broken +# please notice I may be using PSM1 here (not always), as the module may not be built or PSD1 may be broken # since PSD1 is not required for proper rebuilding, we use PSM1 for this module only # most modules should be run via PSD1 or by it's name (which in the background uses PD1) +# This version is for local building + Import-Module "$PSScriptRoot\..\PSPublishModule.psd1" -Force Build-Module -ModuleName 'PSPublishModule' { @@ -109,6 +111,17 @@ Build-Module -ModuleName 'PSPublishModule' { #CertificatePFXBase64 = $BasePfx #CertificatePFXPassword = "zGT" DoNotAttemptToFixRelativePaths = $false + + # required for Cmdlet/Alias functionality + NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" + ResolveBinaryConflicts = $true + ResolveBinaryConflictsName = 'PSPublishModule' + NETProjectName = 'PSPublishModule' + NETConfiguration = 'Release' + NETFramework = 'net6.0', 'net472' + NETHandleAssemblyWithSameName = $true + DotSourceLibraries = $true + DotSourceClasses = $true } New-ConfigurationBuild @newConfigurationBuildSplat @@ -134,7 +147,7 @@ Build-Module -ModuleName 'PSPublishModule' { ### FOR TESTING PURPOSES ONLY ### - ### SHOWING HHOW THINGS WORK HERE ### + ### SHOWING HOW THINGS WORK HERE ### #New-ConfigurationArtefact -Type Packed -Enable -Path "$PSScriptRoot\..\Artefacts\Packed2" -IncludeTagName -ID 'Packed2' #New-ConfigurationArtefact -Type Packed -Enable -Path "$PSScriptRoot\..\Artefacts\Packed1" -IncludeTagName From d38831082fe6050665c311ae2a810925729ff7ba Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 19:11:58 +0200 Subject: [PATCH 05/32] Update module builder --- Build/Build-Module.ps1 | 37 +++++++++++++++++++------------- Build/Build-ModuleSimplified.ps1 | 36 ++++++++++++++++++------------- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/Build/Build-Module.ps1 b/Build/Build-Module.ps1 index 6288c3c..372a2c8 100644 --- a/Build/Build-Module.ps1 +++ b/Build/Build-Module.ps1 @@ -4,6 +4,10 @@ # This version is used for GitHub Actions and is used to build the module +# We need to rmeove library before we start, as it may contain old files, which will be in use once PSD1 loads +# This is only required for PSPublisModule, as it's the only module that is being built by itself +Remove-Item -Path "C:\Support\GitHub\PSPublishModule\Lib" -Recurse -Force -ErrorAction Stop + Import-Module "$PSScriptRoot\..\PSPublishModule.psd1" -Force Build-Module -ModuleName 'PSPublishModule' { @@ -101,26 +105,29 @@ Build-Module -ModuleName 'PSPublishModule' { New-ConfigurationImportModule -ImportSelf -ImportRequiredModules $newConfigurationBuildSplat = @{ - Enable = $true + Enable = $true # temporary not signing - SignModule = $false - DeleteTargetModuleBeforeBuild = $true - MergeModuleOnBuild = $true - CertificateThumbprint = '' + SignModule = $false + DeleteTargetModuleBeforeBuild = $true + MergeModuleOnBuild = $true + CertificateThumbprint = '' #CertificatePFXBase64 = $BasePfx #CertificatePFXPassword = "zGT" - DoNotAttemptToFixRelativePaths = $false + DoNotAttemptToFixRelativePaths = $false # required for Cmdlet/Alias functionality - NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" - ResolveBinaryConflicts = $true - ResolveBinaryConflictsName = 'PSPublishModule' - NETProjectName = 'PSPublishModule' - NETConfiguration = 'Release' - NETFramework = 'net6.0', 'net472' - NETHandleAssemblyWithSameName = $true - DotSourceLibraries = $true - DotSourceClasses = $true + NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" + ResolveBinaryConflicts = $true + ResolveBinaryConflictsName = 'PSPublishModule' + NETProjectName = 'PSPublishModule' + NETConfiguration = 'Release' + NETFramework = 'net6.0', 'net472' + NETHandleAssemblyWithSameName = $true + DotSourceLibraries = $true + DotSourceClasses = $true + + # This has to be disabled as it will not have DLLs required to do this + NETBinaryModuleCmdletScanDisabled = $true } New-ConfigurationBuild @newConfigurationBuildSplat diff --git a/Build/Build-ModuleSimplified.ps1 b/Build/Build-ModuleSimplified.ps1 index 6bbe6b9..92ef4fe 100644 --- a/Build/Build-ModuleSimplified.ps1 +++ b/Build/Build-ModuleSimplified.ps1 @@ -5,6 +5,9 @@ # most modules should be run via PSD1 or by it's name (which in the background uses PD1) # This version is for local building +# We need to rmeove library before we start, as it may contain old files, which will be in use once PSD1 loads +# This is only required for PSPublisModule, as it's the only module that is being built by itself +Remove-Item -Path "C:\Support\GitHub\PSPublishModule\Lib" -Recurse -Force -ErrorAction Stop Import-Module "$PSScriptRoot\..\PSPublishModule.psd1" -Force @@ -103,25 +106,28 @@ Build-Module -ModuleName 'PSPublishModule' { New-ConfigurationImportModule -ImportSelf #-ImportRequiredModules $newConfigurationBuildSplat = @{ - Enable = $true - SignModule = $true - DeleteTargetModuleBeforeBuild = $true - MergeModuleOnBuild = $true - CertificateThumbprint = '483292C9E317AA13B07BB7A96AE9D1A5ED9E7703' + Enable = $true + SignModule = $true + DeleteTargetModuleBeforeBuild = $true + MergeModuleOnBuild = $true + CertificateThumbprint = '483292C9E317AA13B07BB7A96AE9D1A5ED9E7703' #CertificatePFXBase64 = $BasePfx #CertificatePFXPassword = "zGT" - DoNotAttemptToFixRelativePaths = $false + DoNotAttemptToFixRelativePaths = $false # required for Cmdlet/Alias functionality - NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" - ResolveBinaryConflicts = $true - ResolveBinaryConflictsName = 'PSPublishModule' - NETProjectName = 'PSPublishModule' - NETConfiguration = 'Release' - NETFramework = 'net6.0', 'net472' - NETHandleAssemblyWithSameName = $true - DotSourceLibraries = $true - DotSourceClasses = $true + NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" + ResolveBinaryConflicts = $true + ResolveBinaryConflictsName = 'PSPublishModule' + NETProjectName = 'PSPublishModule' + NETConfiguration = 'Release' + NETFramework = 'net6.0', 'net472' + NETHandleAssemblyWithSameName = $true + DotSourceLibraries = $true + DotSourceClasses = $true + + # This has to be disabled as it will not have DLLs required to do this + NETBinaryModuleCmdletScanDisabled = $true } New-ConfigurationBuild @newConfigurationBuildSplat From ad59d37bc8712a02e7069cb15ed9287563771b47 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 19:45:37 +0200 Subject: [PATCH 06/32] Improve write-text with preappend and spaces --- Private/Write-Text.ps1 | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/Private/Write-Text.ps1 b/Private/Write-Text.ps1 index 5959a54..13bbfd2 100644 --- a/Private/Write-Text.ps1 +++ b/Private/Write-Text.ps1 @@ -1,13 +1,39 @@ function Write-Text { [CmdletBinding()] param( - [string] $Text, + [Parameter(Position = 0)][string] $Text, [System.ConsoleColor] $Color = [System.ConsoleColor]::Cyan, [System.ConsoleColor] $ColorTime = [System.ConsoleColor]::Green, [switch] $Start, [switch] $End, - [System.Diagnostics.Stopwatch] $Time + [System.Diagnostics.Stopwatch] $Time, + [ValidateSet('Plus', 'Minus', 'Information', 'Addition')][string] $PreAppend, + [string] $SpacesBefore ) + if ($PreAppend) { + if ($PreAppend -eq "Information") { + $TextBefore = "$SpacesBefore[i] " + if (-not $ColorBefore) { + $ColorBefore = [System.ConsoleColor]::Yellow + } + } elseif ($PreAppend -eq 'Minus') { + $TextBefore = "$SpacesBefore[-] " + if (-not $ColorBefore) { + $ColorBefore = [System.ConsoleColor]::Red + } + } elseif ($PreAppend -eq 'Plus') { + $TextBefore = "$SpacesBefore[+] " + if (-not $ColorBefore) { + $ColorBefore = [System.ConsoleColor]::Cyan + } + } elseif ($PreAppend -eq 'Addition') { + $TextBefore = "$SpacesBefore[>] " + if (-not $ColorBefore) { + $ColorBefore = [System.ConsoleColor]::Yellow + } + } + Write-Host -Object "$TextBefore" -NoNewline -ForegroundColor $ColorBefore + } if (-not $Start -and -not $End) { Write-Host "$Text" -ForegroundColor $Color } From 18bc85ec25c2dcc465ce13a5c505f693dfa246d0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 19:45:54 +0200 Subject: [PATCH 07/32] Add ability to disable module scanning for cmdlets --- Public/New-ConfigurationBuild.ps1 | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Public/New-ConfigurationBuild.ps1 b/Public/New-ConfigurationBuild.ps1 index 0c94bb9..4039780 100644 --- a/Public/New-ConfigurationBuild.ps1 +++ b/Public/New-ConfigurationBuild.ps1 @@ -99,6 +99,10 @@ In here you provide one or more binrary module names that you want to import in the module. Just the DLL name with extension without path. Path is assumed to be $PSScriptRoot\Lib\Standard or $PSScriptRoot\Lib\Default or $PSScriptRoot\Lib\Core + .PARAMETER NETBinaryModuleCmdletScanDisabled + This is to disable scanning for cmdlets in binary modules, this is useful if you have a lot of binary modules and you don't want to scan them for cmdlets. + By default it will scan for cmdlets/aliases in binary modules and add them to the module PSD1/PSM1 files. + .PARAMETER NETHandleAssemblyWithSameName Adds try/catch block to handle assembly with same name is already loaded exception and ignore it. It's useful in PowerShell 7, as it's more strict about this than Windows PowerShell, and usually everything should work as expected. @@ -163,7 +167,8 @@ [string[]] $NETIgnoreLibraryOnLoad, [string[]] $NETBinaryModule, [alias('HandleAssemblyWithSameName')][switch] $NETHandleAssemblyWithSameName, - [switch] $NETLineByLineAddType + [switch] $NETLineByLineAddType, + [switch] $NETBinaryModuleCmdletScanDisabled ) if ($PSBoundParameters.ContainsKey('Enable')) { @@ -441,4 +446,13 @@ } } } + + if ($PSBoundParameters.ContainsKey('NETBinaryModuleCmdletScanDisabled')) { + [ordered] @{ + Type = 'BuildLibraries' + BuildLibraries = [ordered] @{ + BinaryModuleCmdletScanDisabled = $NETBinaryModuleCmdletScanDisabled.IsPresent + } + } + } } \ No newline at end of file From 40d2dec5e2f7ffcbb615974d9da459e7c1251874 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 19:46:52 +0200 Subject: [PATCH 08/32] Add function made by @jborean93 to detect cmdlets/aliases in binary files --- Private/Get-PowerShellAssemblyMetaData.ps1 | 71 ++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 Private/Get-PowerShellAssemblyMetaData.ps1 diff --git a/Private/Get-PowerShellAssemblyMetaData.ps1 b/Private/Get-PowerShellAssemblyMetaData.ps1 new file mode 100644 index 0000000..71a980c --- /dev/null +++ b/Private/Get-PowerShellAssemblyMetaData.ps1 @@ -0,0 +1,71 @@ +Function Get-PowerShellAssemblyMetadata { + <# + .SYNOPSIS + Gets the cmdlets and aliases in a dotnet assembly. + + .PARAMETER Path + The assembly to inspect. + + .EXAMPLE + Get-PowerShellAssemblyMetadata -Path MyModule.dll + + .NOTES + This requires the System.Reflection.MetadataLoadContext assembly to be + loaded through Add-Type. WinPS (5.1) will also need to load its deps + System.Memory + System.Collections.Immutable + System.Reflection.Metadata + System.Runtime.CompilerServices.Unsafe + + https://www.nuget.org/packages/System.Reflection.MetadataLoadContext + + Copyright: (c) 2024, Jordan Borean (@jborean93) + MIT License (see LICENSE or https://opensource.org/licenses/MIT) + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory)][string] $Path + ) + try { + $resolver = [System.Reflection.PathAssemblyResolver]::new( + [string[]]@( + (Get-ChildItem -Path ([System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()) -Filter "*.dll").FullName + $Path + )) + } catch { + Write-Text -Text "[-] Can't create PathAssemblyResolver. Please enable 'NETBinaryModuleCmdletScanDisabled' option when building PSPublishModule or investigate why library doesn't load for different modules. Error: $($_.Exception.Message)" -Color Red + return $false + } + $context = [System.Reflection.MetadataLoadContext]::new($resolver) + try { + $smaAssembly = $context.LoadFromAssemblyPath([PSObject].Assembly.Location) + $cmdletType = $smaAssembly.GetType('System.Management.Automation.Cmdlet') + $cmdletAttribute = $smaAssembly.GetType('System.Management.Automation.CmdletAttribute') + $aliasAttribute = $smaAssembly.GetType('System.Management.Automation.AliasAttribute') + + $assembly = $context.LoadFromAssemblyPath($Path) + + $cmdletsToExport = [System.Collections.Generic.List[string]]::new() + $aliasesToExport = [System.Collections.Generic.List[string]]::new() + $assembly.GetTypes() | Where-Object { + $_.IsSubclassOf($cmdletType) + } | ForEach-Object -Process { + $cmdletInfo = $_.CustomAttributes | Where-Object { $_.AttributeType -eq $cmdletAttribute } + if (-not $cmdletInfo) { return } + + $name = "$($cmdletInfo.ConstructorArguments[0].Value)-$($cmdletInfo.ConstructorArguments[1].Value)" + $cmdletsToExport.Add($name) + + $aliases = $_.CustomAttributes | Where-Object { $_.AttributeType -eq $aliasAttribute } + if (-not $aliases -or -not $aliases.ConstructorArguments.Value) { return } + $aliasesToExport.AddRange([string[]]@($aliases.ConstructorArguments.Value.Value)) + } + + [PSCustomObject]@{ + CmdletsToExport = $cmdletsToExport + AliasesToExport = $aliasesToExport + } + } finally { + $context.Dispose() + } +} \ No newline at end of file From 4fd21e44342942e6aef13ea420fdc8fe1cedac99 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 19:47:15 +0200 Subject: [PATCH 09/32] Move function to sepate file for easier code --- Private/Start-ModuleMerging.ps1 | 185 ++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 Private/Start-ModuleMerging.ps1 diff --git a/Private/Start-ModuleMerging.ps1 b/Private/Start-ModuleMerging.ps1 new file mode 100644 index 0000000..52b33a3 --- /dev/null +++ b/Private/Start-ModuleMerging.ps1 @@ -0,0 +1,185 @@ +function Start-ModuleMerging { + [CmdletBinding()] + param( + [System.Collections.IDictionary] $Configuration, + [string] $ProjectName, + [string] $FullTemporaryPath, + [string] $FullModuleTemporaryPath, + [string] $FullProjectPath, + [Array] $LinkDirectories, + [Array] $LinkFilesRoot, + [Array] $LinkPrivatePublicFiles, + [string[]] $DirectoriesWithPS1, + [string[]] $DirectoriesWithClasses, + [System.Collections.IDictionary] $AliasesAndFunctions + ) + if ($Configuration.Steps.BuildModule.Merge) { + foreach ($Directory in $LinkDirectories) { + $Dir = [System.IO.Path]::Combine($FullTemporaryPath, "$Directory") + Add-Directory -Directory $Dir + } + # Workaround to link files that are not ps1/psd1 + [Array] $CompareWorkaround = foreach ($Directory in $DirectoriesWithPS1) { + if ($null -eq $IsWindows -or $IsWindows -eq $true) { + $Dir = -join ($Directory, "\") + } else { + $Dir = -join ($Directory, "/") + } + } + + $LinkDirectoriesWithSupportFiles = $LinkDirectories | Where-Object { $_ -notin $CompareWorkaround } + foreach ($Directory in $LinkDirectoriesWithSupportFiles) { + $Dir = [System.IO.Path]::Combine($FullModuleTemporaryPath, "$Directory") + Add-Directory -Directory $Dir + } + + $LinkingFilesTime = Write-Text "[+] Linking files from root and sub directories" -Start + Copy-InternalFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath + Copy-InternalFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath + Write-Text -End -Time $LinkingFilesTime + + # Workaround to link files that are not ps1/psd1 + $FilesToLink = $LinkPrivatePublicFiles | Where-Object { $_ -notlike '*.ps1' -and $_ -notlike '*.psd1' } + Copy-InternalFiles -LinkFiles $FilesToLink -FullModulePath $FullModuleTemporaryPath -FullProjectPath $FullProjectPath + + if ($Configuration.Information.LibrariesStandard) { + # User provided option, we don't care + } elseif ($Configuration.Information.LibrariesCore -and $Configuration.Information.LibrariesDefault) { + # User provided option for core and default we don't care + } else { + # user hasn't provided any option, we set it to default + $Configuration.Information.LibrariesStandard = [System.IO.Path]::Combine("Lib", "Standard") + $Configuration.Information.LibrariesCore = [System.IO.Path]::Combine("Lib", "Core") + $Configuration.Information.LibrariesDefault = [System.IO.Path]::Combine("Lib", "Default") + } + + if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesCore)) { + if ($null -eq $IsWindows -or $IsWindows -eq $true) { + $StartsWithCore = -join ($Configuration.Information.LibrariesCore, "\") + } else { + $StartsWithCore = -join ($Configuration.Information.LibrariesCore, "/") + } + } + if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesDefault)) { + if ($null -eq $IsWindows -or $IsWindows -eq $true) { + $StartsWithDefault = -join ($Configuration.Information.LibrariesDefault, "\") + } else { + $StartsWithDefault = -join ($Configuration.Information.LibrariesDefault, "/") + } + } + if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesStandard)) { + if ($null -eq $IsWindows -or $IsWindows -eq $true) { + $StartsWithStandard = -join ($Configuration.Information.LibrariesStandard, "\") + } else { + $StartsWithStandard = -join ($Configuration.Information.LibrariesStandard, "/") + } + } + + $CoreFiles = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithCore) } + $DefaultFiles = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithDefault) } + $StandardFiles = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithStandard) } + + $Default = $false + $Core = $false + $Standard = $false + if ($CoreFiles.Count -gt 0) { + $Core = $true + } + if ($DefaultFiles.Count -gt 0) { + $Default = $true + } + if ($StandardFiles.Count -gt 0) { + $Standard = $true + } + if ($Standard -and $Core -and $Default) { + $FrameworkNet = 'Default' + $Framework = 'Standard' + } elseif ($Standard -and $Core) { + $Framework = 'Standard' + $FrameworkNet = 'Standard' + } elseif ($Core -and $Default) { + $Framework = 'Core' + $FrameworkNet = 'Default' + } elseif ($Standard -and $Default) { + $Framework = 'Standard' + $FrameworkNet = 'Default' + } elseif ($Standard) { + $Framework = 'Standard' + $FrameworkNet = 'Standard' + } elseif ($Core) { + $Framework = 'Core' + $FrameworkNet = '' + } elseif ($Default) { + $Framework = '' + $FrameworkNet = 'Default' + } + + if ($Framework -eq 'Core') { + $FilesLibrariesCore = $CoreFiles + } elseif ($Framework -eq 'Standard') { + $FilesLibrariesCore = $StandardFiles + } + if ($FrameworkNet -eq 'Default') { + $FilesLibrariesDefault = $DefaultFiles + } elseif ($FrameworkNet -eq 'Standard') { + $FilesLibrariesDefault = $StandardFiles + } + if ($FrameworkNet -eq 'Standard' -and $Framework -eq 'Standard') { + $FilesLibrariesStandard = $FilesLibrariesCore + } + $Success = Merge-Module -ModuleName $ProjectName ` + -ModulePathSource $FullTemporaryPath ` + -ModulePathTarget $FullModuleTemporaryPath ` + -Sort $Configuration.Options.Merge.Sort ` + -FunctionsToExport $Configuration.Information.Manifest.FunctionsToExport ` + -AliasesToExport $Configuration.Information.Manifest.AliasesToExport ` + -AliasesAndFunctions $AliasesAndFunctions ` + -LibrariesStandard $FilesLibrariesStandard ` + -LibrariesCore $FilesLibrariesCore ` + -LibrariesDefault $FilesLibrariesDefault ` + -FormatCodePSM1 $Configuration.Options.Merge.FormatCodePSM1 ` + -FormatCodePSD1 $Configuration.Options.Merge.FormatCodePSD1 ` + -Configuration $Configuration -DirectoriesWithPS1 $DirectoriesWithPS1 ` + -ClassesPS1 $DirectoriesWithClasses -IncludeAsArray $Configuration.Information.IncludeAsArray + + if ($Success -eq $false) { + return $false + } + + if ($Configuration.Steps.BuildModule.CreateFileCatalog) { + # Something is wrong here for folders other than root, need investigation + $TimeToExecuteSign = [System.Diagnostics.Stopwatch]::StartNew() + Write-Text "[+] Creating file catalog" -Color Blue + $TimeToExecuteSign = [System.Diagnostics.Stopwatch]::StartNew() + $CategoryPaths = @( + $FullModuleTemporaryPath + $NotEmptyPaths = (Get-ChildItem -Directory -Path $FullModuleTemporaryPath -Recurse).FullName + if ($NotEmptyPaths) { + $NotEmptyPaths + } + ) + foreach ($CatPath in $CategoryPaths) { + $CatalogFile = [io.path]::Combine($CatPath, "$ProjectName.cat") + $FileCreated = New-FileCatalog -Path $CatPath -CatalogFilePath $CatalogFile -CatalogVersion 2.0 + if ($FileCreated) { + Write-Text " [>] Catalog file covering $CatPath was created $($FileCreated.Name)" -Color Yellow + } + } + $TimeToExecuteSign.Stop() + Write-Text "[+] Creating file catalog [Time: $($($TimeToExecuteSign.Elapsed).Tostring())]" -Color Blue + } + $SuccessFullSigning = Start-ModuleSigning -Configuration $Configuration -FullModuleTemporaryPath $FullModuleTemporaryPath + if ($SuccessFullSigning -eq $false) { + return $false + } + } else { + foreach ($Directory in $LinkDirectories) { + $Dir = [System.IO.Path]::Combine($FullModuleTemporaryPath, "$Directory") + Add-Directory -Directory $Dir + } + $LinkingFilesTime = Write-Text "[+] Linking files from root and sub directories" -Start + Copy-InternalFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullModuleTemporaryPath -FullProjectPath $FullProjectPath + Copy-InternalFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullModuleTemporaryPath -FullProjectPath $FullProjectPath + Write-Text -End -Time $LinkingFilesTime + } +} \ No newline at end of file From 96a3d67fdf7888cf2dc08a5d043d95b2f4d14170 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 14 Jul 2024 19:47:30 +0200 Subject: [PATCH 10/32] Add scanning for cmdlets/aliases --- Private/Start-LibraryBuilding.ps1 | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Private/Start-LibraryBuilding.ps1 b/Private/Start-LibraryBuilding.ps1 index e918313..ff34cfc 100644 --- a/Private/Start-LibraryBuilding.ps1 +++ b/Private/Start-LibraryBuilding.ps1 @@ -4,7 +4,8 @@ [string] $ModuleName, [string] $RootDirectory, [string] $Version, - [System.Collections.IDictionary] $LibraryConfiguration + [System.Collections.IDictionary] $LibraryConfiguration, + [System.Collections.IDictionary] $CmdletsAliases ) if ($LibraryConfiguration.Count -eq 0) { @@ -130,6 +131,24 @@ } } } + + if (-not $LibraryConfiguration.BinaryModuleCmdletScanDisabled) { + $CmdletsFound = Get-PowerShellAssemblyMetadata -Path $File.FullName + if ($CmdletsFound -eq $false) { + $Errors = $true + } else { + if ($CmdletsFound.CmdletsToExport.Count -gt 0 -or $CmdletsFound.AliasesToExport.Count -gt 0) { + Write-Text -Text "Found $($CmdletsFound.CmdletsToExport.Count) cmdlets and $($CmdletsFound.AliasesToExport.Count) aliases in $File" -Color Yellow -PreAppend Information -SpacesBefore " " + if ($CmdletsFound.CmdletsToExport.Count -gt 0) { + Write-Text -Text "Cmdlets: $($CmdletsFound.CmdletsToExport -join ', ')" -Color Yellow -PreAppend Plus -SpacesBefore " " + } + if ($CmdletsFound.AliasesToExport.Count -gt 0) { + Write-Text -Text "Aliases: $($CmdletsFound.AliasesToExport -join ', ')" -Color Yellow -PreAppend Plus -SpacesBefore " " + } + $CmdletsAliases[$File.FullName] = $CmdletsFound + } + } + } try { Copy-Item -Path $File.FullName -Destination $ModuleBinFrameworkFolder -ErrorAction Stop } catch { From 86f5813dad70cf3fe3a9c005a0ab65c7f39a8dd1 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 21:55:47 +0200 Subject: [PATCH 11/32] Move to front --- .../Get-PowerShellAssemblyMetaData.ps1 | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) rename {Private => Public}/Get-PowerShellAssemblyMetaData.ps1 (79%) diff --git a/Private/Get-PowerShellAssemblyMetaData.ps1 b/Public/Get-PowerShellAssemblyMetaData.ps1 similarity index 79% rename from Private/Get-PowerShellAssemblyMetaData.ps1 rename to Public/Get-PowerShellAssemblyMetaData.ps1 index 71a980c..473319f 100644 --- a/Private/Get-PowerShellAssemblyMetaData.ps1 +++ b/Public/Get-PowerShellAssemblyMetaData.ps1 @@ -26,6 +26,7 @@ param ( [Parameter(Mandatory)][string] $Path ) + Write-Text -Text "[-] Loading assembly $Path" -Color Cyan try { $resolver = [System.Reflection.PathAssemblyResolver]::new( [string[]]@( @@ -36,18 +37,28 @@ Write-Text -Text "[-] Can't create PathAssemblyResolver. Please enable 'NETBinaryModuleCmdletScanDisabled' option when building PSPublishModule or investigate why library doesn't load for different modules. Error: $($_.Exception.Message)" -Color Red return $false } - $context = [System.Reflection.MetadataLoadContext]::new($resolver) + try { + $context = [System.Reflection.MetadataLoadContext]::new($resolver) + } catch { + Write-Text -Text "[-] Can't create MetadataLoadContext for file $Path. Skipping file, as most likely non-powershell binary. Error: $($_.Exception.Message)" -Color DarkYellow + return + } try { $smaAssembly = $context.LoadFromAssemblyPath([PSObject].Assembly.Location) $cmdletType = $smaAssembly.GetType('System.Management.Automation.Cmdlet') $cmdletAttribute = $smaAssembly.GetType('System.Management.Automation.CmdletAttribute') $aliasAttribute = $smaAssembly.GetType('System.Management.Automation.AliasAttribute') + $assembly = $context.LoadFromAssemblyPath($Path) + Write-Verbose -Message "Loaded assembly $($assembly.FullName), $($assembly.Location) searching for cmdlets and aliases" + $cmdletsToExport = [System.Collections.Generic.List[string]]::new() $aliasesToExport = [System.Collections.Generic.List[string]]::new() - $assembly.GetTypes() | Where-Object { + $Types = $assembly.GetTypes() + + $Types | Where-Object { $_.IsSubclassOf($cmdletType) } | ForEach-Object -Process { $cmdletInfo = $_.CustomAttributes | Where-Object { $_.AttributeType -eq $cmdletAttribute } @@ -65,6 +76,10 @@ CmdletsToExport = $cmdletsToExport AliasesToExport = $aliasesToExport } + } catch { + Write-Text -Text "[-] Can't load assembly $Path. Error: $($_.Exception.Message)" -Color Red + $context.Dispose() + return $false } finally { $context.Dispose() } From 4c48ddb559030f2b37f362741288afd1560dd2ed Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 21:58:18 +0200 Subject: [PATCH 12/32] Update --- .../PSPublishModule/{PublishModule.cs => Initialize.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Sources/PSPublishModule/PSPublishModule/{PublishModule.cs => Initialize.cs} (81%) diff --git a/Sources/PSPublishModule/PSPublishModule/PublishModule.cs b/Sources/PSPublishModule/PSPublishModule/Initialize.cs similarity index 81% rename from Sources/PSPublishModule/PSPublishModule/PublishModule.cs rename to Sources/PSPublishModule/PSPublishModule/Initialize.cs index 0474c69..814811c 100644 --- a/Sources/PSPublishModule/PSPublishModule/PublishModule.cs +++ b/Sources/PSPublishModule/PSPublishModule/Initialize.cs @@ -3,6 +3,6 @@ /// /// Dummy class to make the module visible to the module manager. /// -public class PublishModule { +public class Initialize { } From 61dcf64b8e84023f5e5137fc21d7e989a61b5f24 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 21:58:33 +0200 Subject: [PATCH 13/32] more functions to export --- PSPublishModule.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PSPublishModule.psd1 b/PSPublishModule.psd1 index 24ea665..672a8a3 100644 --- a/PSPublishModule.psd1 +++ b/PSPublishModule.psd1 @@ -7,7 +7,7 @@ Copyright = '(c) 2011 - 2024 Przemyslaw Klys @ Evotec. All rights reserved.' Description = 'Simple project allowing preparing, managing, building and publishing modules to PowerShellGallery' DotNetFrameworkVersion = '4.5.2' - FunctionsToExport = @('Convert-CommandsToList', 'Get-MissingFunctions', 'Initialize-PortableModule', 'Initialize-PortableScript', 'Initialize-ProjectManager', 'Invoke-ModuleBuild', 'New-ConfigurationArtefact', 'New-ConfigurationBuild', 'New-ConfigurationCommand', 'New-ConfigurationDocumentation', 'New-ConfigurationExecute', 'New-ConfigurationFormat', 'New-ConfigurationImportModule', 'New-ConfigurationInformation', 'New-ConfigurationManifest', 'New-ConfigurationModule', 'New-ConfigurationModuleSkip', 'New-ConfigurationPublish', 'New-ConfigurationTest', 'Register-Certificate', 'Remove-Comments', 'Send-GitHubRelease', 'Test-BasicModule', 'Test-ScriptFile', 'Test-ScriptModule') + FunctionsToExport = @('Convert-CommandsToList', 'Get-MissingFunctions', 'Get-PowerShellAssemblyMetadata', 'Initialize-PortableModule', 'Initialize-PortableScript', 'Initialize-ProjectManager', 'Invoke-ModuleBuild', 'New-ConfigurationArtefact', 'New-ConfigurationBuild', 'New-ConfigurationCommand', 'New-ConfigurationDocumentation', 'New-ConfigurationExecute', 'New-ConfigurationFormat', 'New-ConfigurationImportModule', 'New-ConfigurationInformation', 'New-ConfigurationManifest', 'New-ConfigurationModule', 'New-ConfigurationModuleSkip', 'New-ConfigurationPublish', 'New-ConfigurationTest', 'Register-Certificate', 'Remove-Comments', 'Send-GitHubRelease', 'Test-BasicModule', 'Test-ScriptFile', 'Test-ScriptModule') GUID = 'eb76426a-1992-40a5-82cd-6480f883ef4d' ModuleVersion = '2.0.14' PowerShellVersion = '5.1' From 45827450b7d2eeda3145da07436b271fa0ee2310 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 21:58:50 +0200 Subject: [PATCH 14/32] Remove data --- Private/New-PrepareStructure.ps1 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Private/New-PrepareStructure.ps1 b/Private/New-PrepareStructure.ps1 index 4f52db7..fbc0ba9 100644 --- a/Private/New-PrepareStructure.ps1 +++ b/Private/New-PrepareStructure.ps1 @@ -92,9 +92,6 @@ if ($PSBoundParameters.ContainsKey('LibrariesStandard')) { $Configuration.Information.LibrariesStandard = $LibrariesStandard } - #if ($DirectoryProjects) { - # $Configuration.Information.DirectoryProjects = $Path - #} if ($FunctionsToExportFolder) { $Configuration.Information.FunctionsToExport = $FunctionsToExportFolder } @@ -153,7 +150,7 @@ $Configuration.Information.Manifest.RootModule = "$($ModuleName).psm1" # 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. - $Configuration.Information.Manifest.CmdletsToExport = @() + #$Configuration.Information.Manifest.CmdletsToExport = @() # Variables to export from this module #$Configuration.Information.Manifest.VariablesToExport = @() From 62b8f5271d15a2885a569a9cd92b95319bfa9b43 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 21:59:08 +0200 Subject: [PATCH 15/32] detecting cmdlet --- Private/Start-LibraryBuilding.ps1 | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Private/Start-LibraryBuilding.ps1 b/Private/Start-LibraryBuilding.ps1 index ff34cfc..a8880a4 100644 --- a/Private/Start-LibraryBuilding.ps1 +++ b/Private/Start-LibraryBuilding.ps1 @@ -95,6 +95,7 @@ } } + $Count = 0 foreach ($Framework in $TranslateFrameworks.Keys) { if ($SupportedFrameworks.Contains($Framework.ToLower()) -and $LibraryConfiguration.Framework.Contains($Framework.ToLower())) { Write-Text "[+] Building $Framework ($Configuration)" @@ -119,6 +120,7 @@ Write-Text "[-] Can't list files in $PublishDirFolder folder. Error: $($_.Exception.Message)" -Color Red return $false } + $Count++ $Errors = $false :fileLoop foreach ($File in $List) { if ($LibraryConfiguration.ExcludeMainLibrary -and $File.Name -eq "$ModuleName.dll") { @@ -132,20 +134,22 @@ } } - if (-not $LibraryConfiguration.BinaryModuleCmdletScanDisabled) { - $CmdletsFound = Get-PowerShellAssemblyMetadata -Path $File.FullName - if ($CmdletsFound -eq $false) { - $Errors = $true - } else { - if ($CmdletsFound.CmdletsToExport.Count -gt 0 -or $CmdletsFound.AliasesToExport.Count -gt 0) { - Write-Text -Text "Found $($CmdletsFound.CmdletsToExport.Count) cmdlets and $($CmdletsFound.AliasesToExport.Count) aliases in $File" -Color Yellow -PreAppend Information -SpacesBefore " " - if ($CmdletsFound.CmdletsToExport.Count -gt 0) { - Write-Text -Text "Cmdlets: $($CmdletsFound.CmdletsToExport -join ', ')" -Color Yellow -PreAppend Plus -SpacesBefore " " + if ($Count -eq 1) { + if (-not $LibraryConfiguration.BinaryModuleCmdletScanDisabled) { + $CmdletsFound = Get-PowerShellAssemblyMetadata -Path $File.FullName + if ($CmdletsFound -eq $false) { + $Errors = $true + } else { + if ($CmdletsFound.CmdletsToExport.Count -gt 0 -or $CmdletsFound.AliasesToExport.Count -gt 0) { + Write-Text -Text "Found $($CmdletsFound.CmdletsToExport.Count) cmdlets and $($CmdletsFound.AliasesToExport.Count) aliases in $File" -Color Yellow -PreAppend Information -SpacesBefore " " + if ($CmdletsFound.CmdletsToExport.Count -gt 0) { + Write-Text -Text "Cmdlets: $($CmdletsFound.CmdletsToExport -join ', ')" -Color Yellow -PreAppend Plus -SpacesBefore " " + } + if ($CmdletsFound.AliasesToExport.Count -gt 0) { + Write-Text -Text "Aliases: $($CmdletsFound.AliasesToExport -join ', ')" -Color Yellow -PreAppend Plus -SpacesBefore " " + } + $CmdletsAliases[$File.FullName] = $CmdletsFound } - if ($CmdletsFound.AliasesToExport.Count -gt 0) { - Write-Text -Text "Aliases: $($CmdletsFound.AliasesToExport -join ', ')" -Color Yellow -PreAppend Plus -SpacesBefore " " - } - $CmdletsAliases[$File.FullName] = $CmdletsFound } } } From 5267ba3a6b1a2f8c5e0570c000ed80db2ba4fb7e Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 21:59:35 +0200 Subject: [PATCH 16/32] Improvements around functions/cmdlets/aliases detection --- .../Start-PreparingFunctionsAndAliases.ps1 | 60 +++++++++++++------ 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/Private/Start-PreparingFunctionsAndAliases.ps1 b/Private/Start-PreparingFunctionsAndAliases.ps1 index fcedb69..bf1b348 100644 --- a/Private/Start-PreparingFunctionsAndAliases.ps1 +++ b/Private/Start-PreparingFunctionsAndAliases.ps1 @@ -3,18 +3,18 @@ param( [System.Collections.IDictionary] $Configuration, $FullProjectPath, - $Files + $Files, + [System.Collections.IDictionary] $CmdletsAliases ) - if ($Configuration.Information.Manifest.FunctionsToExport -and $Configuration.Information.Manifest.AliasesToExport) { + if ($Configuration.Information.Manifest.FunctionsToExport -and $Configuration.Information.Manifest.AliasesToExport -and $Configuration.Information.Manifest.CmdletsToExport) { return $true } $AliasesAndFunctions = Write-TextWithTime -Text 'Preparing function and aliases names' { - Get-FunctionAliasesFromFolder -FullProjectPath $FullProjectPath -Files $Files #-Folder $Configuration.Information.AliasesToExport + Get-FunctionAliasesFromFolder -FullProjectPath $FullProjectPath -Files $Files } -PreAppend Information - Write-TextWithTime -Text "Checking for duplicates in funcions and aliases" { - # if ($AliasesAndFunctions -is [System.Collections.IDictionary]) { + Write-TextWithTime -Text "Checking for duplicates in funcions, aliases and cmdlets" { # if user hasn't defined functions we will use auto-detected functions if ($null -eq $Configuration.Information.Manifest.FunctionsToExport) { $Configuration.Information.Manifest.FunctionsToExport = $AliasesAndFunctions.Keys | Where-Object { $_ } @@ -25,22 +25,26 @@ # if user hasn't defined aliases we will use auto-detected aliases if ($null -eq $Configuration.Information.Manifest.AliasesToExport) { # 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. - $Configuration.Information.Manifest.AliasesToExport = $AliasesAndFunctions.Values | ForEach-Object { $_ } | Where-Object { $_ } + $Configuration.Information.Manifest.AliasesToExport = @( + $AliasesAndFunctions.Values | ForEach-Object { $_ } | Where-Object { $_ } + # cmdlets aliases will have duplicates if there is core/default folder + $CmdletsAliases.Values.AliasesToExport | ForEach-Object { $_ } | Where-Object { $_ } + ) if (-not $Configuration.Information.Manifest.AliasesToExport) { $Configuration.Information.Manifest.AliasesToExport = @() } } - # } else { - # this is not used, as we're using Hashtable above, but maybe if we change mind we can go back - # $Configuration.Information.Manifest.FunctionsToExport = $AliasesAndFunctions.Name | Where-Object { $_ } - # if (-not $Configuration.Information.Manifest.FunctionsToExport) { - # $Configuration.Information.Manifest.FunctionsToExport = @() - # } - # $Configuration.Information.Manifest.AliasesToExport = $AliasesAndFunctions.Alias | ForEach-Object { $_ } | Where-Object { $_ } - # if (-not $Configuration.Information.Manifest.AliasesToExport) { - # $Configuration.Information.Manifest.AliasesToExport = @() - # } - # } + # if user hasn't defined cmdlets we will use auto-detected cmdlets + if ($null -eq $Configuration.Information.Manifest.CmdletsToExport) { + $Configuration.Information.Manifest.CmdletsToExport = @( + $CmdletsAliases.Values.CmdletsToExport | ForEach-Object { $_ } | Where-Object { $_ } + ) + if (-not $Configuration.Information.Manifest.CmdletsToExport) { + $Configuration.Information.Manifest.CmdletsToExport = @() + } else { + $Configuration.Information.Manifest.CmdletsToExport = $Configuration.Information.Manifest.CmdletsToExport + } + } $FoundDuplicateAliases = $false if ($Configuration.Information.Manifest.AliasesToExport) { $UniqueAliases = $Configuration.Information.Manifest.AliasesToExport | Select-Object -Unique @@ -50,6 +54,10 @@ Write-Text " [-] Alias $Alias is also used as function name. Fix it!" -Color Red $FoundDuplicateAliases = $true } + if ($Alias -in $Configuration.Information.Manifest.CmdletsToExport) { + Write-Text " [-] Alias $Alias is also used as cmdlet name. Fix it!" -Color Red + $FoundDuplicateAliases = $true + } } foreach ($Alias in $DiffrenceAliases.InputObject) { Write-TextWithTime -Text " [-] Alias $Alias is used multiple times. Fix it!" -Color Red @@ -59,6 +67,24 @@ return $false } } + $FoundDuplicateCmdlets = $false + if ($Configuration.Information.Manifest.CmdletsToExport) { + $UniqueCmdlets = $Configuration.Information.Manifest.CmdletsToExport | Select-Object -Unique + $DiffrenceCmdlets = Compare-Object -ReferenceObject $Configuration.Information.Manifest.CmdletsToExport -DifferenceObject $UniqueCmdlets + foreach ($Cmdlet in $Configuration.Information.Manifest.CmdletsToExport) { + if ($Cmdlet -in $Configuration.Information.Manifest.FunctionsToExport) { + Write-Text " [-] Cmdlet $Cmdlet is also used as function name. Fix it!" -Color Red + $FoundDuplicateCmdlets = $true + } + } + foreach ($Cmdlet in $DiffrenceCmdlets.InputObject) { + Write-TextWithTime -Text " [-] Cmdlet $Cmdlet is used multiple times. Fix it!" -Color Red + $FoundDuplicateCmdlets = $true + } + if ($FoundDuplicateCmdlets) { + return $false + } + } if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.ScriptsToProcess)) { if ($null -eq $IsWindows -or $IsWindows -eq $true) { $StartsWithEnums = "$($Configuration.Information.ScriptsToProcess)\" From d871f62c8df3178a21a446dd5e39c93b66cf9730 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 21:59:48 +0200 Subject: [PATCH 17/32] Remove type --- Private/New-PSMFile.ps1 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Private/New-PSMFile.ps1 b/Private/New-PSMFile.ps1 index d732a8c..9157dba 100644 --- a/Private/New-PSMFile.ps1 +++ b/Private/New-PSMFile.ps1 @@ -4,7 +4,8 @@ function New-PSMFile { [string] $Path, [string[]] $FunctionNames, [string[]] $FunctionAliaes, - [System.Collections.IDictionary] $AliasesAndFunctions, + $AliasesAndFunctions, + [System.Collections.IDictionary] $CmdletsAliases, [Array] $LibrariesStandard, [Array] $LibrariesCore, [Array] $LibrariesDefault, @@ -68,14 +69,10 @@ function New-PSMFile { @( "`$ModuleFunctions = @{" foreach ($Module in $CommandModuleDependencies.Keys) { - #$Commands = "'$($CommandModuleDependencies[$Module] -join "','")'" "$Module = @{" - foreach ($Command in $($CommandModuleDependencies[$Module])) { - #foreach ($Function in $AliasesAndFunctions.Keys) { $Alias = "'$($AliasesAndFunctions[$Command] -join "','")'" " '$Command' = $Alias" - #} } "}" } From 43b7b18aae25510f38c793c5565b3641f94d525f Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 22:01:28 +0200 Subject: [PATCH 18/32] Building module improvment --- Private/Merge-Module.ps1 | 4 +- Private/Start-ModuleBuilding.ps1 | 211 ++++++------------------------- 2 files changed, 41 insertions(+), 174 deletions(-) diff --git a/Private/Merge-Module.ps1 b/Private/Merge-Module.ps1 index a35995c..41dcac8 100644 --- a/Private/Merge-Module.ps1 +++ b/Private/Merge-Module.ps1 @@ -9,7 +9,8 @@ function Merge-Module { [string] $Sort = 'NONE', [string[]] $FunctionsToExport, [string[]] $AliasesToExport, - [System.Collections.IDictionary] $AliasesAndFunctions, + $AliasesAndFunctions, + [System.Collections.IDictionary] $CmdletsAliases, [Array] $LibrariesStandard, [Array] $LibrariesCore, [Array] $LibrariesDefault, @@ -319,6 +320,7 @@ function Merge-Module { FunctionNames = $FunctionsToExport FunctionAliaes = $AliasesToExport AliasesAndFunctions = $AliasesAndFunctions + CmdletsAliases = $CmdletsAliases LibrariesStandard = $LibrariesStandard LibrariesCore = $LibrariesCore LibrariesDefault = $LibrariesDefault diff --git a/Private/Start-ModuleBuilding.ps1 b/Private/Start-ModuleBuilding.ps1 index ca8df19..1464773 100644 --- a/Private/Start-ModuleBuilding.ps1 +++ b/Private/Start-ModuleBuilding.ps1 @@ -74,7 +74,18 @@ return $false } - $Success = Start-LibraryBuilding -RootDirectory $FullProjectPath -Version $Configuration.Information.Manifest.ModuleVersion -ModuleName $ProjectName -LibraryConfiguration $Configuration.Steps.BuildLibraries + + $CmdletsAliases = [ordered] @{} + + $startLibraryBuildingSplat = @{ + RootDirectory = $FullProjectPath + Version = $Configuration.Information.Manifest.ModuleVersion + ModuleName = $ProjectName + LibraryConfiguration = $Configuration.Steps.BuildLibraries + CmdletsAliases = $CmdletsAliases + } + + $Success = Start-LibraryBuilding @startLibraryBuildingSplat if ($Success -eq $false) { return $false } @@ -106,8 +117,15 @@ $DirectoriesWithPS1 = $Variables.DirectoriesWithPS1 $Files = $Variables.Files - $AliasesAndFunctions = Start-PreparingFunctionsAndAliases -Configuration $Configuration -FullProjectPath $FullProjectPath -Files $Files - if ($AliasesAndFunctions -eq $false) { + $startPreparingFunctionsAndAliasesSplat = @{ + Configuration = $Configuration + FullProjectPath = $FullProjectPath + Files = $Files + CmdletsAliases = $CmdletsAliases + } + + $AliasesAndFunctions = Start-PreparingFunctionsAndAliases @startPreparingFunctionsAndAliasesSplat + if ($AliasesAndFunctions -contains $false) { return $false } @@ -161,178 +179,25 @@ return } - if ($Configuration.Steps.BuildModule.Merge) { - foreach ($Directory in $LinkDirectories) { - $Dir = [System.IO.Path]::Combine($FullTemporaryPath, "$Directory") - Add-Directory -Directory $Dir - } - # Workaround to link files that are not ps1/psd1 - [Array] $CompareWorkaround = foreach ($Directory in $DirectoriesWithPS1) { - if ($null -eq $IsWindows -or $IsWindows -eq $true) { - $Dir = -join ($Directory, "\") - } else { - $Dir = -join ($Directory, "/") - } - #-join ($_, '\') - } - - $LinkDirectoriesWithSupportFiles = $LinkDirectories | Where-Object { $_ -notin $CompareWorkaround } - foreach ($Directory in $LinkDirectoriesWithSupportFiles) { - $Dir = [System.IO.Path]::Combine($FullModuleTemporaryPath, "$Directory") - Add-Directory -Directory $Dir - } - - $LinkingFilesTime = Write-Text "[+] Linking files from root and sub directories" -Start - Copy-InternalFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath - Copy-InternalFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath - Write-Text -End -Time $LinkingFilesTime - - # Workaround to link files that are not ps1/psd1 - $FilesToLink = $LinkPrivatePublicFiles | Where-Object { $_ -notlike '*.ps1' -and $_ -notlike '*.psd1' } - Copy-InternalFiles -LinkFiles $FilesToLink -FullModulePath $FullModuleTemporaryPath -FullProjectPath $FullProjectPath - - if ($Configuration.Information.LibrariesStandard) { - # User provided option, we don't care - } elseif ($Configuration.Information.LibrariesCore -and $Configuration.Information.LibrariesDefault) { - # User provided option for core and default we don't care - } else { - # user hasn't provided any option, we set it to default - $Configuration.Information.LibrariesStandard = [System.IO.Path]::Combine("Lib", "Standard") - $Configuration.Information.LibrariesCore = [System.IO.Path]::Combine("Lib", "Core") - $Configuration.Information.LibrariesDefault = [System.IO.Path]::Combine("Lib", "Default") - } - - if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesCore)) { - if ($null -eq $IsWindows -or $IsWindows -eq $true) { - $StartsWithCore = -join ($Configuration.Information.LibrariesCore, "\") - } else { - $StartsWithCore = -join ($Configuration.Information.LibrariesCore, "/") - } - } - if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesDefault)) { - if ($null -eq $IsWindows -or $IsWindows -eq $true) { - $StartsWithDefault = -join ($Configuration.Information.LibrariesDefault, "\") - } else { - $StartsWithDefault = -join ($Configuration.Information.LibrariesDefault, "/") - } - } - if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesStandard)) { - if ($null -eq $IsWindows -or $IsWindows -eq $true) { - $StartsWithStandard = -join ($Configuration.Information.LibrariesStandard, "\") - } else { - $StartsWithStandard = -join ($Configuration.Information.LibrariesStandard, "/") - } - } - - $CoreFiles = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithCore) } - $DefaultFiles = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithDefault) } - $StandardFiles = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithStandard) } - - $Default = $false - $Core = $false - $Standard = $false - if ($CoreFiles.Count -gt 0) { - $Core = $true - } - if ($DefaultFiles.Count -gt 0) { - $Default = $true - } - if ($StandardFiles.Count -gt 0) { - $Standard = $true - } - if ($Standard -and $Core -and $Default) { - $FrameworkNet = 'Default' - $Framework = 'Standard' - } elseif ($Standard -and $Core) { - $Framework = 'Standard' - $FrameworkNet = 'Standard' - } elseif ($Core -and $Default) { - $Framework = 'Core' - $FrameworkNet = 'Default' - } elseif ($Standard -and $Default) { - $Framework = 'Standard' - $FrameworkNet = 'Default' - } elseif ($Standard) { - $Framework = 'Standard' - $FrameworkNet = 'Standard' - } elseif ($Core) { - $Framework = 'Core' - $FrameworkNet = '' - } elseif ($Default) { - $Framework = '' - $FrameworkNet = 'Default' - } - - if ($Framework -eq 'Core') { - $FilesLibrariesCore = $CoreFiles - } elseif ($Framework -eq 'Standard') { - $FilesLibrariesCore = $StandardFiles - } - if ($FrameworkNet -eq 'Default') { - $FilesLibrariesDefault = $DefaultFiles - } elseif ($FrameworkNet -eq 'Standard') { - $FilesLibrariesDefault = $StandardFiles - } - if ($FrameworkNet -eq 'Standard' -and $Framework -eq 'Standard') { - $FilesLibrariesStandard = $FilesLibrariesCore - } - $Success = Merge-Module -ModuleName $ProjectName ` - -ModulePathSource $FullTemporaryPath ` - -ModulePathTarget $FullModuleTemporaryPath ` - -Sort $Configuration.Options.Merge.Sort ` - -FunctionsToExport $Configuration.Information.Manifest.FunctionsToExport ` - -AliasesToExport $Configuration.Information.Manifest.AliasesToExport ` - -AliasesAndFunctions $AliasesAndFunctions ` - -LibrariesStandard $FilesLibrariesStandard ` - -LibrariesCore $FilesLibrariesCore ` - -LibrariesDefault $FilesLibrariesDefault ` - -FormatCodePSM1 $Configuration.Options.Merge.FormatCodePSM1 ` - -FormatCodePSD1 $Configuration.Options.Merge.FormatCodePSD1 ` - -Configuration $Configuration -DirectoriesWithPS1 $DirectoriesWithPS1 ` - -ClassesPS1 $DirectoriesWithClasses -IncludeAsArray $Configuration.Information.IncludeAsArray - - if ($Success -eq $false) { - return $false - } - - if ($Configuration.Steps.BuildModule.CreateFileCatalog) { - # Something is wrong here for folders other than root, need investigation - $TimeToExecuteSign = [System.Diagnostics.Stopwatch]::StartNew() - Write-Text "[+] Creating file catalog" -Color Blue - $TimeToExecuteSign = [System.Diagnostics.Stopwatch]::StartNew() - $CategoryPaths = @( - $FullModuleTemporaryPath - $NotEmptyPaths = (Get-ChildItem -Directory -Path $FullModuleTemporaryPath -Recurse).FullName - if ($NotEmptyPaths) { - $NotEmptyPaths - } - ) - foreach ($CatPath in $CategoryPaths) { - $CatalogFile = [io.path]::Combine($CatPath, "$ProjectName.cat") - $FileCreated = New-FileCatalog -Path $CatPath -CatalogFilePath $CatalogFile -CatalogVersion 2.0 - if ($FileCreated) { - Write-Text " [>] Catalog file covering $CatPath was created $($FileCreated.Name)" -Color Yellow - } - } - $TimeToExecuteSign.Stop() - Write-Text "[+] Creating file catalog [Time: $($($TimeToExecuteSign.Elapsed).Tostring())]" -Color Blue - } - $SuccessFullSigning = Start-ModuleSigning -Configuration $Configuration -FullModuleTemporaryPath $FullModuleTemporaryPath - if ($SuccessFullSigning -eq $false) { - return $false - } - } - if (-not $Configuration.Steps.BuildModule.Merge) { - foreach ($Directory in $LinkDirectories) { - $Dir = [System.IO.Path]::Combine($FullModuleTemporaryPath, "$Directory") - Add-Directory -Directory $Dir - } - $LinkingFilesTime = Write-Text "[+] Linking files from root and sub directories" -Start - Copy-InternalFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullModuleTemporaryPath -FullProjectPath $FullProjectPath - Copy-InternalFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullModuleTemporaryPath -FullProjectPath $FullProjectPath - Write-Text -End -Time $LinkingFilesTime + $startModuleMergingSplat = @{ + Configuration = $Configuration + ProjectName = $ProjectName + FullTemporaryPath = $FullTemporaryPath + FullModuleTemporaryPath = $FullModuleTemporaryPath + FullProjectPath = $FullProjectPath + LinkDirectories = $LinkDirectories + LinkFilesRoot = $LinkFilesRoot + LinkPrivatePublicFiles = $LinkPrivatePublicFiles + DirectoriesWithPS1 = $DirectoriesWithPS1 + DirectoriesWithClasses = $DirectoriesWithClasses + AliasesAndFunction = $AliasesAndFunctions + CmdletsAliases = $CmdletsAliases } + $Success = Start-ModuleMerging @startModuleMergingSplat + if ($Success -contains $false) { + return $false + } # Revers Path to current locatikon Set-Location -Path $CurrentLocation From ac40d82d3e7259fc54527b04d1d7f0860d6bbde6 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 22:01:41 +0200 Subject: [PATCH 19/32] Small improvements --- Private/Start-ModuleMerging.ps1 | 39 ++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/Private/Start-ModuleMerging.ps1 b/Private/Start-ModuleMerging.ps1 index 52b33a3..947c44b 100644 --- a/Private/Start-ModuleMerging.ps1 +++ b/Private/Start-ModuleMerging.ps1 @@ -11,7 +11,8 @@ [Array] $LinkPrivatePublicFiles, [string[]] $DirectoriesWithPS1, [string[]] $DirectoriesWithClasses, - [System.Collections.IDictionary] $AliasesAndFunctions + $AliasesAndFunctions, + [System.Collections.IDictionary] $CmdletsAliases ) if ($Configuration.Steps.BuildModule.Merge) { foreach ($Directory in $LinkDirectories) { @@ -127,20 +128,28 @@ if ($FrameworkNet -eq 'Standard' -and $Framework -eq 'Standard') { $FilesLibrariesStandard = $FilesLibrariesCore } - $Success = Merge-Module -ModuleName $ProjectName ` - -ModulePathSource $FullTemporaryPath ` - -ModulePathTarget $FullModuleTemporaryPath ` - -Sort $Configuration.Options.Merge.Sort ` - -FunctionsToExport $Configuration.Information.Manifest.FunctionsToExport ` - -AliasesToExport $Configuration.Information.Manifest.AliasesToExport ` - -AliasesAndFunctions $AliasesAndFunctions ` - -LibrariesStandard $FilesLibrariesStandard ` - -LibrariesCore $FilesLibrariesCore ` - -LibrariesDefault $FilesLibrariesDefault ` - -FormatCodePSM1 $Configuration.Options.Merge.FormatCodePSM1 ` - -FormatCodePSD1 $Configuration.Options.Merge.FormatCodePSD1 ` - -Configuration $Configuration -DirectoriesWithPS1 $DirectoriesWithPS1 ` - -ClassesPS1 $DirectoriesWithClasses -IncludeAsArray $Configuration.Information.IncludeAsArray + + $mergeModuleSplat = @{ + ModuleName = $ProjectName + ModulePathSource = $FullTemporaryPath + ModulePathTarget = $FullModuleTemporaryPath + Sort = $Configuration.Options.Merge.Sort + FunctionsToExport = $Configuration.Information.Manifest.FunctionsToExport + AliasesToExport = $Configuration.Information.Manifest.AliasesToExport + AliasesAndFunctions = $AliasesAndFunctions + CmdletsAliases = $CmdletsAliases + LibrariesStandard = $FilesLibrariesStandard + LibrariesCore = $FilesLibrariesCore + LibrariesDefault = $FilesLibrariesDefault + FormatCodePSM1 = $Configuration.Options.Merge.FormatCodePSM1 + FormatCodePSD1 = $Configuration.Options.Merge.FormatCodePSD1 + Configuration = $Configuration + DirectoriesWithPS1 = $DirectoriesWithPS1 + ClassesPS1 = $DirectoriesWithClasses + IncludeAsArray = $Configuration.Information.IncludeAsArray + } + + $Success = Merge-Module @mergeModuleSplat if ($Success -eq $false) { return $false From b471f7ce62206cff1703fb4442b53fec13c1246c Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 22:02:25 +0200 Subject: [PATCH 20/32] update project config --- .../PSPublishModule/PSPublishModule.csproj | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj b/Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj index f8bf986..e70cedd 100644 --- a/Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj +++ b/Sources/PSPublishModule/PSPublishModule/PSPublishModule.csproj @@ -1,33 +1,33 @@  - - net472;netstandard2.0;net6.0;net7.0;net8.0 - PSPublishModule - PSPublishModule - PSPublishModule - 0.1.0 - false - Evotec - Przemyslaw Klys - latest - true - + + net472;netstandard2.0;net6.0;net7.0;net8.0 + PSPublishModule + PSPublishModule + PSPublishModule + 1.0.0 + false + Evotec + Przemyslaw Klys + latest + true + - - true - + + true + - - - - + + + + - - - + + + - - - + + + \ No newline at end of file From 8f4f3b547ed02b33474216ce94499dea43ab3863 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sat, 20 Jul 2024 22:04:22 +0200 Subject: [PATCH 21/32] Detect identity --- .../Example.FindMicrosoftClientIdentity.ps1 | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Examples/Example.FindMicrosoftClientIdentity.ps1 diff --git a/Examples/Example.FindMicrosoftClientIdentity.ps1 b/Examples/Example.FindMicrosoftClientIdentity.ps1 new file mode 100644 index 0000000..8f5a74b --- /dev/null +++ b/Examples/Example.FindMicrosoftClientIdentity.ps1 @@ -0,0 +1,27 @@ +$Folder = $Env:PSModulePath -split ";" +foreach ($F in $Folder) { + if (Test-Path -Path $F) { + Get-ChildItem -LiteralPath $F -Recurse -Filter "*.dll" | ForEach-Object { + if ($_.Name -like "Microsoft.Identity.Client.dll") { + [PSCustomObject] @{ + Name = $_.FullName + Version = $_.VersionInfo.FileVersion + } + } + } + } +} + +# Could not load type 'Microsoft.Identity.Client.Extensions.Msal.MsalCachePersistenceException +foreach ($F in $Folder) { + if (Test-Path -Path $F) { + Get-ChildItem -LiteralPath $F -Recurse -Filter "*.dll" | ForEach-Object { + if ($_.Name -like "Microsoft.Identity.Client.*") { + [PSCustomObject] @{ + Name = $_.FullName + Version = $_.VersionInfo.FileVersion + } + } + } + } +} \ No newline at end of file From 5cbf5013923bcea2b191ac726456fb47170f6bae Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 21 Jul 2024 18:28:12 +0200 Subject: [PATCH 22/32] Improve resolve conflict with class choice --- Private/Merge-Module.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Private/Merge-Module.ps1 b/Private/Merge-Module.ps1 index 41dcac8..3012ec2 100644 --- a/Private/Merge-Module.ps1 +++ b/Private/Merge-Module.ps1 @@ -281,9 +281,9 @@ function Merge-Module { $IntegrateContent = @( # add resolve conflicting binary option if ($Configuration.Steps.BuildModule.ResolveBinaryConflicts -is [System.Collections.IDictionary]) { - New-DLLResolveConflict -ProjectName $Configuration.Steps.BuildModule.ResolveBinaryConflicts.ProjectName + New-DLLResolveConflict -ProjectName $Configuration.Steps.BuildModule.ResolveBinaryConflicts.ProjectName -LibraryConfiguration $Configuration.Steps.BuildLibraries } elseif ($Configuration.Steps.BuildModule.ResolveBinaryConflicts -eq $true) { - New-DLLResolveConflict + New-DLLResolveConflict -LibraryConfiguration $Configuration.Steps.BuildLibraries } Add-BinaryImportModule -Configuration $Configuration -LibrariesStandard $LibrariesStandard -LibrariesCore $LibrariesCore -LibrariesDefault $LibrariesDefault From 4aa62c41a30194aa2ed1c7acebf05e2f949fe985 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 21 Jul 2024 18:28:32 +0200 Subject: [PATCH 23/32] Resolve conflict improvement for class choice --- Private/New-DLLResolveConflict.ps1 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Private/New-DLLResolveConflict.ps1 b/Private/New-DLLResolveConflict.ps1 index fd0d8dd..33aee51 100644 --- a/Private/New-DLLResolveConflict.ps1 +++ b/Private/New-DLLResolveConflict.ps1 @@ -1,8 +1,16 @@ function New-DLLResolveConflict { [CmdletBinding()] param( - [string] $ProjectName + [string] $ProjectName, + [System.Collections.IDictionary] $LibraryConfiguration ) + + if ($LibraryConfiguration.SearchClass) { + $SearchClass = $LibraryConfiguration.SearchClass + } else { + $SearchClass = "`$LibraryName.Initialize" + } + if ($ProjectName) { $StandardName = "'$ProjectName'" } else { @@ -13,7 +21,7 @@ # Get library name, from the PSM1 file name `$LibraryName = $StandardName `$Library = "`$LibraryName.dll" - `$Class = "`$LibraryName.Initialize" + `$Class = "$SearchClass" `$AssemblyFolders = Get-ChildItem -Path `$PSScriptRoot\Lib -Directory -ErrorAction SilentlyContinue From 356e0baa1bf5c781809101f30dd54c6d78a86023 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 21 Jul 2024 18:28:48 +0200 Subject: [PATCH 24/32] New options, rename and added docs --- Public/New-ConfigurationBuild.ps1 | 64 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/Public/New-ConfigurationBuild.ps1 b/Public/New-ConfigurationBuild.ps1 index 4039780..b8a818c 100644 --- a/Public/New-ConfigurationBuild.ps1 +++ b/Public/New-ConfigurationBuild.ps1 @@ -46,15 +46,6 @@ By default module builder will attempt to fix it. This option disables this functionality. Best practice is to use $MyInvocation.MyCommand.Module.ModuleBase or similar instead of relative paths. - .PARAMETER MergeLibraryDebugging - Parameter description - - .PARAMETER ResolveBinaryConflicts - Parameter description - - .PARAMETER ResolveBinaryConflictsName - Parameter description - .PARAMETER CertificateThumbprint Parameter description @@ -110,6 +101,18 @@ .PARAMETER NETLineByLineAddType Adds Add-Type line by line, this is useful if you have a lot of libraries and you want to see which one is causing the issue. + .PARAMETER NETMergeLibraryDebugging + Add special logic to simplify debugging of merged libraries, this is useful if you have a lot of libraries and you want to see which one is causing the issue. + + .PARAMETER NETResolveBinaryConflicts + Add special logic to resolve binary conflicts. It uses by defalt the project name. If you want to use different name use NETResolveBinaryConflictsName + + .PARAMETER NETResolveBinaryConflictsName + Add special logic to resolve binary conflicts for specific project name. + + .PARAMETER NETSearchClass + Provide a name for class when using NETResolveBinaryConflicts or NETResolveBinaryConflictsName. By default it uses `$LibraryName.Initialize` however that may not be always the case + .EXAMPLE $newConfigurationBuildSplat = @{ Enable = $true @@ -117,8 +120,8 @@ MergeModuleOnBuild = $true MergeFunctionsFromApprovedModules = $true CertificateThumbprint = '483292C9E317AA1' - ResolveBinaryConflicts = $true - ResolveBinaryConflictsName = 'Transferetto' + NETResolveBinaryConflicts = $true + NETResolveBinaryConflictsName = 'Transferetto' NETProjectName = 'Transferetto' NETConfiguration = 'Release' NETFramework = 'netstandard2.0' @@ -149,10 +152,6 @@ [switch] $DoNotAttemptToFixRelativePaths, - [alias("NETMergeLibraryDebugging")][switch] $MergeLibraryDebugging, - [switch] $ResolveBinaryConflicts, - [string] $ResolveBinaryConflictsName, - [string] $CertificateThumbprint, [string] $CertificatePFXPath, [string] $CertificatePFXBase64, @@ -168,7 +167,11 @@ [string[]] $NETBinaryModule, [alias('HandleAssemblyWithSameName')][switch] $NETHandleAssemblyWithSameName, [switch] $NETLineByLineAddType, - [switch] $NETBinaryModuleCmdletScanDisabled + [switch] $NETBinaryModuleCmdletScanDisabled, + [alias("MergeLibraryDebugging")][switch] $NETMergeLibraryDebugging, + [alias("ResolveBinaryConflicts")][switch] $NETResolveBinaryConflicts, + [alias("ResolveBinaryConflictsName")][string] $NETResolveBinaryConflictsName, + [string] $NETSearchClass ) if ($PSBoundParameters.ContainsKey('Enable')) { @@ -274,28 +277,28 @@ } } - if ($PSBoundParameters.ContainsKey('MergeLibraryDebugging')) { + if ($PSBoundParameters.ContainsKey('NETMergeLibraryDebugging')) { [ordered] @{ Type = 'Build' BuildModule = [ordered] @{ - DebugDLL = $MergeLibraryDebugging.IsPresent + DebugDLL = $NETMergeLibraryDebugging.IsPresent } } } - if ($PSBoundParameters.ContainsKey('ResolveBinaryConflictsName')) { + if ($PSBoundParameters.ContainsKey('NETResolveBinaryConflictsName')) { [ordered] @{ Type = 'Build' BuildModule = [ordered] @{ ResolveBinaryConflicts = @{ - ProjectName = $ResolveBinaryConflictsName + ProjectName = $NETResolveBinaryConflictsName } } } - } elseif ($PSBoundParameters.ContainsKey('ResolveBinaryConflicts')) { + } elseif ($PSBoundParameters.ContainsKey('NETResolveBinaryConflicts')) { [ordered] @{ Type = 'Build' BuildModule = [ordered] @{ - ResolveBinaryConflicts = $ResolveBinaryConflicts.IsPresent + ResolveBinaryConflicts = $NETResolveBinaryConflicts.IsPresent } } } @@ -343,14 +346,6 @@ # Build libraries configuration, this is useful if you have C# project that you want to build # so libraries are autogenerated and you can use them in your PowerShell module - - # $BuildLibraries = @{ - # Enable = $false # build once every time nuget gets updated - # Configuration = 'Release' - # Framework = 'netstandard2.0', 'net472' - # ProjectName = 'ImagePlayground.PowerShell' - # } - if ($PSBoundParameters.ContainsKey('NETConfiguration')) { [ordered] @{ Type = 'BuildLibraries' @@ -455,4 +450,13 @@ } } } + + if ($PSBoundParameters.ContainsKey('NETSearchClass')) { + [ordered] @{ + Type = 'BuildLibraries' + BuildLibraries = [ordered] @{ + SearchClass = $NETSearchClass + } + } + } } \ No newline at end of file From 9bf084a467b8e66f135bffc4723be15b22ef9322 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 21 Jul 2024 18:30:04 +0200 Subject: [PATCH 25/32] Update basic example --- Examples/Example.CmdletAliasDetection.ps1 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Examples/Example.CmdletAliasDetection.ps1 diff --git a/Examples/Example.CmdletAliasDetection.ps1 b/Examples/Example.CmdletAliasDetection.ps1 new file mode 100644 index 0000000..45c571a --- /dev/null +++ b/Examples/Example.CmdletAliasDetection.ps1 @@ -0,0 +1,6 @@ +Import-Module .\PSPublishModule.psd1 -Force + +#Get-PowerShellAssemblyMetadata -Path "C:\Support\GitHub\Mailozaurr\Sources\Mailozaurr.PowerShell\bin\Release\net472\Mailozaurr.PowerShell.dll" | Format-Table +#Get-PowerShellAssemblyMetadata -Path "C:\Support\GitHub\Mailozaurr\Sources\Mailozaurr.PowerShell\bin\Release\net7.0\Mailozaurr.PowerShell.dll" +#Get-PowerShellAssemblyMetadata -Path "C:\Support\GitHub\PSEventViewer\Sources\PSEventViewer\bin\Debug\net6.0\PSEventViewer.dll" | Format-Table +Get-PowerShellAssemblyMetadata -Path "C:\Support\GitHub\PSEventViewer\Sources\PSEventViewer\bin\Debug\net472\PSEventViewer.dll" -Verbose | Format-Table \ No newline at end of file From 5a4890cd2e4d554edb5f297140b9959ffa159a8b Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 21 Jul 2024 21:48:02 +0200 Subject: [PATCH 26/32] Fixes detection of functions/aliases when other folders contain "public" in their name --- Private/Get-FunctionAliasesFromFolder.ps1 | 30 ++++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Private/Get-FunctionAliasesFromFolder.ps1 b/Private/Get-FunctionAliasesFromFolder.ps1 index 1c22706..23aaffb 100644 --- a/Private/Get-FunctionAliasesFromFolder.ps1 +++ b/Private/Get-FunctionAliasesFromFolder.ps1 @@ -3,22 +3,34 @@ function Get-FunctionAliasesFromFolder { param( [string] $FullProjectPath, [string[]] $Folder, - [Array] $Files + [Array] $Files, + [string] $FunctionsToExport, + [string] $AliasesToExport ) - $FilesPS1 = foreach ($File in $Files) { - $Path = [io.path]::Combine("*", 'Public', '*') - if ($file.FullName -like $Path) { - if ($File.Extension -eq '.ps1' -or $File.Extension -eq '*.psm1') { - $File + [Array] $FilesPS1 = foreach ($File in $Files) { + if ($FunctionsToExport) { + $PathFunctions = [io.path]::Combine($FullProjectPath, $FunctionsToExport, '*') + if ($File.FullName -like $PathFunctions) { + if ($File.Extension -eq '.ps1' -or $File.Extension -eq '*.psm1') { + $File + } + } + } + if ($AliasesToExport -and $AliasesToExport -ne $FunctionsToExport) { + $PathAliases = [io.path]::Combine($FullProjectPath, $AliasesToExport, '*') + if ($File.FullName -like $PathAliases) { + if ($File.Extension -eq '.ps1' -or $File.Extension -eq '*.psm1') { + $File + } } } } - [Array] $Content = foreach ($File in $FilesPS1) { + [Array] $Content = foreach ($File in $FilesPS1 | Sort-Object -Unique) { '' Get-Content -LiteralPath $File.FullName -Raw -Encoding UTF8 } $Code = $Content -join [System.Environment]::NewLine - $AliasesToExport = Get-FunctionAliases -Content $Code -AsHashtable - $AliasesToExport + $OutputAliasesToExport = Get-FunctionAliases -Content $Code -AsHashtable + $OutputAliasesToExport } \ No newline at end of file From 9ab1a345628f2c0ef42b1e7396741a54c2ca196b Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 21 Jul 2024 21:49:13 +0200 Subject: [PATCH 27/32] Added missing hashtables --- Private/New-PSMFile.ps1 | 2 +- Private/Start-ModuleMerging.ps1 | 2 +- Private/Start-PreparingFunctionsAndAliases.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Private/New-PSMFile.ps1 b/Private/New-PSMFile.ps1 index 9157dba..699b992 100644 --- a/Private/New-PSMFile.ps1 +++ b/Private/New-PSMFile.ps1 @@ -4,7 +4,7 @@ function New-PSMFile { [string] $Path, [string[]] $FunctionNames, [string[]] $FunctionAliaes, - $AliasesAndFunctions, + [System.Collections.IDictionary] $AliasesAndFunctions, [System.Collections.IDictionary] $CmdletsAliases, [Array] $LibrariesStandard, [Array] $LibrariesCore, diff --git a/Private/Start-ModuleMerging.ps1 b/Private/Start-ModuleMerging.ps1 index 947c44b..f0898ff 100644 --- a/Private/Start-ModuleMerging.ps1 +++ b/Private/Start-ModuleMerging.ps1 @@ -11,7 +11,7 @@ [Array] $LinkPrivatePublicFiles, [string[]] $DirectoriesWithPS1, [string[]] $DirectoriesWithClasses, - $AliasesAndFunctions, + [System.Collections.IDictionary] $AliasesAndFunctions, [System.Collections.IDictionary] $CmdletsAliases ) if ($Configuration.Steps.BuildModule.Merge) { diff --git a/Private/Start-PreparingFunctionsAndAliases.ps1 b/Private/Start-PreparingFunctionsAndAliases.ps1 index bf1b348..3793df1 100644 --- a/Private/Start-PreparingFunctionsAndAliases.ps1 +++ b/Private/Start-PreparingFunctionsAndAliases.ps1 @@ -11,7 +11,7 @@ } $AliasesAndFunctions = Write-TextWithTime -Text 'Preparing function and aliases names' { - Get-FunctionAliasesFromFolder -FullProjectPath $FullProjectPath -Files $Files + Get-FunctionAliasesFromFolder -FullProjectPath $FullProjectPath -Files $Files -FunctionsToExport $Configuration.Information.FunctionsToExport -AliasesToExport $Configuration.Information.AliasesToExport } -PreAppend Information Write-TextWithTime -Text "Checking for duplicates in funcions, aliases and cmdlets" { From 28156c4249b94e10da4545ea46b475c9c723e7c3 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Sun, 21 Jul 2024 21:49:40 +0200 Subject: [PATCH 28/32] Added missing hashtable --- Private/Merge-Module.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Private/Merge-Module.ps1 b/Private/Merge-Module.ps1 index 3012ec2..1606dbb 100644 --- a/Private/Merge-Module.ps1 +++ b/Private/Merge-Module.ps1 @@ -9,7 +9,7 @@ function Merge-Module { [string] $Sort = 'NONE', [string[]] $FunctionsToExport, [string[]] $AliasesToExport, - $AliasesAndFunctions, + [System.Collections.IDictionary] $AliasesAndFunctions, [System.Collections.IDictionary] $CmdletsAliases, [Array] $LibrariesStandard, [Array] $LibrariesCore, From caaf671aa1cff3f095f6f088c3b286a79a665d88 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Wed, 7 Aug 2024 10:58:51 +0200 Subject: [PATCH 29/32] Fix wrong export of cmdlet --- Private/New-PSMFile.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Private/New-PSMFile.ps1 b/Private/New-PSMFile.ps1 index 699b992..0b0eadf 100644 --- a/Private/New-PSMFile.ps1 +++ b/Private/New-PSMFile.ps1 @@ -61,7 +61,9 @@ function New-PSMFile { $Cmdlet = ($Configuration.Information.Manifest.CmdletsToExport | Sort-Object -Unique) -join "','" $Cmdlet = "'$Cmdlet'" } else { - $Cmdlet = '"*"' + if ($BinaryModule.Count -gt 0) { + $Cmdlet = '"*"' + } } # This allows to export functions only if module loading works correctly @@ -113,7 +115,7 @@ function New-PSMFile { # for now we just export everything # we may need to change it in the future - if ($BinaryModule.Count -gt 0 -or $Cmdlet) { + if ($Cmdlet) { "Export-ModuleMember -Function @(`$FunctionsToLoad) -Alias @(`$AliasesToLoad) -Cmdlet $Cmdlet" | Out-File -Append -LiteralPath $Path -Encoding $Encoding -ErrorAction Stop } else { "Export-ModuleMember -Function @(`$FunctionsToLoad) -Alias @(`$AliasesToLoad)" | Out-File -Append -LiteralPath $Path -Encoding $Encoding -ErrorAction Stop @@ -123,7 +125,7 @@ function New-PSMFile { # this loads functions/aliases as designed #"" | Out-File -Append -LiteralPath $Path -Encoding utf8 "# Export functions and aliases as required" | Out-File -Append -LiteralPath $Path -Encoding $Encoding - if ($BinaryModule.Count -gt 0 -or $Cmdlet) { + if ($Cmdlet) { # for now we just export everything # we may need to change it in the future "Export-ModuleMember -Function @($Functions) -Alias @($Aliases) -Cmdlet $Cmdlet" | Out-File -Append -LiteralPath $Path -Encoding $Encoding -ErrorAction Stop From a2784b09e5f1b87ba2bc6d5eb87eaf30de6015e0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Wed, 14 Aug 2024 20:52:13 +0200 Subject: [PATCH 30/32] Add custom and builtin replacement functionality --- PSPublishModule.psd1 | 2 +- Private/Merge-Module.ps1 | 5 +++ Private/New-PrepareStructure.ps1 | 14 ++++++++ Private/Repair-CustomPlaceHolders.ps1 | 48 +++++++++++++++++++++++++ Public/New-ConfigurationBuild.ps1 | 25 +++++++++++++ Public/New-ConfigurationPlaceHolder.ps1 | 24 +++++++++++++ 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 Private/Repair-CustomPlaceHolders.ps1 create mode 100644 Public/New-ConfigurationPlaceHolder.ps1 diff --git a/PSPublishModule.psd1 b/PSPublishModule.psd1 index 672a8a3..d1661bc 100644 --- a/PSPublishModule.psd1 +++ b/PSPublishModule.psd1 @@ -7,7 +7,7 @@ Copyright = '(c) 2011 - 2024 Przemyslaw Klys @ Evotec. All rights reserved.' Description = 'Simple project allowing preparing, managing, building and publishing modules to PowerShellGallery' DotNetFrameworkVersion = '4.5.2' - FunctionsToExport = @('Convert-CommandsToList', 'Get-MissingFunctions', 'Get-PowerShellAssemblyMetadata', 'Initialize-PortableModule', 'Initialize-PortableScript', 'Initialize-ProjectManager', 'Invoke-ModuleBuild', 'New-ConfigurationArtefact', 'New-ConfigurationBuild', 'New-ConfigurationCommand', 'New-ConfigurationDocumentation', 'New-ConfigurationExecute', 'New-ConfigurationFormat', 'New-ConfigurationImportModule', 'New-ConfigurationInformation', 'New-ConfigurationManifest', 'New-ConfigurationModule', 'New-ConfigurationModuleSkip', 'New-ConfigurationPublish', 'New-ConfigurationTest', 'Register-Certificate', 'Remove-Comments', 'Send-GitHubRelease', 'Test-BasicModule', 'Test-ScriptFile', 'Test-ScriptModule') + FunctionsToExport = @('Convert-CommandsToList', 'Get-MissingFunctions', 'Get-PowerShellAssemblyMetadata', 'Initialize-PortableModule', 'Initialize-PortableScript', 'Initialize-ProjectManager', 'Invoke-ModuleBuild', 'New-ConfigurationArtefact', 'New-ConfigurationBuild', 'New-ConfigurationCommand', 'New-ConfigurationDocumentation', 'New-ConfigurationExecute', 'New-ConfigurationFormat', 'New-ConfigurationImportModule', 'New-ConfigurationInformation', 'New-ConfigurationManifest', 'New-ConfigurationModule', 'New-ConfigurationModuleSkip', 'New-ConfigurationPlaceHolder', 'New-ConfigurationPublish', 'New-ConfigurationTest', 'Register-Certificate', 'Remove-Comments', 'Send-GitHubRelease', 'Test-BasicModule', 'Test-ScriptFile', 'Test-ScriptModule') GUID = 'eb76426a-1992-40a5-82cd-6480f883ef4d' ModuleVersion = '2.0.14' PowerShellVersion = '5.1' diff --git a/Private/Merge-Module.ps1 b/Private/Merge-Module.ps1 index 1606dbb..a0bdbaf 100644 --- a/Private/Merge-Module.ps1 +++ b/Private/Merge-Module.ps1 @@ -340,6 +340,11 @@ function Merge-Module { return $false } + $Success = Repair-CustomPlaceHolders -Path $PSM1FilePath -Configuration $Configuration + if ($Success -eq $false) { + return $false + } + # Format standard PSM1 file $Success = Format-Code -FilePath $PSM1FilePath -FormatCode $FormatCodePSM1 if ($Success -eq $false) { diff --git a/Private/New-PrepareStructure.ps1 b/Private/New-PrepareStructure.ps1 index fbc0ba9..a5b1e8c 100644 --- a/Private/New-PrepareStructure.ps1 +++ b/Private/New-PrepareStructure.ps1 @@ -154,6 +154,14 @@ # Variables to export from this module #$Configuration.Information.Manifest.VariablesToExport = @() + # This is to store custom placeholders + if (-not $Configuration.PlaceHolder) { + $Configuration.PlaceHolder = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() + } + if (-not $Configuration.PlaceHolderOption) { + $Configuration.PlaceHolderOption = [ordered] @{} + } + Write-TextWithTime -Text "Reading configuration" { if ($Settings) { $ExecutedSettings = & $Settings @@ -234,6 +242,12 @@ $Configuration.Options[$Key][$Entry] = $Setting.Options[$Key][$Entry] } } + } elseif ($Setting.Type -eq 'PlaceHolder') { + $Configuration.PlaceHolder.Add($Setting.Configuration) + } elseif ($Setting.Type -eq 'PlaceHolderOption') { + foreach ($Key in $Setting.PlaceHolderOption.Keys) { + $Configuration.PlaceHolderOption[$Key] = $Setting.PlaceHolderOption[$Key] + } } } } diff --git a/Private/Repair-CustomPlaceHolders.ps1 b/Private/Repair-CustomPlaceHolders.ps1 new file mode 100644 index 0000000..d9470ce --- /dev/null +++ b/Private/Repair-CustomPlaceHolders.ps1 @@ -0,0 +1,48 @@ +function Repair-CustomPlaceHolders { + [CmdletBinding()] + param( + [Parameter(Mandatory)][string] $Path, + [System.Collections.IDictionary] $Configuration + ) + + $ModuleName = $Configuration.Information.ModuleName + $ModuleVersion = $Configuration.Information.Manifest.ModuleVersion + $TagName = "v$($ModuleVersion)" + if ($Configuration.CurrentSettings.PreRelease) { + $ModuleVersionWithPreRelease = "$($ModuleVersion)-$($Configuration.CurrentSettings.PreRelease)" + $TagModuleVersionWithPreRelease = "v$($ModuleVersionWithPreRelease)" + } else { + $ModuleVersionWithPreRelease = $ModuleVersion + $TagModuleVersionWithPreRelease = "v$($ModuleVersion)" + } + + $BuiltinPlaceHolders = @( + @{ Find = '{ModuleName}'; Replace = $ModuleName } + @{ Find = ''; Replace = $ModuleName } + @{ Find = '{ModuleVersion}'; Replace = $ModuleVersion } + @{ Find = ''; Replace = $ModuleVersion } + @{ Find = '{ModuleVersionWithPreRelease}'; Replace = $ModuleVersionWithPreRelease } + @{ Find = ''; Replace = $ModuleVersionWithPreRelease } + @{ Find = '{TagModuleVersionWithPreRelease}'; Replace = $TagModuleVersionWithPreRelease } + @{ Find = ''; Replace = $TagModuleVersionWithPreRelease } + @{ Find = '{TagName}'; Replace = $TagName } + @{ Find = ''; Replace = $TagName } + ) + + Write-TextWithTime -Text "Replacing built-in and custom placeholders" -Color Yellow { + $PSM1Content = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 -ErrorAction Stop + + # Replace built-in placeholders + if ($Configuration.PlaceHolderOption.SkipBuiltinReplacements -ne $true) { + foreach ($PlaceHolder in $BuiltinPlaceHolders) { + $PSM1Content = $PSM1Content.Replace($PlaceHolder.Find, $PlaceHolder.Replace) + } + } + # Replace custom placeholders provided by the user + foreach ($PlaceHolder in $Configuration.PlaceHolders) { + $PSM1Content = $PSM1Content.Replace($PlaceHolder.Find, $PlaceHolder.Replace) + } + + Set-Content -LiteralPath $Path -Value $PSM1Content -Encoding UTF8 -ErrorAction Stop -Force + } -PreAppend Plus -SpacesBefore ' ' +} \ No newline at end of file diff --git a/Public/New-ConfigurationBuild.ps1 b/Public/New-ConfigurationBuild.ps1 index b8a818c..4d2d80f 100644 --- a/Public/New-ConfigurationBuild.ps1 +++ b/Public/New-ConfigurationBuild.ps1 @@ -113,6 +113,18 @@ .PARAMETER NETSearchClass Provide a name for class when using NETResolveBinaryConflicts or NETResolveBinaryConflictsName. By default it uses `$LibraryName.Initialize` however that may not be always the case + .PARAMETER SkipBuiltinReplacements + Skip builtin replacements option disables builtin replacements that are done by module builder. + This is useful if you use any of known replacements and you don't want them to be replaced by module builder. + This has to be used on the PSPublishModule by default, as it would break the module on publish. + + Current known replacements are: + - / {ModuleName} - the name of the module i.e PSPublishModule + - / {ModuleVersion} - the version of the module i.e 1.0.0 + - / {ModuleVersionWithPreRelease} - the version of the module with pre-release tag i.e 1.0.0-Preview1 + - / {TagModuleVersionWithPreRelease} - the version of the module with pre-release tag i.e v1.0.0-Preview1 + - / {TagName} - the name of the tag - i.e. v1.0.0 + .EXAMPLE $newConfigurationBuildSplat = @{ Enable = $true @@ -150,6 +162,7 @@ [switch] $UseWildcardForFunctions, [switch] $LocalVersioning, + [switch] $SkipBuiltinReplacements, [switch] $DoNotAttemptToFixRelativePaths, [string] $CertificateThumbprint, @@ -459,4 +472,16 @@ } } } + + # This logic disables built-in replacements that are done by module builder + # It's nessecary to prevent replacements that are done by module builder when module builder builds itself + # but can be used in other cases as well + if ($PSBoundParameters.ContainsKey('SkipBuiltinReplacements')) { + [ordered] @{ + Type = 'PlaceHolderOption' + PlaceHolderOption = [ordered]@{ + SkipBuiltinReplacements = $true + } + } + } } \ No newline at end of file diff --git a/Public/New-ConfigurationPlaceHolder.ps1 b/Public/New-ConfigurationPlaceHolder.ps1 new file mode 100644 index 0000000..d2721f9 --- /dev/null +++ b/Public/New-ConfigurationPlaceHolder.ps1 @@ -0,0 +1,24 @@ +function New-ConfigurationPlaceHolder { + [CmdletBinding(DefaultParameterSetName = 'FindAndReplace')] + param( + [Parameter(Mandatory, ParameterSetName = 'CustomReplacement')][System.Collections.IDictionary[]] $CustomReplacement, + [Parameter(Mandatory, ParameterSetName = 'FindAndReplace')][string] $Find, + [Parameter(Mandatory, ParameterSetName = 'FindAndReplace')][string] $Replace + ) + + foreach ($Replacement in $CustomReplacement) { + [ordered] @{ + Type = 'PlaceHolder' + Configuration = $Replacement + } + } + if ($PSBoundParameters.ContainsKey("Find") -and $PSBoundParameters.ContainsKey("Replace")) { + [ordered] @{ + Type = 'PlaceHolder' + Configuration = @{ + Find = $Find + Replace = $Replace + } + } + } +} \ No newline at end of file From d468acff1beb392ecb8a41c01a8d649e1dd5c777 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Wed, 14 Aug 2024 20:52:30 +0200 Subject: [PATCH 31/32] Disable it for main module builder --- Build/Build-Module.ps1 | 3 ++- Build/Build-ModuleLegacy.ps1 | 9 ++++++--- Build/Build-ModuleSimplified.ps1 | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Build/Build-Module.ps1 b/Build/Build-Module.ps1 index 372a2c8..dea68ec 100644 --- a/Build/Build-Module.ps1 +++ b/Build/Build-Module.ps1 @@ -6,7 +6,7 @@ # We need to rmeove library before we start, as it may contain old files, which will be in use once PSD1 loads # This is only required for PSPublisModule, as it's the only module that is being built by itself -Remove-Item -Path "C:\Support\GitHub\PSPublishModule\Lib" -Recurse -Force -ErrorAction Stop +Remove-Item -Path "C:\Support\GitHub\PSPublishModule\Lib" -Recurse -Force -ErrorAction SilentlyContinue Import-Module "$PSScriptRoot\..\PSPublishModule.psd1" -Force @@ -114,6 +114,7 @@ Build-Module -ModuleName 'PSPublishModule' { #CertificatePFXBase64 = $BasePfx #CertificatePFXPassword = "zGT" DoNotAttemptToFixRelativePaths = $false + SkipBuiltinReplacements = $true # required for Cmdlet/Alias functionality NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" diff --git a/Build/Build-ModuleLegacy.ps1 b/Build/Build-ModuleLegacy.ps1 index 9367bf6..483aaa0 100644 --- a/Build/Build-ModuleLegacy.ps1 +++ b/Build/Build-ModuleLegacy.ps1 @@ -2,7 +2,7 @@ Import-Module "$PSScriptRoot\..\PSPublishModule.psd1" -Force $Configuration = @{ - Information = @{ + Information = @{ ModuleName = 'PSPublishModule' #DirectoryProjects = 'C:\Support\GitHub' @@ -65,7 +65,7 @@ $Configuration = @{ DotNetFrameworkVersion = '4.5.2' } } - Options = @{ + Options = @{ Merge = @{ Sort = 'None' FormatCodePSM1 = @{ @@ -168,7 +168,10 @@ $Configuration = @{ CertificateThumbprint = '36A8A2D0E227D81A2D3B60DCE0CFCF23BEFC343B' } } - Steps = @{ + PlaceHolderOptions = @{ + SkipBuiltinReplacements = $true + } + Steps = @{ BuildLibraries = @{ Enable = $false # build once every time nuget gets updated Configuration = 'Release' diff --git a/Build/Build-ModuleSimplified.ps1 b/Build/Build-ModuleSimplified.ps1 index 92ef4fe..d71cb3b 100644 --- a/Build/Build-ModuleSimplified.ps1 +++ b/Build/Build-ModuleSimplified.ps1 @@ -7,7 +7,7 @@ # This version is for local building # We need to rmeove library before we start, as it may contain old files, which will be in use once PSD1 loads # This is only required for PSPublisModule, as it's the only module that is being built by itself -Remove-Item -Path "C:\Support\GitHub\PSPublishModule\Lib" -Recurse -Force -ErrorAction Stop +Remove-Item -Path "C:\Support\GitHub\PSPublishModule\Lib" -Recurse -Force -ErrorAction SilentlyContinue Import-Module "$PSScriptRoot\..\PSPublishModule.psd1" -Force @@ -114,6 +114,7 @@ Build-Module -ModuleName 'PSPublishModule' { #CertificatePFXBase64 = $BasePfx #CertificatePFXPassword = "zGT" DoNotAttemptToFixRelativePaths = $false + SkipBuiltinReplacements = $true # required for Cmdlet/Alias functionality NETProjectPath = "$PSScriptRoot\..\Sources\PSPublishModule\PSPublishModule" From 81c09a14f60be1c8e3e8a1a1d9a688dfbdabfc63 Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Wed, 14 Aug 2024 20:55:41 +0200 Subject: [PATCH 32/32] Add some docs --- Public/New-ConfigurationPlaceHolder.ps1 | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Public/New-ConfigurationPlaceHolder.ps1 b/Public/New-ConfigurationPlaceHolder.ps1 index d2721f9..6b8056a 100644 --- a/Public/New-ConfigurationPlaceHolder.ps1 +++ b/Public/New-ConfigurationPlaceHolder.ps1 @@ -1,4 +1,33 @@ function New-ConfigurationPlaceHolder { + <# + .SYNOPSIS + Command helping define custom placeholders replacing content within a script or module during the build process. + + .DESCRIPTION + Command helping define custom placeholders replacing content within a script or module during the build process. + It modifies only the content of the script or module (PSM1) and does not modify the sources. + + .PARAMETER CustomReplacement + Hashtable array with custom placeholders to replace. Each hashtable must contain two keys: Find and Replace. + + .PARAMETER Find + The string to find in the script or module content. + + .PARAMETER Replace + The string to replace the Find string in the script or module content. + + .EXAMPLE + New-ConfigurationPlaceHolder -Find '{CustomName}' -Replace 'SpecialCase' + + .EXAMPLE + New-ConfigurationPlaceHolder -CustomReplacement @( + @{ Find = '{CustomName}'; Replace = 'SpecialCase' } + @{ Find = '{CustomVersion}'; Replace = '1.0.0' } + ) + + .NOTES + General notes + #> [CmdletBinding(DefaultParameterSetName = 'FindAndReplace')] param( [Parameter(Mandatory, ParameterSetName = 'CustomReplacement')][System.Collections.IDictionary[]] $CustomReplacement,