Skip to content

Build-PSBuildModule -Compile with no CompileDirectories compiles the working directory #206

Description

@tablackburn

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

  1. 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.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions