Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/PSModuleUtils.psd1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@{
RootModule = 'PSModuleUtils.psm1'
ModuleVersion = '4.2.0'
ModuleVersion = '4.2.1'
GUID = '3c63c38f-c32c-4837-a6fa-0b456f4099ce'
Author = ''
CompanyName = ''
Expand Down
29 changes: 23 additions & 6 deletions src/Private/Invoke-PSModuleAnalyzerCasingWorkaround.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)"
)
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/Public/Invoke-PSModuleAnalyzer.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
36 changes: 36 additions & 0 deletions tests/Invoke-PSModuleAnalyzerCasingWorkaround.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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'
@'
Expand Down