From e829bc1b51b1adff0d99d3b3cb71ba679098c3b2 Mon Sep 17 00:00:00 2001 From: Trent Blackburn Date: Wed, 19 Aug 2026 23:24:15 -0400 Subject: [PATCH 1/3] test: Guard Set-BuildEnvironment against the psake 5.x break escape BuildHelpers' Get-BuildVariable uses `break` inside switch blocks. Called from a Pester BeforeAll, that break can unwind out of the block. psake 4.9.x absorbs it; psake 5.x does not, so Pester fails the whole Manifest.tests.ps1 container (pester/Pester#2669) and the suite drops 12 tests. build.ps1 already calls Set-BuildEnvironment -Force before Invoke-psake, so inside a build the call in BeforeAll is redundant. Guard it on $env:BHProjectName: a no-op during a build, still populated for standalone Invoke-Pester runs. Found while spiking psake 5.0.4 for psake/PowerShellBuild#155. Verified: full suite 428 passed / 0 failed under both psake 4.9.1 and 5.0.4, and 10/0 standalone with a cleared environment. Refs #155 (cherry picked from commit 5ec9a0105b063b3174eabd5dc6aad92df166a962) --- tests/Manifest.tests.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/Manifest.tests.ps1 b/tests/Manifest.tests.ps1 index b659654..4150b20 100644 --- a/tests/Manifest.tests.ps1 +++ b/tests/Manifest.tests.ps1 @@ -1,5 +1,10 @@ BeforeAll { - Set-BuildEnvironment -Force + # Only call Set-BuildEnvironment when the build variables are not already present. + # build.ps1 sets them before Invoke-psake, so inside a build this is a no-op; standalone + # Pester runs still get them. Calling it unconditionally lets an escaping 'break' from + # BuildHelpers' Get-BuildVariable switch blocks unwind out of this BeforeAll, which + # psake 4.9.x absorbs but psake 5.x does not -- Pester then fails the whole container. + if (-not $env:BHProjectName) { Set-BuildEnvironment -Force } $moduleName = $env:BHProjectName $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest $outputDir = Join-Path -Path $ENV:BHProjectPath -ChildPath 'Output' From 8282fe7b4eb65ce1d24a0cd65675c887ffccf218 Mon Sep 17 00:00:00 2001 From: Trent Blackburn Date: Wed, 19 Aug 2026 23:37:36 -0400 Subject: [PATCH 2/3] chore: Move the build toolchain to psake 5.0.4 Bumps psake from 4.9.0 to 5.0.4 in requirements.psd1. The module manifest still requires psake 4.9.0 or newer: PowerShellBuild's task definitions are unchanged and run on both majors, so consumers are not forced to upgrade. This mirrors the Pester decision in #120 -- raising a consumer-facing minimum for no functional gain is not worth the churn. The #155 spike verified the whole surface: task names, task dependencies, and the $PSBPreference contract are identical under 5.0.4, and none of the four breaking changes in psake's own v4-to-v5 guide apply here. Full suite is 428 passed / 0 failed under both psake 4.9.1 and 5.0.4. Two consumer-visible psake 5.x behaviors are documented in the migration guide: Invoke-psake now returns a PsakeBuildResult where v4 returned nothing, and Pester tests calling Set-BuildEnvironment in a BeforeAll can fail the whole container. The second is undocumented upstream and was found by the spike. Closes #161 --- CHANGELOG.md | 13 +++++ docs/migration-v0.8-to-v1.0.md | 97 +++++++++++++++++++++++++++++++++- requirements.psd1 | 2 +- 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd8089f..ade2b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- [**#161**](https://github.com/psake/PowerShellBuild/issues/161) + The build and test toolchain now uses psake **5.0.4** (previously + 4.9.0) in `requirements.psd1`. The module manifest still requires + `psake` 4.9.0 or newer — PowerShellBuild's task definitions are + unchanged and run on both majors, so consumers are not forced to + upgrade. If you do move to psake 5.x, two behaviors can affect your + build: `Invoke-psake` now returns a `PsakeBuildResult` where it + previously returned nothing, and Pester tests calling + `Set-BuildEnvironment` in a `BeforeAll` can fail the whole container. + See the + [v0.8 → v1.0 migration guide](docs/migration-v0.8-to-v1.0.md) for both, + with detection commands and fixes. + - [**#120**](https://github.com/psake/PowerShellBuild/issues/120) **Breaking:** the module manifest now requires PowerShell 5.1 or newer (`PowerShellVersion = '5.1'`, previously `'3.0'`) and declares diff --git a/docs/migration-v0.8-to-v1.0.md b/docs/migration-v0.8-to-v1.0.md index 04b0b6e..7f340b2 100644 --- a/docs/migration-v0.8-to-v1.0.md +++ b/docs/migration-v0.8-to-v1.0.md @@ -25,9 +25,13 @@ One line per break; follow the link for details and migration steps. - [Script analysis now actually fails the build](#script-analysis-now-actually-fails-the-build) — the `Analyze` task's severity threshold never fired in 0.8.x; a build that passed before may now correctly fail. +- [psake 5.x is now the tested build toolchain](#psake-5x-is-now-the-tested-build-toolchain) + — `Invoke-psake` returns a result object where it previously returned + nothing, and Pester tests that call `Set-BuildEnvironment` can start + failing. -> More entries will follow as the Phase 2 migrations to -> Microsoft.PowerShell.PlatyPS 1.x and psake 5.x land. +> More entries will follow as the Phase 2 migration to +> Microsoft.PowerShell.PlatyPS 1.x lands. ## AI-assisted migration @@ -156,6 +160,95 @@ now runs as documented instead of throwing. Tracked in issue #96. +### psake 5.x is now the tested build toolchain + +PowerShellBuild's own build and test toolchain moved from psake 4.9.0 to +**psake 5.0.4**. The module manifest still requires only `psake` **4.9.0 +or newer**, so this does not force you to upgrade: PowerShellBuild's +task definitions are unchanged and run on both majors. Task names, +task dependencies, and the `$PSBPreference` contract are all identical. + +Two things can still affect you if *you* move to psake 5.x. + +**1. `Invoke-psake` now returns a result object.** + +In psake 4.x, `Invoke-psake` wrote nothing to the pipeline. In 5.x it +returns a `PsakeBuildResult` (`Success`, `Duration`, `Tasks`, +`ErrorMessage`, and more). A `build.ps1` that captures or pipes the call +now receives an object where it previously received nothing. + +**Before (psake 4.x):** + + # $result is $null; the build's success is read from $psake + $result = Invoke-psake -buildFile ./psakeFile.ps1 -taskList $Task -nologo + exit ([int](-not $psake.build_success)) + +**After (psake 5.x):** + + # $result is a PsakeBuildResult + $result = Invoke-psake -buildFile ./psakeFile.ps1 -taskList $Task -nologo + exit ([int](-not $result.Success)) + +`$psake.build_success` is still set by psake 5.x, so the original form +keeps working — you only need to change anything if an unexpected object +on the pipeline breaks your script (for example, a `build.ps1` whose +output is consumed by another tool). + +**Detection:** look for an assignment or pipe on the `Invoke-psake` call +in your build file. + + Select-String -Path ./build.ps1 -Pattern '=\s*Invoke-psake|Invoke-psake.*\|' + +**2. Pester tests that call `Set-BuildEnvironment` can start failing.** + +If your Pester suite calls BuildHelpers' `Set-BuildEnvironment` inside a +`BeforeAll` block, and that suite runs inside a psake task, the whole +test container can fail on psake 5.x with: + + A 'break' or 'continue' statement with a label that does not match + any enclosing loop escaped from your code. + +`Set-BuildEnvironment` calls `Get-BuildVariable`, which uses `break` +inside `switch` blocks. That `break` can unwind out of the `BeforeAll`. +psake 4.9.x's task invocation absorbs it; psake 5.x's does not, so +Pester fails the container and every test in it +([pester/Pester#2669](https://github.com/pester/Pester/issues/2669)). + +The fix is to skip the call when the build variables are already set — +your build script normally sets them before invoking psake, which makes +the call redundant there while keeping standalone `Invoke-Pester` runs +working: + +**Before:** + + BeforeAll { + Set-BuildEnvironment -Force + } + +**After:** + + BeforeAll { + if (-not $env:BHProjectName) { Set-BuildEnvironment -Force } + } + +Wrapping the call in a dummy loop does **not** help — the `break` escapes +that too. + +**Detection:** + + Select-String -Path ./tests -Pattern 'Set-BuildEnvironment' -Recurse + +For psake's own list of v4 → v5 breaking changes (`default.ps1` +auto-detection, the `psake.ps1`/`psake.cmd` launchers, .NET Framework +below 4.0, and the `$framework` global), see +[`psake/psake docs/migration-v4-to-v5.md`](https://github.com/psake/psake/blob/main/docs/migration-v4-to-v5.md). +None of them affect PowerShellBuild itself. + +Tracked in issue +[#161](https://github.com/psake/PowerShellBuild/issues/161); spike +findings and evidence in +[#155](https://github.com/psake/PowerShellBuild/issues/155). + ## Adding an entry (for PR contributors) Every breaking-change PR that lands in v1.0.0 must add an entry here for diff --git a/requirements.psd1 b/requirements.psd1 index c7e5de0..3f920f5 100755 --- a/requirements.psd1 +++ b/requirements.psd1 @@ -9,7 +9,7 @@ SkipPublisherCheck = $true } } - psake = '4.9.0' + psake = '5.0.4' PSScriptAnalyzer = '1.25.0' InvokeBuild = '5.14.23' platyPS = '0.14.2' From f9dfeefb381c5a451053dc7e5250efe63dc9b4b9 Mon Sep 17 00:00:00 2001 From: Trent Blackburn Date: Thu, 20 Aug 2026 00:11:51 -0400 Subject: [PATCH 3/3] docs: Drop the changelog and migration-guide entries for the psake bump Neither belongs: this change is not user-facing. requirements.psd1 is this repository's own build toolchain, not anything a consumer touches, and the manifest's RequiredModules floor deliberately stays at psake 4.9.0 -- so upgrading PowerShellBuild 0.8.x to 1.0.0 does not move anyone to psake 5.x. The changelog records user-facing changes. The migration guide's stated scope is breaking changes plus behavioral changes that can require action when upgrading PowerShellBuild; the psake 5.x behaviors are triggered by upgrading psake, not PowerShellBuild, and psake's own v4-to-v5 guide is their proper home. If a later change raises the manifest floor to psake 5.x, that change adds the migration entry, where it will be correct. The Set-BuildEnvironment interaction stays documented where it is actionable: the comment in tests/Manifest.tests.ps1 and the spike findings in #155. --- CHANGELOG.md | 13 ----- docs/migration-v0.8-to-v1.0.md | 97 +--------------------------------- 2 files changed, 2 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ade2b4b..cd8089f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,19 +9,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- [**#161**](https://github.com/psake/PowerShellBuild/issues/161) - The build and test toolchain now uses psake **5.0.4** (previously - 4.9.0) in `requirements.psd1`. The module manifest still requires - `psake` 4.9.0 or newer — PowerShellBuild's task definitions are - unchanged and run on both majors, so consumers are not forced to - upgrade. If you do move to psake 5.x, two behaviors can affect your - build: `Invoke-psake` now returns a `PsakeBuildResult` where it - previously returned nothing, and Pester tests calling - `Set-BuildEnvironment` in a `BeforeAll` can fail the whole container. - See the - [v0.8 → v1.0 migration guide](docs/migration-v0.8-to-v1.0.md) for both, - with detection commands and fixes. - - [**#120**](https://github.com/psake/PowerShellBuild/issues/120) **Breaking:** the module manifest now requires PowerShell 5.1 or newer (`PowerShellVersion = '5.1'`, previously `'3.0'`) and declares diff --git a/docs/migration-v0.8-to-v1.0.md b/docs/migration-v0.8-to-v1.0.md index 7f340b2..04b0b6e 100644 --- a/docs/migration-v0.8-to-v1.0.md +++ b/docs/migration-v0.8-to-v1.0.md @@ -25,13 +25,9 @@ One line per break; follow the link for details and migration steps. - [Script analysis now actually fails the build](#script-analysis-now-actually-fails-the-build) — the `Analyze` task's severity threshold never fired in 0.8.x; a build that passed before may now correctly fail. -- [psake 5.x is now the tested build toolchain](#psake-5x-is-now-the-tested-build-toolchain) - — `Invoke-psake` returns a result object where it previously returned - nothing, and Pester tests that call `Set-BuildEnvironment` can start - failing. -> More entries will follow as the Phase 2 migration to -> Microsoft.PowerShell.PlatyPS 1.x lands. +> More entries will follow as the Phase 2 migrations to +> Microsoft.PowerShell.PlatyPS 1.x and psake 5.x land. ## AI-assisted migration @@ -160,95 +156,6 @@ now runs as documented instead of throwing. Tracked in issue #96. -### psake 5.x is now the tested build toolchain - -PowerShellBuild's own build and test toolchain moved from psake 4.9.0 to -**psake 5.0.4**. The module manifest still requires only `psake` **4.9.0 -or newer**, so this does not force you to upgrade: PowerShellBuild's -task definitions are unchanged and run on both majors. Task names, -task dependencies, and the `$PSBPreference` contract are all identical. - -Two things can still affect you if *you* move to psake 5.x. - -**1. `Invoke-psake` now returns a result object.** - -In psake 4.x, `Invoke-psake` wrote nothing to the pipeline. In 5.x it -returns a `PsakeBuildResult` (`Success`, `Duration`, `Tasks`, -`ErrorMessage`, and more). A `build.ps1` that captures or pipes the call -now receives an object where it previously received nothing. - -**Before (psake 4.x):** - - # $result is $null; the build's success is read from $psake - $result = Invoke-psake -buildFile ./psakeFile.ps1 -taskList $Task -nologo - exit ([int](-not $psake.build_success)) - -**After (psake 5.x):** - - # $result is a PsakeBuildResult - $result = Invoke-psake -buildFile ./psakeFile.ps1 -taskList $Task -nologo - exit ([int](-not $result.Success)) - -`$psake.build_success` is still set by psake 5.x, so the original form -keeps working — you only need to change anything if an unexpected object -on the pipeline breaks your script (for example, a `build.ps1` whose -output is consumed by another tool). - -**Detection:** look for an assignment or pipe on the `Invoke-psake` call -in your build file. - - Select-String -Path ./build.ps1 -Pattern '=\s*Invoke-psake|Invoke-psake.*\|' - -**2. Pester tests that call `Set-BuildEnvironment` can start failing.** - -If your Pester suite calls BuildHelpers' `Set-BuildEnvironment` inside a -`BeforeAll` block, and that suite runs inside a psake task, the whole -test container can fail on psake 5.x with: - - A 'break' or 'continue' statement with a label that does not match - any enclosing loop escaped from your code. - -`Set-BuildEnvironment` calls `Get-BuildVariable`, which uses `break` -inside `switch` blocks. That `break` can unwind out of the `BeforeAll`. -psake 4.9.x's task invocation absorbs it; psake 5.x's does not, so -Pester fails the container and every test in it -([pester/Pester#2669](https://github.com/pester/Pester/issues/2669)). - -The fix is to skip the call when the build variables are already set — -your build script normally sets them before invoking psake, which makes -the call redundant there while keeping standalone `Invoke-Pester` runs -working: - -**Before:** - - BeforeAll { - Set-BuildEnvironment -Force - } - -**After:** - - BeforeAll { - if (-not $env:BHProjectName) { Set-BuildEnvironment -Force } - } - -Wrapping the call in a dummy loop does **not** help — the `break` escapes -that too. - -**Detection:** - - Select-String -Path ./tests -Pattern 'Set-BuildEnvironment' -Recurse - -For psake's own list of v4 → v5 breaking changes (`default.ps1` -auto-detection, the `psake.ps1`/`psake.cmd` launchers, .NET Framework -below 4.0, and the `$framework` global), see -[`psake/psake docs/migration-v4-to-v5.md`](https://github.com/psake/psake/blob/main/docs/migration-v4-to-v5.md). -None of them affect PowerShellBuild itself. - -Tracked in issue -[#161](https://github.com/psake/PowerShellBuild/issues/161); spike -findings and evidence in -[#155](https://github.com/psake/PowerShellBuild/issues/155). - ## Adding an entry (for PR contributors) Every breaking-change PR that lands in v1.0.0 must add an entry here for