From 536e569316281db733506dfb5ff313f478139290 Mon Sep 17 00:00:00 2001 From: Cory Douthat <80133232+corydouthat-sq@users.noreply.github.com> Date: Tue, 15 Sep 2026 02:19:48 -0400 Subject: [PATCH 1/2] fix(windows): keep the Windows script tests runnable on Windows PowerShell 5.1 Every Windows recipe in the justfile runs powershell.exe, WindowsDev.psm1 is written to stay 5.1-compatible, and docs/windows-onboarding.md tells contributors to use normal PowerShell. Yet `just test-windows-dev` failed on that host: Test-WindowsDev.ps1 used the PowerShell 6+ semver type accelerator, which Windows PowerShell 5.1 does not have, and nothing in CI ran the script tests, so the break was invisible upstream. Keep the two ordering assertions where SemanticVersion exists and skip them (visibly) where it does not; they check the type Tauri's updater ordering relies on, not Berd code. Add a scan that fails the suite if any Windows script picks up a 6+ only construct again, run `just test-windows-dev` in the rust-windows CI job under Windows PowerShell, and state the host contract in the onboarding doc. The alternative is to require PowerShell 7 everywhere (install it in the bootstrap, `#Requires -Version 7`, switch the recipes to pwsh); this change keeps the smaller, already-documented contract instead. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 6 ++++++ docs/windows-onboarding.md | 6 +++++- scripts/windows/Test-WindowsDev.ps1 | 30 +++++++++++++++++++++++++---- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4e8e116f..62086121a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,6 +137,12 @@ jobs: shell: powershell run: just ci-windows + # Windows PowerShell 5.1 is the host the justfile pins for every Windows + # recipe; running the script tests under it keeps them 5.1-compatible. + - name: Run Windows script tests under Windows PowerShell 5.1 + shell: powershell + run: just test-windows-dev + transcript-virtualization: name: Transcript virtualization needs: frontend diff --git a/docs/windows-onboarding.md b/docs/windows-onboarding.md index f8fbdf186..1b172215e 100644 --- a/docs/windows-onboarding.md +++ b/docs/windows-onboarding.md @@ -17,6 +17,9 @@ Native Berd provider sign-in is intentionally deferred on Windows. The app and Use normal PowerShell. You do not need a Visual Studio Developer PowerShell; the Windows scripts load the Visual Studio build environment before running Cargo. +Windows PowerShell 5.1 (the `powershell.exe` every `just` Windows recipe runs) +and PowerShell 7 are both supported; the scripts avoid PowerShell 6+ only +constructs and `just test-windows-dev` checks that. The repeatable entrypoint is `just`, but a completely fresh Windows machine still needs two seed steps: @@ -154,7 +157,8 @@ just test-windows-dev `tauri-check-windows` runs Windows-native Rust/Tauri checks with external sidecars disabled. `test-windows-dev` covers focused Windows script path, stamp, -and cleanup helpers. +and cleanup helpers; CI runs it under Windows PowerShell 5.1 on every pull +request. ## Cleanup And Reset diff --git a/scripts/windows/Test-WindowsDev.ps1 b/scripts/windows/Test-WindowsDev.ps1 index b4bdb38e9..c1506f73f 100644 --- a/scripts/windows/Test-WindowsDev.ps1 +++ b/scripts/windows/Test-WindowsDev.ps1 @@ -159,10 +159,32 @@ try { ($bundleScript -notmatch 'NotePropertyName version -NotePropertyValue \$resolvedVersion\.Version') $true Assert-Equal "Tauri config preserves prerelease package identity" ` ($bundleScript -match 'NotePropertyName version -NotePropertyValue \$resolvedVersion\.RichVersion') $true - Assert-Equal "native updater orders rc.2 after rc.1" ` - ([semver]"1.2.3-rc.2" -gt [semver]"1.2.3-rc.1") $true - Assert-Equal "native updater orders stable after prerelease" ` - ([semver]"1.2.3" -gt [semver]"1.2.3-rc.2") $true + # These two assertions exercise System.Management.Automation.SemanticVersion, + # the type behind the PowerShell 6+ semver accelerator, not Berd code. + # Windows PowerShell 5.1, which every justfile Windows recipe runs, has no + # such type, so only check the ordering where it exists. + $semanticVersionType = "System.Management.Automation.SemanticVersion" -as [type] + if ($null -ne $semanticVersionType) { + Assert-Equal "native updater orders rc.2 after rc.1" ` + (("1.2.3-rc.2" -as $semanticVersionType) -gt ("1.2.3-rc.1" -as $semanticVersionType)) $true + Assert-Equal "native updater orders stable after prerelease" ` + (("1.2.3" -as $semanticVersionType) -gt ("1.2.3-rc.2" -as $semanticVersionType)) $true + } else { + Write-Host "SKIP native updater semver ordering (PowerShell $($PSVersionTable.PSVersion) has no SemanticVersion type)" -ForegroundColor Yellow + } + + # Windows PowerShell 5.1 is the host every justfile Windows recipe pins, so + # the Windows scripts must stay clear of PowerShell 6+ only constructs. + $powerShell7OnlyPattern = '\[semver\]|Start-ThreadJob|ForEach-Object[^\r\n]*-Parallel\b' + # This harness names the forbidden tokens in the pattern above and already + # runs under 5.1 in CI, so it is exempt from the scan. + $windowsScripts = Get-ChildItem -Path $PSScriptRoot -File | + Where-Object { $_.Extension -in @(".ps1", ".psm1") -and $_.Name -ne "Test-WindowsDev.ps1" } + $powerShell7OnlyOffenders = @($windowsScripts | Where-Object { + (Get-Content -Raw -LiteralPath $_.FullName) -match $powerShell7OnlyPattern + } | ForEach-Object { $_.Name }) + Assert-Equal "Windows scripts stay Windows PowerShell 5.1 compatible ($($powerShell7OnlyOffenders -join ', '))" ` + $powerShell7OnlyOffenders.Count 0 $buildScript = Get-Content -Raw (Join-Path (Get-BerdRepoRoot) "src-tauri\build.rs") Assert-Equal "Rust rebuilds when the resolved app version changes" ($buildScript -match 'cargo:rerun-if-env-changed=BERD_APP_VERSION') $true From b8461673984f48f90181c0eafa1afcaad60b01f3 Mon Sep 17 00:00:00 2001 From: Cory Douthat <80133232+corydouthat-sq@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:47:58 -0400 Subject: [PATCH 2/2] test(windows): name the PowerShell 6+ constructs the compatibility scan actually checks The assertion in Test-WindowsDev.ps1 presented itself as a general Windows PowerShell 5.1 compatibility guard, but its regex only recognizes three spellings. Narrow the contract instead of overstating it: - Extract the denylist into Test-PowerShell7OnlyConstruct and document that it catches the constructs that have actually bitten these scripts ([semver], Start-ThreadJob, ForEach-Object -Parallel); the real 5.1 guarantee is the CI step that runs this whole suite under powershell.exe. - Add fixture assertions that each denylisted construct is detected and that look-alike 5.1-safe lines (plain ForEach-Object, "semver" in a string, Start-Job) are not. - Rename the scan assertion so it names the limited contract. - Align the sentence in docs/windows-onboarding.md that said the suite checks for PowerShell 6+ constructs in general. The harness stays exempt from the scan because the fixtures spell out the denylisted tokens. Co-Authored-By: Claude Fable 5.1 --- docs/windows-onboarding.md | 6 ++++-- scripts/windows/Test-WindowsDev.ps1 | 33 +++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/docs/windows-onboarding.md b/docs/windows-onboarding.md index 1b172215e..64b0b77dc 100644 --- a/docs/windows-onboarding.md +++ b/docs/windows-onboarding.md @@ -18,8 +18,10 @@ Native Berd provider sign-in is intentionally deferred on Windows. The app and Use normal PowerShell. You do not need a Visual Studio Developer PowerShell; the Windows scripts load the Visual Studio build environment before running Cargo. Windows PowerShell 5.1 (the `powershell.exe` every `just` Windows recipe runs) -and PowerShell 7 are both supported; the scripts avoid PowerShell 6+ only -constructs and `just test-windows-dev` checks that. +and PowerShell 7 are both supported. CI runs `just test-windows-dev` under +`powershell.exe`, which is what keeps the scripts 5.1-compatible; the suite also +scans the Windows scripts for the PowerShell 6+-only constructs that have bitten +them before (`[semver]`, `Start-ThreadJob`, `ForEach-Object -Parallel`). The repeatable entrypoint is `just`, but a completely fresh Windows machine still needs two seed steps: diff --git a/scripts/windows/Test-WindowsDev.ps1 b/scripts/windows/Test-WindowsDev.ps1 index c1506f73f..e90cb31af 100644 --- a/scripts/windows/Test-WindowsDev.ps1 +++ b/scripts/windows/Test-WindowsDev.ps1 @@ -36,6 +36,17 @@ function Assert-NoThrow { } } +# Denylist of the PowerShell 6+-only constructs that have actually bitten the +# Windows scripts: the [semver] type accelerator, Start-ThreadJob, and +# ForEach-Object -Parallel. It is deliberately not a general 5.1 compatibility +# checker; that guarantee comes from CI running this whole suite under +# powershell.exe ("Run Windows script tests under Windows PowerShell 5.1" in +# .github/workflows/ci.yml). +function Test-PowerShell7OnlyConstruct { + param([string]$Text) + return [bool]($Text -match '\[semver\]|Start-ThreadJob|ForEach-Object[^\r\n]*-Parallel\b') +} + $oldGooseDevRoot = $env:GOOSE_DEV_ROOT $oldGooseRepo = $env:GOOSE_DEV_REPO $oldGooseTarget = $env:GOOSE_DEV_CARGO_TARGET_DIR @@ -173,17 +184,25 @@ try { Write-Host "SKIP native updater semver ordering (PowerShell $($PSVersionTable.PSVersion) has no SemanticVersion type)" -ForegroundColor Yellow } - # Windows PowerShell 5.1 is the host every justfile Windows recipe pins, so - # the Windows scripts must stay clear of PowerShell 6+ only constructs. - $powerShell7OnlyPattern = '\[semver\]|Start-ThreadJob|ForEach-Object[^\r\n]*-Parallel\b' - # This harness names the forbidden tokens in the pattern above and already - # runs under 5.1 in CI, so it is exempt from the scan. + # Pin the limited contract of Test-PowerShell7OnlyConstruct: each of the + # three denylisted constructs is detected, and look-alike 5.1-safe lines + # are not. This is a known-offender scan, not a full 5.1 compatibility + # check; the real guarantee is CI running this suite under powershell.exe. + Assert-Equal "PowerShell 6+ scan detects [semver]" (Test-PowerShell7OnlyConstruct '[semver]"1.2.3"') $true + Assert-Equal "PowerShell 6+ scan detects Start-ThreadJob" (Test-PowerShell7OnlyConstruct 'Start-ThreadJob { }') $true + Assert-Equal "PowerShell 6+ scan detects ForEach-Object -Parallel" (Test-PowerShell7OnlyConstruct '1..3 | ForEach-Object -Parallel { $_ }') $true + Assert-Equal "PowerShell 6+ scan ignores plain ForEach-Object" (Test-PowerShell7OnlyConstruct 'ForEach-Object { $_ }') $false + Assert-Equal "PowerShell 6+ scan ignores the word semver in a string" (Test-PowerShell7OnlyConstruct '$x = "semver"') $false + Assert-Equal "PowerShell 6+ scan ignores Start-Job" (Test-PowerShell7OnlyConstruct 'Start-Job { }') $false + + # This harness holds the fixtures above (which spell out the denylisted + # tokens) and already runs under 5.1 in CI, so it is exempt from the scan. $windowsScripts = Get-ChildItem -Path $PSScriptRoot -File | Where-Object { $_.Extension -in @(".ps1", ".psm1") -and $_.Name -ne "Test-WindowsDev.ps1" } $powerShell7OnlyOffenders = @($windowsScripts | Where-Object { - (Get-Content -Raw -LiteralPath $_.FullName) -match $powerShell7OnlyPattern + Test-PowerShell7OnlyConstruct (Get-Content -Raw -LiteralPath $_.FullName) } | ForEach-Object { $_.Name }) - Assert-Equal "Windows scripts stay Windows PowerShell 5.1 compatible ($($powerShell7OnlyOffenders -join ', '))" ` + Assert-Equal "Windows scripts avoid the known PowerShell 6+-only constructs ([semver], Start-ThreadJob, ForEach-Object -Parallel) ($($powerShell7OnlyOffenders -join ', '))" ` $powerShell7OnlyOffenders.Count 0 $buildScript = Get-Content -Raw (Join-Path (Get-BerdRepoRoot) "src-tauri\build.rs")