Skip to content

psake 5.x spike: assess breakage under psake 5.0.4 (clean bump, no extras) #155

Description

@tablackburn

Part of #120 (Phase 2 — psake 5.x bump).

Question

What actually breaks under psake 5.x, and does the damage stay inside this repository or reach consumers?

Two psake files are in play and they carry very different risk:

  • psakeFile.ps1 at the repo root — this repo's own build. Breakage here is ours to fix.
  • PowerShellBuild/psakeFile.ps1 — the consumer-facing task definitions that every downstream build.ps1 dot-sources. Breakage here is a breaking change for everyone.

Scope

Keep this spike clean. requirements.psd1 currently pins psake = '4.9.0'; PSGallery is at 5.0.4. Bump the pin, run the build, record what happens.

Explicitly out of scope for the spike — these are what sank the previous attempt (#117, closed): task caching via Inputs/Outputs, LLM-mode Pester output, external PesterConfiguration file support, and the new public Format-PSBuildResult function. Those are 1.1.0 features, not a dependency bump.

Known reproducer

psake 5.0.4 installed side by side with the pinned 4.9.0 already shadows it on at least one maintainer machine and fails Manifest.tests.ps1. That failure is a starting point, not necessarily the whole story — confirm whether it is a genuine 5.x incompatibility or an artifact of two psake versions being visible at once.

Abort criterion (agreed before the spike, deliberately)

  • Migrate if the consumer-facing PowerShellBuild/psakeFile.ps1 survives 5.x with only mechanical changes.
  • Cut psake 5.x to 1.1.0 and ship 1.0.0 on psake 4.9.0 if 5.x forces a change to the task names or the $PSBPreference contract that consumers' build.ps1 files depend on.

This criterion is fixed in advance so the call is made on the findings rather than on sunk cost. Record which side the spike landed on, with evidence, as the resolution comment.

Done when

The findings are posted here and the migrate/cut call is made. Migration PR(s) are then created as follow-ups — their number and shape depend on what this spike finds, so they are deliberately not charted yet.

Activity

  1. added this to the v1.0.0 milestone on Aug 20, 2026
  2. self-assigned this
    on Aug 20, 2026
  3. tablackburn commented on Aug 20, 2026

    @tablackburn
    ContributorAuthor

    Spike result: MIGRATE

    Ran the clean bump (no #117 extras). Under the abort criterion agreed before the spike, the
    consumer-facing PowerShellBuild/psakeFile.ps1 survives psake 5.0.4 with zero changes —
    no task renames, no $PSBPreference change. So psake 5.x stays in v1.0.0.

    Method

    requirements.psd1 pins psake = '4.9.0'; PSGallery is at 5.0.4. Rather than swap the pin
    (which leaves the other version visible and shadowing), each run used an isolated module root
    prepended to PSModulePath, so exactly one psake version was resolvable — verified for the
    parent session and for Start-Job children, which matters because build.tests.ps1 and
    IBTasks.tests.ps1 spawn child builds.

    Run Result
    Full suite, psake 4.9.1 (baseline) 428 passed / 0 failed
    Full suite, psake 5.0.4 418 passed / 12 failed
    Full suite, psake 5.0.4 + the one-line fix below 428 passed / 0 failed
    Full suite, psake 4.9.1 + the same fix 428 passed / 0 failed (no regression)

    Checked against the official guide

    psake/psake docs/migration-v4-to-v5.md
    lists four breaking changes. None apply here:

    1. default.ps1 no longer auto-detected — we pass -buildFile ./psakeFile.ps1 explicitly
    2. psake.ps1 / psake.cmd removed — we already use Import-Module psake + Invoke-psake
    3. .NET Framework < 4.0 unsupported — no Framework calls anywhere
    4. $framework global removed — not used

    The only $framework / config_default matches in the repo are a local function parameter and
    a string literal inside Build/Convert-PSAke.ps1, an unrelated psake→Invoke-Build converter.
    OutputHandler / ColoredOutput / psake-config.ps1 are likewise unused.

    The guide also confirms two things the spike measured directly: $psake.build_success is
    explicitly still supported, and Invoke-psake now returns a PsakeBuildResult.

    API surface: a superset, no removals we touch

    Task keeps every v4 parameter and adds Definition; Properties adds Hashtable;
    FormatTaskName, Get-PSakeScriptTasks -BuildFile, and Invoke-Psake's
    -BuildFile/-TaskList/-NoLogo/-Parameters are unchanged. Verified working under 5.0.4:

    • $psake.context.currentTaskName — used at PowerShellBuild/psakeFile.ps1 lines 178, 201, 213
    • $psake.context.Peek().Tasks.Keys — line 381
    • $psake.build_success — build.ps1, and tests/TestModule/build.ps1 (the consumer simulation)

    Renamed but unused by this repo: Exec → Execute, -notr → -NoTimeReport, and
    Include's fileNamePathToInclude → Path/LiteralPath. (The Include = ... hits in
    psakeFile.ps1 and Build-PSBuildModule.ps1 are Copy-Item hashtable keys, not the psake
    keyword.)

    The 12 failures: one latent bug in our own test file

    All 12 came from a single container, tests/Manifest.tests.ps1, failing with:

    InvalidOperationException: A 'break' or 'continue' statement with a label that does not match
    any enclosing loop escaped from your code. (pester/Pester#2669)

    Root cause, isolated to a two-file reproducer: Set-BuildEnvironment -Force in BeforeAll.
    BuildHelpers' Get-BuildVariable uses break inside switch blocks; called from a Pester
    BeforeAll, that break unwinds out of the block. psake 4.9.x's call structure absorbs it,
    psake 5.0.4's (Invoke-BuildPlan.ps1, & $task.Action) does not, so Pester fails the container.

    Pester file with Set-BuildEnvironment in BeforeAll, run inside a psake task:
      psake 4.9.1 => Passed        psake 5.0.4 => Failed
    Same file without that call:
      psake 4.9.1 => Passed        psake 5.0.4 => Passed
    

    This is not documented in the upstream migration guide — worth reporting to BuildHelpers,
    and worth knowing for any consumer whose Pester tests call Set-BuildEnvironment.

    Fix is one line — build.ps1 already calls Set-BuildEnvironment -Force before Invoke-psake,
    so the call inside BeforeAll is redundant during a build:

    if (-not $env:BHProjectName) { Set-BuildEnvironment -Force }

    A dummy enclosing loop (foreach ($once in 1) { ... }) does not absorb it — tested and
    rejected. Committed on spike/psake-5x
    (5ec9a01) for the migration PR to pick up. Standalone Invoke-Pester with a cleared
    environment still passes 10/0, so the guard does not break out-of-build runs.

    One consumer-visible behavior change

    Invoke-psake now emits a PsakeBuildResult to the pipeline (Success, Duration, Tasks,
    ErrorMessage, ...) where v4 emitted nothing. Any consumer whose build.ps1 assigns or pipes
    the result of Invoke-psake gets an object that was not there before. Ours ignores it, so this
    is a migration-guide entry, not a code change.

    Correction to an earlier read: I initially measured the failing build exiting 0, which
    would have meant CI going green on a red build. That was an artifact of my harness — exit in
    a &-invoked script sets $LASTEXITCODE without terminating the wrapper. Measured directly,
    $psake.build_success is False and build.ps1's computed exit code is 1. No exit-code
    regression.
    Likewise Task default { <action> } is rejected by 4.9.1 too — a pre-existing
    quirk, not a psake 5 change.

    Not attempted, by design

    Task caching (Inputs/Outputs), LLM Pester output, external PesterConfiguration file
    support, and Format-PSBuildResult — the #117 extras. They stay out of v1.0.0.

    Follow-up

    Migration work is now specifiable and is filed as #161. Given the consumer-facing psakeFile
    needs no changes, it is a single small PR, not a chain.

  4. tablackburn commented on Aug 20, 2026

    @tablackburn
    ContributorAuthor

    Resolved: MIGRATE. psake 5.x stays in v1.0.0 — the consumer-facing psakeFile needs no changes. Findings and evidence in the resolution comment above; migration work filed as #161.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestwayfinder:taskWayfinder ticket: manual work that unblocks a decision

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions