diff --git a/src/PSModuleUtils.psd1 b/src/PSModuleUtils.psd1 index 294c59a..1e85df0 100644 --- a/src/PSModuleUtils.psd1 +++ b/src/PSModuleUtils.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PSModuleUtils.psm1' - ModuleVersion = '4.2.0' + ModuleVersion = '4.2.1' GUID = '3c63c38f-c32c-4837-a6fa-0b456f4099ce' Author = '' CompanyName = '' diff --git a/src/Private/Invoke-PSModuleAnalyzerCasingWorkaround.ps1 b/src/Private/Invoke-PSModuleAnalyzerCasingWorkaround.ps1 index 40abdb6..93622eb 100644 --- a/src/Private/Invoke-PSModuleAnalyzerCasingWorkaround.ps1 +++ b/src/Private/Invoke-PSModuleAnalyzerCasingWorkaround.ps1 @@ -4,8 +4,10 @@ Internal: invokes PSScriptAnalyzer with a temporary command-casing workaround. .DESCRIPTION Runs PSUseCorrectCasing sequentially per file when command casing is enabled, then runs the remaining analysis -recursively. PSScriptAnalyzer 1.25.0 can fail while resolving command metadata during a recursive multi-file -command-casing analysis. +recursively. PSScriptAnalyzer 1.25.0 can fail while resolving command metadata during command-casing analysis; +splitting the run keeps one file's failure from silencing every other rule. A file whose casing analysis still +fails on its own is reported as a warning and skipped for that rule alone - the recursive pass analyzes it with +everything else. .PARAMETER Path The file or directory to analyze. @@ -118,10 +120,25 @@ function Invoke-PSModuleAnalyzerCasingWorkaround { Where-Object { $_.Extension -in '.ps1', '.psm1', '.psd1' } | ForEach-Object { $casingArguments.Path = $_.FullName - Invoke-ScriptAnalyzer @casingArguments | - ForEach-Object { - $casingResultCount++ - $_ + # The metadata-resolution failure this function exists for can + # also strike a single-file analysis, so one broken file must + # not end the run: the recursive pass below still analyzes it + # with every rule except casing, and the warning keeps the + # skip from being silent. + try { + Invoke-ScriptAnalyzer @casingArguments | + ForEach-Object { + $casingResultCount++ + $_ + } + } + catch { + $casingFailure = $_ + Write-Warning ( + "PSUseCorrectCasing analysis failed for '$($casingArguments.Path)' and was " + + 'skipped for that file; the remaining rules still analyze it. ' + + "Underlying error: $($casingFailure.Exception.Message)" + ) } } diff --git a/src/Public/Invoke-PSModuleAnalyzer.ps1 b/src/Public/Invoke-PSModuleAnalyzer.ps1 index 012a178..a49ac06 100644 --- a/src/Public/Invoke-PSModuleAnalyzer.ps1 +++ b/src/Public/Invoke-PSModuleAnalyzer.ps1 @@ -99,8 +99,9 @@ function Invoke-PSModuleAnalyzer { $scriptAnalyzerArgs.Fix = $true } - # After PSScriptAnalyzer fixes recursive PSUseCorrectCasing command metadata resolution, uncomment this call - # and remove the private workaround and its tests. + # After PSScriptAnalyzer fixes PSUseCorrectCasing command metadata resolution - it fails on some + # constructs even in a single-file analysis - uncomment this call and remove the private workaround + # and its tests. # Invoke-ScriptAnalyzer @scriptAnalyzerArgs Invoke-PSModuleAnalyzerCasingWorkaround @scriptAnalyzerArgs } diff --git a/tests/Invoke-PSModuleAnalyzerCasingWorkaround.Tests.ps1 b/tests/Invoke-PSModuleAnalyzerCasingWorkaround.Tests.ps1 index e6178e4..a7bb26c 100644 --- a/tests/Invoke-PSModuleAnalyzerCasingWorkaround.Tests.ps1 +++ b/tests/Invoke-PSModuleAnalyzerCasingWorkaround.Tests.ps1 @@ -111,6 +111,42 @@ Describe 'Invoke-PSModuleAnalyzerCasingWorkaround' -Tag 'Unit' { } } + It 'should warn and continue when casing analysis fails for one file' { + $fixtureDir = Join-Path -Path $TestDrive -ChildPath 'CasingCrashFixture' + $null = New-Item -ItemType Directory -Path $fixtureDir -Force + "function Get-Crashing { 'crashing' }" | Set-Content -Path "$fixtureDir/Crashing.ps1" + "function Get-Healthy { 'healthy' }" | Set-Content -Path "$fixtureDir/Healthy.ps1" + $scriptAnalyzerArguments = @{ + Path = $fixtureDir + Settings = $script:defaultSettingsPath + Recurse = $true + Severity = 'Information' + EnableExit = $false + ReportSummary = $true + ErrorAction = 'Stop' + } + Mock Invoke-ScriptAnalyzer {} + Mock Invoke-ScriptAnalyzer { + throw 'Unable to cast object of type FunctionMemberAst to type FunctionDefinitionAst.' + } -ParameterFilter { + $Settings.IncludeRules -contains 'PSUseCorrectCasing' -and $Path -like '*Crashing.ps1' + } + + $warnings = $null + Invoke-PSModuleAnalyzerCasingWorkaround @scriptAnalyzerArguments -WarningVariable warnings 3>$null + + $warnings | Should -HaveCount 1 + $warnings[0].Message | Should -BeLike '*Crashing.ps1*' + $warnings[0].Message | Should -BeLike '*FunctionMemberAst*' + Should -Invoke Invoke-ScriptAnalyzer -Exactly -Times 1 -ParameterFilter { + $Settings.IncludeRules -contains 'PSUseCorrectCasing' -and $Path -like '*Healthy.ps1' + } + Should -Invoke Invoke-ScriptAnalyzer -Exactly -Times 1 -ParameterFilter { + $Settings.Rules.PSUseCorrectCasing.Enable -eq $false -and + $Recurse -eq $true + } + } + It 'should invoke PSScriptAnalyzer unchanged when command casing does not need the workaround' { $settingsPath = Join-Path -Path $TestDrive -ChildPath 'DisabledCasingSettings.psd1' @'