You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implements Phase 3d from #487: Invoke-psake Analyze now uses an explicit organization ruleset and completes with zero findings.
Structural
Added PSScriptAnalyzerSettings.psd1 at repo root — full default ruleset (IncludeDefaultRules), only two repo-wide exclusions (PSAvoidUsingWriteHost for console UX, PSUseToExportFieldsInManifest for the manifest's VariablesToExport = '*'). No Severity key — confirmed empirically that setting one silently drops ParseError-severity records with no trace of what was hidden.
Wired the settings file into psakeFile.ps1, and added a PrepareAnalysis task that puts the staged module on PSModulePath before analysis runs. Without it, every koan's using module PSKoans fails to resolve — which not only raises a ModuleNotFoundDuringParse error per file, but silently suppresses every other rule that needs symbol resolution (aliases, output types, etc.) for that file.
Koan false positives (113 → 0)
~41 PSUseDeclaredVarsMoreThanAssignments false positives fixed with $script:, scoping BeforeAll/BeforeEach fixture variables so PSScriptAnalyzer's per-scriptblock analysis can see they're used by sibling It blocks (same root cause as the existing tests/*.Tests.ps1 fixes).
~15 justified SuppressMessageAttribute annotations (each with a specific comment) for false positives that can't be fixed without altering an exercise: values consumed only by an unsolved __/____ blank, AD -Filter strings expanded by the ActiveDirectory provider (not PowerShell interpolation), values read only inside nested closures (Mock -MockWith, Select-Object calculated properties, ForEach-Object) PSScriptAnalyzer doesn't trace, switches used only as parameter-set discriminators, and zero-side-effect example functions flagged for ShouldProcess/SingularNouns on verb/name alone.
42 lines of trailing whitespace trimmed (mechanical).
No koan blanks, exercise structure, or pedagogical content were touched.
Real bugs found along the way
AboutQueryingDatabases.Koans.ps1: Out-File - FilePath ... (stray space) broke positional binding and errored at runtime — fixed to -FilePath.
AddPesterAssertionOperator.ps1: replaced the alias Add-AssertionOperator with Add-ShouldOperator, and replaced the empty-catch registration guard with an explicit Get-ShouldOperator existence check (no more silently swallowing unexpected errors).
AboutLists.Koans.ps1: renamed the automatic-variable-shadowing $_ scriptblock parameter to $Item (no suppression attribute is discoverable on a bare, non-function scriptblock's own param() for this rule).
Production code (37 → 0)
Real fixes: correct [OutputType] on Invoke-Koan/New-KoanRunspace; Set-PSKoanSetting body wrapped in process {} so it actually honors its own already-declared ValueFromPipeline parameters.
.\build.ps1 -Task Analyze → zero findings (confirmed twice, fresh process).
Full test suite: 729 passed / 6 failed / 8 skipped, identical before and after (the 6 failures are pre-existing, unrelated Register-Advice.Tests.ps1$PROFILE-binding errors, environment-specific).
Implements Phase 3d analysis from #487.
- Add PSScriptAnalyzerSettings.psd1 at the repo root: runs the full
PSScriptAnalyzer default ruleset (IncludeDefaultRules), with only two
repo-wide exclusions (PSAvoidUsingWriteHost for the console UX,
PSUseToExportFieldsInManifest for the manifest's VariablesToExport = '*').
Deliberately omits a Severity key, since setting one silently drops
ParseError-severity records with no way to see what was suppressed.
- Wire the settings file into psakeFile.ps1 via
$PSBPreference.Test.ScriptAnalysis.SettingsPath, and add a
PrepareAnalysis task that puts the staged module on PSModulePath before
analysis runs. Without it, every koan's `using module PSKoans` fails to
resolve, which both raises a ModuleNotFoundDuringParse error per file and
silently suppresses every other rule that depends on symbol resolution
(aliases, output types, etc.) for that file.
- Fix ~41 PSUseDeclaredVarsMoreThanAssignments false positives across koan
files by scoping BeforeAll/BeforeEach fixture variables with $script:,
mirroring the same fix already applied to tests/*.Tests.ps1. These are
genuine cross-block Pester usages (BeforeAll -> sibling It) that
PSScriptAnalyzer cannot trace across scriptblock boundaries.
- Add justified SuppressMessageAttribute annotations (each with a specific
comment) for genuine false positives that can't be fixed without altering
the exercise: values consumed only by an unsolved `__`/`____` blank,
ActiveDirectory -Filter strings expanded by the AD provider rather than
PowerShell interpolation, values read only inside nested closures
(Mock -MockWith, Select-Object calculated properties, ForEach-Object)
that PSScriptAnalyzer doesn't trace, switches used only as parameter-set
discriminators, and zero-side-effect factory/example functions flagged
for ShouldProcess/SingularNouns based on verb/name alone.
- Fix two real bugs found along the way:
- AboutQueryingDatabases.Koans.ps1: `Out-File - FilePath ...` (stray
space) was breaking positional parameter binding and erroring at
runtime; corrected to `-FilePath`.
- AddPesterAssertionOperator.ps1: replaced the alias `Add-AssertionOperator`
with `Add-ShouldOperator`, and replaced the try/empty-catch registration
guard with an explicit Get-ShouldOperator existence check.
- Real code fixes in Public/Private module functions: correct [OutputType]
declarations on Invoke-Koan and New-KoanRunspace, and wrap
Set-PSKoanSetting's body in a process {} block so it actually honors its
own already-declared ValueFromPipeline parameters.
- Rename the automatic-variable-shadowing $_ scriptblock parameter to
$Item in AboutLists.Koans.ps1 (both filters, for consistency); no
suppression attribute is discoverable on a bare (non-function)
scriptblock's own param block for this rule.
- Trim trailing whitespace (42 lines, mechanical) across several koan
files.
No koan blanks, exercise structure, or pedagogical content were modified.
`.\build.ps1 -Task Analyze` now completes with zero findings; full test
suite unchanged at 729 passed / 6 pre-existing unrelated failures
(Register-Advice, environment-specific $PROFILE binding) / 8 skipped.
Results for commit 4668ab7. ± Comparison against base commit b99ac78.
This pull request removes 222 and adds 240 tests. Note that renamed tests count towards both.
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Init/AddPesterAssertionOperator.ps1'; Length=755; Name='AddPesterAssertionOperator.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Init/RegisterArgumentCompleters.ps1'; Length=2613; Name='RegisterArgumentCompleters.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/Invoke-Koan.ps1'; Length=3119; Name='Invoke-Koan.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/Measure-Koan.ps1'; Length=2628; Name='Measure-Koan.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/New-KoanRunspace.ps1'; Length=1440; Name='New-KoanRunspace.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/New-PSKoanErrorRecord.ps1'; Length=3132; Name='New-PSKoanErrorRecord.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Public/Get-Blank.ps1'; Length=1871; Name='Get-Blank.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Public/Get-PSKoan.ps1'; Length=5445; Name='Get-PSKoan.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Public/Set-PSKoanSetting.ps1'; Length=3457; Name='Set-PSKoanSetting.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Public/Show-Karma.ps1'; Length=8914; Name='Show-Karma.ps1'}
…
/Users/runner/work/PSKoans/PSKoans/tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 ‑ Set-PSKoanSetting.Settings file Exists.Setting values with -Name and -Value.should add a new setting via pipeline (PSCustomObject with Name/Value): Editor = code-insiders
/Users/runner/work/PSKoans/PSKoans/tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 ‑ Set-PSKoanSetting.Settings file Exists.Setting values with -Name and -Value.should add a new setting via pipeline (PSCustomObject with Name/Value): LibraryFolder = TestDrive:/PSKoans
/Users/runner/work/PSKoans/PSKoans/tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 ‑ Set-PSKoanSetting.Settings file Exists.Setting values with -Name and -Value.should add a new setting via pipeline (PSCustomObject with Name/Value): TestSetting1 = TestValue1
/Users/runner/work/PSKoans/PSKoans/tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 ‑ Set-PSKoanSetting.Settings file Exists.Setting values with -Settings Hashtable.should add or overwrite multiple new settings via pipeline (hashtable)
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Init/AddPesterAssertionOperator.ps1'; Length=1065; Name='AddPesterAssertionOperator.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Init/RegisterArgumentCompleters.ps1'; Length=3899; Name='RegisterArgumentCompleters.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/Invoke-Koan.ps1'; Length=3166; Name='Invoke-Koan.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/Measure-Koan.ps1'; Length=2960; Name='Measure-Koan.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/New-KoanRunspace.ps1'; Length=1747; Name='New-KoanRunspace.ps1'}
/Users/runner/work/PSKoans/PSKoans/tests/ModuleValidation.Tests.ps1 ‑ Static Analysis: Module & Repository Files.Repository Code.has exactly one line feed at EOF in IO.FileInfo{FullName='/Users/runner/work/PSKoans/PSKoans/Output/PSKoans/0.67.0/Private/New-PSKoanErrorRecord.ps1'; Length=3374; Name='New-PSKoanErrorRecord.ps1'}
…
♻️ This comment has been updated with latest results.
Adds an explicit PSScriptAnalyzer ruleset and staged-module preparation so Invoke-psake Analyze reaches zero findings, with analyzer cleanup and runtime fixes across tests, production code, and koans.
Changes:
Adds analyzer settings and build integration.
Scopes fixtures and documents justified suppressions.
Fixes pipeline handling, output metadata, assertion registration, and koan syntax.
Removes incidental whitespace and analyzer findings.
- RegisterArgumentCompleters.ps1: replace generic "false positive"
suppression justifications with the actual reason -- Command,
Parameter, CommandAst, and FakeBoundParams are required positional
slots in the host-supplied Register-ArgumentCompleter callback
signature that these completers don't use.
- Set-PSKoanSetting.ps1: add regression tests piping supported input
forms (PSCustomObject with Name/Value, hashtable for Settings) as
requested. Writing the hashtable-pipe test surfaced a real binding
bug: DefaultParameterSetName 'Single' made pipeline binding prefer
the ValueFromPipelineByPropertyName 'Single' set as a tiebreaker,
which a bare hashtable can never satisfy, so piped hashtables
silently failed to bind. Fixed by changing the default parameter
set to 'Multiple'.
Removing the assignment also removes this test's only ConvertFrom-Csv call, so the test now checks only that its fixture is a string and no longer exercises the behavior named by the test. Keep an invocation here (discarding its result if necessary) so this koan still demonstrates that ConvertFrom-Csv accepts string data.
EndInvoke output type conflicts with collection metadata
PSKoans/Private/Invoke-Koan.ps1:21
EndInvoke() returns a collection internally, but line 88 writes that collection to PowerShell's success stream, which enumerates it. Callers therefore receive the contained Pester result objects rather than a PSDataCollection; declaring the collection type gives consumers inaccurate command metadata. Keep the emitted item type and suppress the analyzer false positive with this runtime behavior documented.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #487.
What
Implements Phase 3d from #487:
Invoke-psake Analyzenow uses an explicit organization ruleset and completes with zero findings.Structural
PSScriptAnalyzerSettings.psd1at repo root — full default ruleset (IncludeDefaultRules), only two repo-wide exclusions (PSAvoidUsingWriteHostfor console UX,PSUseToExportFieldsInManifestfor the manifest'sVariablesToExport = '*'). NoSeveritykey — confirmed empirically that setting one silently dropsParseError-severity records with no trace of what was hidden.psakeFile.ps1, and added aPrepareAnalysistask that puts the staged module onPSModulePathbefore analysis runs. Without it, every koan'susing module PSKoansfails to resolve — which not only raises aModuleNotFoundDuringParseerror per file, but silently suppresses every other rule that needs symbol resolution (aliases, output types, etc.) for that file.Koan false positives (113 → 0)
PSUseDeclaredVarsMoreThanAssignmentsfalse positives fixed with$script:, scopingBeforeAll/BeforeEachfixture variables so PSScriptAnalyzer's per-scriptblock analysis can see they're used by siblingItblocks (same root cause as the existingtests/*.Tests.ps1fixes).SuppressMessageAttributeannotations (each with a specific comment) for false positives that can't be fixed without altering an exercise: values consumed only by an unsolved__/____blank, AD-Filterstrings expanded by the ActiveDirectory provider (not PowerShell interpolation), values read only inside nested closures (Mock -MockWith,Select-Objectcalculated properties,ForEach-Object) PSScriptAnalyzer doesn't trace, switches used only as parameter-set discriminators, and zero-side-effect example functions flagged forShouldProcess/SingularNounson verb/name alone.Real bugs found along the way
AboutQueryingDatabases.Koans.ps1:Out-File - FilePath ...(stray space) broke positional binding and errored at runtime — fixed to-FilePath.AddPesterAssertionOperator.ps1: replaced the aliasAdd-AssertionOperatorwithAdd-ShouldOperator, and replaced the empty-catch registration guard with an explicitGet-ShouldOperatorexistence check (no more silently swallowing unexpected errors).AboutLists.Koans.ps1: renamed the automatic-variable-shadowing$_scriptblock parameter to$Item(no suppression attribute is discoverable on a bare, non-function scriptblock's ownparam()for this rule).Production code (37 → 0)
[OutputType]onInvoke-Koan/New-KoanRunspace;Set-PSKoanSettingbody wrapped inprocess {}so it actually honors its own already-declaredValueFromPipelineparameters.ArgumentCompletersignature params (mirrorsbuild.ps1's existing convention), values read only inside nested closures, parameter-set-discriminator switches, and zero-side-effect factory functions.Verification
.\build.ps1 -Task Analyze→ zero findings (confirmed twice, fresh process).Register-Advice.Tests.ps1$PROFILE-binding errors, environment-specific).