diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e35d3d..25ee9e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -592,6 +592,9 @@ jobs: - name: Package native ARM64 ZIP shell: pwsh run: ./scripts/package-release.ps1 -Version $env:PACKAGE_VERSION -Architecture arm64 -OutputDirectory dist -TargetDirectory target + - name: Keep Windows PowerShell alive on the ARM64 runner image + shell: pwsh + run: ./scripts/conformance/windows_arm64_module_path.ps1 - name: Run ARM64 Windows conformance shell: pwsh env: diff --git a/.github/workflows/windows-arm64-diagnostics.yml b/.github/workflows/windows-arm64-diagnostics.yml index 99dc929..76ccecb 100644 --- a/.github/workflows/windows-arm64-diagnostics.yml +++ b/.github/workflows/windows-arm64-diagnostics.yml @@ -13,6 +13,7 @@ on: - .github/workflows/windows-arm64-diagnostics.yml - scripts/conformance/conpty_smoke.py - scripts/conformance/explicit_command_boot.py + - scripts/conformance/windows_arm64_module_path.ps1 permissions: contents: read @@ -68,6 +69,18 @@ jobs: if: always() shell: pwsh run: python scripts/conformance/windows_standard_user.py scripts/conformance/explicit_command_boot.py diagnostic-runtime/pebrel.exe diagnostic-output/explicit-commands.json + - name: Apply the ARM64 image module-path workaround + if: always() + shell: pwsh + run: ./scripts/conformance/windows_arm64_module_path.ps1 + - name: Explicit -e commands after the workaround + if: always() + shell: pwsh + run: python scripts/conformance/windows_standard_user.py scripts/conformance/explicit_command_boot.py diagnostic-runtime/pebrel.exe diagnostic-output/explicit-commands-fixed.json + - name: Conformance after the workaround + if: always() + shell: pwsh + run: python scripts/conformance/windows_standard_user.py scripts/conformance/run.py --app diagnostic-runtime/pebrel.exe --platform windows-aarch64 --output diagnostic-output/fixed/windows-arm64-report.json - uses: actions/upload-artifact@v4 if: always() with: diff --git a/scripts/conformance/conpty_smoke.py b/scripts/conformance/conpty_smoke.py index d781cf8..6dc0b98 100644 --- a/scripts/conformance/conpty_smoke.py +++ b/scripts/conformance/conpty_smoke.py @@ -246,7 +246,7 @@ def run_case( first_output_ms = round((time.monotonic() - started) * 1000) result["first_output_ms"] = first_output_ms result["output_bytes"] = len(output) - result["output_head"] = output[:400].decode("utf-8", "replace") + result["output_head"] = output[:2000].decode("utf-8", "replace") kernel.CloseHandle(process.hThread) kernel.CloseHandle(process.hProcess) kernel.DeleteProcThreadAttributeList(attributes) diff --git a/scripts/conformance/explicit_command_boot.py b/scripts/conformance/explicit_command_boot.py index 149d9b4..1b19277 100644 --- a/scripts/conformance/explicit_command_boot.py +++ b/scripts/conformance/explicit_command_boot.py @@ -221,6 +221,11 @@ if app_env is not None: f"Get-Module -ListAvailable PSReadLine | ForEach-Object {{ $_.Version.ToString() + ' ' + $_.Path }}; Write-Output {MARKER}"], ), } + variants["windows-default-import-utility-verbose"] = ( + windows_default, + ["powershell.exe", "-NoLogo", "-NoProfile", "-Command", + f"Import-Module Microsoft.PowerShell.Utility -Verbose; Write-Output {MARKER}"], + ) for index, entry in enumerate(app_entries): variants[f"app-entries-without-{index}"] = ([e for e in app_entries if e != entry], POWERSHELL_51) for name, (entries, command) in variants.items(): @@ -231,7 +236,7 @@ if app_env is not None: library, subprocess.list2cmdline(command), 0, REPLAY_WAIT, done_marker=MARKER.encode(), env=env, cwd=None ) summary = {k: result.get(k) for k in ("error", "first_output_ms", "marker_ms", "output_bytes")} - summary["output_head"] = str(result.get("output_head", ""))[:300] + summary["output_head"] = str(result.get("output_head", ""))[:1500] summary["entries"] = entries print("psmodulepath", name, "PASS" if result.get("marker_ms") is not None else "SILENT", json.dumps(summary)[:400], flush=True) report.setdefault("psmodulepath_variants", {})[name] = summary # type: ignore[index] diff --git a/scripts/conformance/windows_arm64_module_path.ps1 b/scripts/conformance/windows_arm64_module_path.ps1 new file mode 100644 index 0000000..ec205e8 --- /dev/null +++ b/scripts/conformance/windows_arm64_module_path.ps1 @@ -0,0 +1,23 @@ +# Work around the windows-11-arm runner image: Windows PowerShell 5.1 there +# renders nothing (not even `Write-Output`) when its PSModulePath is the +# registry value alone, and recovers as soon as the PowerShell 7 module +# directories that the runner's pwsh step shell injects come first. Pebrel +# rebuilds child environments from the registry (the stock console host rule), so +# the injected entries never reach the default tab; putting them into the +# user-level registry value keeps the rebuild faithful and PS 5.1 alive. +# Evidence: .github/workflows/windows-arm64-diagnostics.yml bisect, 2026-09-22. +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' +$pwsh7 = @( + (Join-Path $HOME 'Documents\PowerShell\Modules'), + (Join-Path $env:ProgramFiles 'PowerShell\Modules'), + (Join-Path $env:ProgramFiles 'PowerShell\7\Modules') +) +$machine = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') +$user = [Environment]::GetEnvironmentVariable('PSModulePath', 'User') +$entries = @($pwsh7) + @(($user, $machine) -join ';' -split ';' | Where-Object { $_ }) +$value = ($entries | Select-Object -Unique) -join ';' +[Environment]::SetEnvironmentVariable('PSModulePath', $value, 'User') +"User PSModulePath: $value"