Found while writing tests for the cloud-review fixes on #205. The first draft of a test passed for the wrong reason, which is how this surfaced.
What happens
CompileDirectories defaults to @() on the function:
# PowerShellBuild/Public/Build-PSBuildModule.ps1
[string[]]$CompileDirectories = @(),
With an empty array, the compile branch does:
$resolvedCompileDirectories = $CompileDirectories | ForEach-Object { [IO.Path]::Combine($Path, $_) }
$getChildItemSplat = @{
Path = $resolvedCompileDirectories # empty
Filter = "*.ps1"
Recurse = $true
...
}
$allScripts = Get-ChildItem @getChildItemSplat
Get-ChildItem -Path @() binds nothing, so it falls back to the current location and recurses. Every *.ps1 under the working directory is concatenated into the compiled .psm1.
Observed
Calling it from a checkout of this repository, with -Compile and no -CompileDirectories, produced a root module containing this repository's own test files:
$totalTabsCount++
}
}
$totalTabsCount | Should -Be 0
That is tests/Meta.tests.ps1 inside a built module. The module then fails to import — no valid module was found in any module directory — and the build reports success.
Why it is reachable
$PSBPreference.Build.CompileDirectories defaults to @("Enum", "Classes", "Private", "Public") in build.properties.ps1, and both task files forward it, so consumers going through the psake or Invoke-Build tasks never hit this. It bites anyone calling the public function directly — which is a supported way to use it, since it is exported and documented with its own .EXAMPLE.
The function's own default is therefore unusable: -Compile without -CompileDirectories never produces a correct module.
Why it is worth fixing rather than documenting
It fails silently and produces a plausible-looking artifact, which is the same family as #201 and the two defects the review of #205 just found. A caller gets a green build and a module full of whatever happened to be under their working directory.
Options
- Default the parameter to the same value
build.properties.ps1 uses — @("Enum", "Classes", "Private", "Public"). Makes the direct call behave like the task path. Changes behavior for anyone relying on the current fallback, which is hard to imagine being deliberate.
- Skip the concatenation when the list is empty, so
-Compile with no directories produces a root module holding only the header, the appended source .psm1, and the footer. Honest, and closer to what the parameter literally says.
- Reject the combination — throw when
-Compile is set and CompileDirectories is empty.
I lean 1, since it makes the function standalone-usable and matches what every caller in this repository already passes. It is a behavior change to a public function, so it wants a maintainer decision rather than being folded into an unrelated pull request.
Not introduced by #205; the default has been @() throughout. Surfaced there because the new tests are the first to call the function directly without the parameter.
Found while writing tests for the cloud-review fixes on #205. The first draft of a test passed for the wrong reason, which is how this surfaced.
What happens
CompileDirectoriesdefaults to@()on the function:With an empty array, the compile branch does:
Get-ChildItem -Path @()binds nothing, so it falls back to the current location and recurses. Every*.ps1under the working directory is concatenated into the compiled.psm1.Observed
Calling it from a checkout of this repository, with
-Compileand no-CompileDirectories, produced a root module containing this repository's own test files:That is
tests/Meta.tests.ps1inside a built module. The module then fails to import —no valid module was found in any module directory— and the build reports success.Why it is reachable
$PSBPreference.Build.CompileDirectoriesdefaults to@("Enum", "Classes", "Private", "Public")inbuild.properties.ps1, and both task files forward it, so consumers going through the psake or Invoke-Build tasks never hit this. It bites anyone calling the public function directly — which is a supported way to use it, since it is exported and documented with its own.EXAMPLE.The function's own default is therefore unusable:
-Compilewithout-CompileDirectoriesnever produces a correct module.Why it is worth fixing rather than documenting
It fails silently and produces a plausible-looking artifact, which is the same family as #201 and the two defects the review of #205 just found. A caller gets a green build and a module full of whatever happened to be under their working directory.
Options
build.properties.ps1uses —@("Enum", "Classes", "Private", "Public"). Makes the direct call behave like the task path. Changes behavior for anyone relying on the current fallback, which is hard to imagine being deliberate.-Compilewith no directories produces a root module holding only the header, the appended source.psm1, and the footer. Honest, and closer to what the parameter literally says.-Compileis set andCompileDirectoriesis empty.I lean 1, since it makes the function standalone-usable and matches what every caller in this repository already passes. It is a behavior change to a public function, so it wants a maintainer decision rather than being folded into an unrelated pull request.
Not introduced by #205; the default has been
@()throughout. Surfaced there because the new tests are the first to call the function directly without the parameter.