From da9153fd1cc0c69b6cb5e3d55b71597910e5f148 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Fri, 15 May 2026 23:29:34 -0400 Subject: [PATCH 1/2] Explicitly add a Windows PowerShell job to the build matrix. There are people still using this there. --- .github/workflows/build.yml | 60 +++++++++++++++--------- Source/Public/Invoke-ScriptGenerator.ps1 | 8 ++-- Tests/Integration/Parameters.Tests.ps1 | 11 ++--- Tests/Private/InitializeBuild.Tests.ps1 | 8 ++-- Tests/Public/Add-Parameter.Tests.ps1 | 12 ++--- Tests/Public/Merge-ScriptBlock.Tests.ps1 | 32 ++++++------- build.build.ps1 | 2 +- 7 files changed, 76 insertions(+), 57 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a30e165..d678fee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,6 +70,10 @@ jobs: fail-fast: false matrix: os: [ windows-latest, ubuntu-latest, macos-latest ] + shell: [ pwsh ] + include: + - os: windows-latest + shell: powershell steps: - name: Download build.requires.psd1 uses: actions/download-artifact@v8 @@ -79,39 +83,53 @@ jobs: uses: actions/download-artifact@v8 with: name: ModuleBuilder - path: output/ModuleBuilder # /home/runner/work/ModuleBuilder/ModuleBuilder/output/ModuleBuilder + path: Modules/ModuleBuilder # /home/runner/work/ModuleBuilder/ModuleBuilder/output/ModuleBuilder - name: Download Pester Tests uses: actions/download-artifact@v8 with: name: PesterTests path: PesterTests - - name: Install Output Modules + # Avoid installing ModuleBuilder with ModuleFast, so there's only one copy + - name: Remove ModuleBuilder from build.requires shell: pwsh run: | # PowerShell - # https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-powershell#powershell-module-locations - $ModuleDestination = if ($IsWindows) { - Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'PowerShell/Modules' - } else { - Join-Path $HOME '.local/share/powershell/Modules' - } - - Get-ChildItem -Directory output -OutVariable Modules - | Move-Item -Destination { Join-Path $ModuleDestination $_.Name } -Force - - Write-Host "Installing $($Modules -join ', ') to $ModuleDestination" - Get-ChildItem -Directory $ModuleDestination - Write-Host "PSModulePath:" - $Env:PSModulePath -split ([IO.Path]::PathSeparator) | Out-Host - - # Avoid installing ModuleBuilder with ModuleFast, so there's only one copy - @(Get-Content build.requires.psd1) - | Where { $_ -notmatch "ModuleBuilder"} - | Set-Content build.requires.psd1 + @(Get-Content build.requires.psd1).Where({ $_ -notmatch "ModuleBuilder"}) | Set-Content build.requires.psd1 - name: ⚡ Install Required Modules uses: JustinGrote/ModuleFast-action@v0.0.1 + env: + MODULEFAST_DESTINATION: ${{ github.workspace }}/Modules - name: Invoke-Pester + if: matrix.shell == 'powershell' + shell: powershell + env: + MODULEFAST_DESTINATION: ${{ github.workspace }}/Modules + run: | # PowerShell + $Env:PSModulePath = $Env:MODULEFAST_DESTINATION + [IO.Path]::PathSeparator + $Env:PSModulePath + + # For the cross-platform matrix we don't need to do coverage or anything complicated + $Result = Invoke-Pester . -PassThru + @( + "## Pester Tests for ${{ matrix.os }}" + "" + $Result.Duration.ToString() + "| Total | Passed | Failed |" + "|------:|-------:|-------:|" + "| $($Result.TotalCount) | $($Result.PassedCount) | $($Result.FailedCount) |" + "" + "| Duration | Total | Passed | Failed | Skipped | Name |" + "|---------:|------:|-------:|-------:|--------:|:-----|" + @($Result.Containers).ForEach{ + "| $($_.Duration) | $($_.TotalCount) | $($_.PassedCount) | $($_.FailedCount) | $($_.SkippedCount) | $($_.Name) |" + } + ) | Out-File -FilePath $env:GITHUB_STEP_SUMMARY + - name: Invoke-Pester + if: matrix.shell == 'pwsh' shell: pwsh + env: + MODULEFAST_DESTINATION: ${{ github.workspace }}/Modules run: | # PowerShell + $Env:PSModulePath = $Env:MODULEFAST_DESTINATION + [IO.Path]::PathSeparator + $Env:PSModulePath + # For the cross-platform matrix we don't need to do coverage or anything complicated $Result = Invoke-Pester . -PassThru @( diff --git a/Source/Public/Invoke-ScriptGenerator.ps1 b/Source/Public/Invoke-ScriptGenerator.ps1 index dc5d0d2..165f1bc 100644 --- a/Source/Public/Invoke-ScriptGenerator.ps1 +++ b/Source/Public/Invoke-ScriptGenerator.ps1 @@ -114,9 +114,11 @@ function Invoke-ScriptGenerator { } # Find that generator... - $GeneratorCmd = Get-Command -Name ${Generator} -ParameterType Ast -ErrorAction Ignore <# -CommandType Function #> - | Where-Object { $_.OutputType.Name -eq "TextReplacement" -or ($_.CommandType -eq "Alias" -and $_.Definition -like "PesterMock*" ) } - | Select-Object -First 1 + $GeneratorCmd = Get-Command -Name ${Generator} -ParameterType Ast -ErrorAction Ignore <# -CommandType Function #> | + Where-Object { + $_.OutputType.Name -eq "TextReplacement" -or ($_.CommandType -eq "Alias" -and $_.Definition -like "PesterMock*" ) + } | + Select-Object -First 1 if (-not $GeneratorCmd) { Write-Error "Generator missconfiguration. Unable to find Generator = '$Generator'" diff --git a/Tests/Integration/Parameters.Tests.ps1 b/Tests/Integration/Parameters.Tests.ps1 index 2cec9c9..8ffdfc6 100644 --- a/Tests/Integration/Parameters.Tests.ps1 +++ b/Tests/Integration/Parameters.Tests.ps1 @@ -11,15 +11,14 @@ Describe "Parameters" -Tag Integration { New-Item $PSScriptRoot/Result3/Parameters/3.0.0/DeleteMe.md -ItemType File -Force Write-Host "Module Under Test:" - Get-Command Build-Module - | Get-Module -Name { $_.Source } - | Get-Item - | Out-Host + Get-Command Build-Module | + Get-Module -Name { $_.Source } | + Get-Item | + Out-Host } It "Passthru is read from the build manifest" { - Build-Module (Convert-FolderSeparator "$PSScriptRoot/Parameters/build.psd1") -Verbose -OutVariable Output - | Out-Host + Build-Module (Convert-FolderSeparator "$PSScriptRoot/Parameters/build.psd1") -Verbose -OutVariable Output | Out-Host $Output | Should -Not -BeNullOrEmpty $Output.Path | Convert-FolderSeparator | Should -Be (Convert-FolderSeparator "$PSScriptRoot/Result3/Parameters/3.0.0/Parameters.psd1") diff --git a/Tests/Private/InitializeBuild.Tests.ps1 b/Tests/Private/InitializeBuild.Tests.ps1 index 5db014b..b03d024 100644 --- a/Tests/Private/InitializeBuild.Tests.ps1 +++ b/Tests/Private/InitializeBuild.Tests.ps1 @@ -52,11 +52,11 @@ Describe "InitializeBuild" { $Result.Result.Name | Should -Be "MyModule" $Result.Result.SourceDirectories | Should -Be @("Classes", "Private", "Public") - (Convert-FolderSeparator $Result.Result.ModuleBase) - | Should -Be (Convert-FolderSeparator "TestDrive:\Source") + (Convert-FolderSeparator $Result.Result.ModuleBase) | + Should -Be (Convert-FolderSeparator "TestDrive:\Source") - (Convert-FolderSeparator $Result.Result.SourcePath) - | Should -Be (Convert-FolderSeparator "TestDrive:\Source\MyModule.psd1") + (Convert-FolderSeparator $Result.Result.SourcePath) | + Should -Be (Convert-FolderSeparator "TestDrive:\Source\MyModule.psd1") } It "Returns default values from the Build Command" { diff --git a/Tests/Public/Add-Parameter.Tests.ps1 b/Tests/Public/Add-Parameter.Tests.ps1 index da3ac2b..13d7684 100644 --- a/Tests/Public/Add-Parameter.Tests.ps1 +++ b/Tests/Public/Add-Parameter.Tests.ps1 @@ -65,12 +65,12 @@ Describe "Add-Parameter" { $showDate | Should -Not -BeNullOrEmpty - $showDate.Body.ParamBlock.Parameters.Name.VariablePath.UserPath - | Should -Be @('Format', 'ForegroundColor', 'BackgroundColor') + $showDate.Body.ParamBlock.Parameters.Name.VariablePath.UserPath | + Should -Be @('Format', 'ForegroundColor', 'BackgroundColor') $showUserName | Should -Not -BeNullOrEmpty - $showUserName.Body.ParamBlock.Parameters.Name.VariablePath.UserPath - | Should -Be @('ForegroundColor', 'BackgroundColor') + $showUserName.Body.ParamBlock.Parameters.Name.VariablePath.UserPath | + Should -Be @('ForegroundColor', 'BackgroundColor') # Get-Date Should not be modified, since it does not match the FunctionName filter $getDate = $Ast.Find({ @@ -79,8 +79,8 @@ Describe "Add-Parameter" { $node.Name -eq 'Get-Date' }, $true) $getDate | Should -Not -BeNullOrEmpty - $getDate.Body.ParamBlock.Parameters.Name.VariablePath.UserPath - | Should -Be @('Format') + $getDate.Body.ParamBlock.Parameters.Name.VariablePath.UserPath | + Should -Be @('Format') } } } diff --git a/Tests/Public/Merge-ScriptBlock.Tests.ps1 b/Tests/Public/Merge-ScriptBlock.Tests.ps1 index d15d11f..6e2941f 100644 --- a/Tests/Public/Merge-ScriptBlock.Tests.ps1 +++ b/Tests/Public/Merge-ScriptBlock.Tests.ps1 @@ -60,24 +60,24 @@ Describe "Merge-ScriptBlock" { $showDate | Should -Not -BeNullOrEmpty - $showDate.Body.EndBlock -split "`n" - | ForEach-Object { $_.Trim() } - | Select-Object -Skip 1 - | Select-Object -First 3 - | Should -Be @( - "`$ForegroundColor.ToVt() + `$BackgroundColor.ToVt(`$true) + (" - "Get-Date -Format `$Format" - ") + `"``e[0m`"" - ) + $showDate.Body.EndBlock -split "`n" | + ForEach-Object { $_.Trim() } | + Select-Object -Skip 1 | + Select-Object -First 3 | + Should -Be @( + "`$ForegroundColor.ToVt() + `$BackgroundColor.ToVt(`$true) + (" + "Get-Date -Format `$Format" + ") + `"``e[0m`"" + ) $showUserName | Should -Not -BeNullOrEmpty - $showUserName.Body.EndBlock -split "`n" - | ForEach-Object { $_.Trim() } - | Select-Object -Skip 1 - | Select-Object -First 3 - | Should -Be @( - "`$ForegroundColor.ToVt() + `$BackgroundColor.ToVt(`$true) + (" - "[Environment]::UserName" + $showUserName.Body.EndBlock -split "`n" | + ForEach-Object { $_.Trim() } | + Select-Object -Skip 1 | + Select-Object -First 3 | + Should -Be @( + "`$ForegroundColor.ToVt() + `$BackgroundColor.ToVt(`$true) + (" + "[Environment]::UserName" ") + `"``e[0m`"" ) } diff --git a/build.build.ps1 b/build.build.ps1 index 053c3ec..53e6e49 100644 --- a/build.build.ps1 +++ b/build.build.ps1 @@ -19,7 +19,7 @@ Write-Information "$($PSStyle.Foreground.BrightMagenta)build.build.ps1$($PSStyle ## Self-contained build script - can be invoked directly or via Invoke-Build if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { - . (Convert-Path ../../[tT]asks/scripts/Invoke-Build.ps1) -File $MyInvocation.MyCommand.Path @PSBoundParameters -Result Result + & (Convert-Path ../../[tT]asks/scripts/Invoke-Build.ps1) -File $MyInvocation.MyCommand.Path @PSBoundParameters -Result Result if ($Result.Error) { $Error[-1].ScriptStackTrace | Out-Host From b8a8f3cd5978a8f668889283267ba5758f5ab011 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Sat, 16 May 2026 17:13:37 -0400 Subject: [PATCH 2/2] Fix Windows PowerShell regressions in tests. --- Tests/Private/CompressToBase64.Tests.ps1 | 8 ++++---- Tests/Private/ConvertToAst.Tests.ps1 | 2 +- .../Public/ConvertTo-SourceLineNumber.Tests.ps1 | 17 +++++++---------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Tests/Private/CompressToBase64.Tests.ps1 b/Tests/Private/CompressToBase64.Tests.ps1 index 898d81f..b11bcc0 100644 --- a/Tests/Private/CompressToBase64.Tests.ps1 +++ b/Tests/Private/CompressToBase64.Tests.ps1 @@ -4,7 +4,7 @@ Describe "CompressToBase64" { Context "It compresses and encodes a file for embedding into a script" { BeforeAll { $Base64 = InModuleScope ModuleBuilder { - CompressToBase64 $PSCommandPath + CompressToBase64 (Join-Path $PSScriptRoot CompressToBase64.Tests.ps1) } } @@ -22,14 +22,14 @@ Describe "CompressToBase64" { $OutputStream.Seek(0, "Begin") $Source = [System.IO.StreamReader]::new($OutputStream, $true).ReadToEnd() - $Source | Should -Be (Get-Content $PSCommandPath -Raw) + $Source | Should -Be (Get-Content (Join-Path $PSScriptRoot CompressToBase64.Tests.ps1) -Raw) } } Context "It wraps the Base64 encoded content in the specified command" { BeforeAll { $Base64 = InModuleScope ModuleBuilder { - CompressToBase64 $PSCommandPath -ExpandScriptName ImportBase64Module + CompressToBase64 (Join-Path $PSScriptRoot CompressToBase64.Tests.ps1) -ExpandScriptName ImportBase64Module } } @@ -49,7 +49,7 @@ Describe "CompressToBase64" { Context "It wraps the Base64 encoded content in the specified scriptblock" { BeforeAll { $Base64 = InModuleScope ModuleBuilder { - Get-ChildItem $PSCommandPath | CompressToBase64 -ExpandScript { ImportBase64Module } + Get-ChildItem (Join-Path $PSScriptRoot CompressToBase64.Tests.ps1) | CompressToBase64 -ExpandScript { ImportBase64Module } } } diff --git a/Tests/Private/ConvertToAst.Tests.ps1 b/Tests/Private/ConvertToAst.Tests.ps1 index a71589c..92b668b 100644 --- a/Tests/Private/ConvertToAst.Tests.ps1 +++ b/Tests/Private/ConvertToAst.Tests.ps1 @@ -4,7 +4,7 @@ Describe "ConvertToAst" { Context "It returns a ParseResult for file paths" { BeforeAll { $ParseResult = InModuleScope ModuleBuilder { - ConvertToAst -Code $PSCommandPath + ConvertToAst -Code (Join-Path $PSScriptRoot ConvertToAst.Tests.ps1) } } diff --git a/Tests/Public/ConvertTo-SourceLineNumber.Tests.ps1 b/Tests/Public/ConvertTo-SourceLineNumber.Tests.ps1 index 8f432d9..4e1629e 100644 --- a/Tests/Public/ConvertTo-SourceLineNumber.Tests.ps1 +++ b/Tests/Public/ConvertTo-SourceLineNumber.Tests.ps1 @@ -35,29 +35,26 @@ Describe "ConvertTo-SourceLineNumber" { } It "Should throw if the SourceFile doesn't exist" { - { Convert-LineNumber -SourceFile TestDrive:/NoSuchFile -SourceLineNumber 10 } | - Should -Throw "'TestDrive:/NoSuchFile' does not exist" + { Convert-ToSourceLineNumber -SourceFile TestDrive:${\}NoSuchFile -SourceLineNumber 10 } | + Should -Throw "'TestDrive:${\}NoSuchFile' does not exist" } It 'Should work with an error PositionMessage' { $line = Select-String -Path $Convert_LineNumber_ModulePath 'function Set-Source {' | ForEach-Object LineNumber - $SourceLocation = "At ${Convert_LineNumber_ModulePath}:$line char:17" | Convert-LineNumber - # This test is assuming you built the code on Windows. Should Convert-LineNumber convert the path? + $SourceLocation = "At ${Convert_LineNumber_ModulePath}:$line char:17" | Convert-ToSourceLineNumber $SourceLocation.SourceFile | Should -Be ".${\}Public${\}Set-Source.ps1" $SourceLocation.SourceLineNumber | Should -Be 1 } It 'Should work with ScriptStackTrace messages' { - $SourceFile = Join-Path $Convert_LineNumber_ModuleSource Public/Set-Source.ps1 | Convert-Path - - $outputLine = Select-String -Path $Convert_LineNumber_ModulePath "sto͞o′pĭd" | % LineNumber - $sourceLine = Select-String -Path $SourceFile "sto͞o′pĭd" | % LineNumber + $SourceFile = Join-Path $Convert_LineNumber_ModuleSource (Join-Path Public Set-Source.ps1) | Convert-Path - $SourceLocation = "At Set-Source, ${Convert_LineNumber_ModulePath}: line $outputLine" | Convert-LineNumber + $outputLine = Select-String -Path $Convert_LineNumber_ModulePath "sto͞o′pĭd" | ForEach-Object LineNumber + $sourceLine = Select-String -Path $SourceFile "sto͞o′pĭd" | ForEach-Object LineNumber - # This test is assuming you built the code on Windows. Should Convert-LineNumber convert the path? + $SourceLocation = "At Set-Source, ${Convert_LineNumber_ModulePath}: line $outputLine" | Convert-ToSourceLineNumber $SourceLocation.SourceFile | Should -Be ".${\}Public${\}Set-Source.ps1" $SourceLocation.SourceLineNumber | Should -Be $sourceLine }