diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a517122c..a4cfc7d55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `apm install -g --target codex` now honors `CODEX_HOME` for user-scope Codex MCP config writes, falling back to `~/.codex/config.toml` when unset. (closes #1861) (#1863) +- Windows pip fallback no longer terminates early when native pip writes + stderr under `$ErrorActionPreference = "Stop"`. (closes #1874) (#1876) ## [0.21.0] - 2026-06-19 diff --git a/install.ps1 b/install.ps1 index 5d186562f..65d04d083 100644 --- a/install.ps1 +++ b/install.ps1 @@ -265,14 +265,20 @@ function Install-ViaPip { } $pipIndexArgs = Get-PipIndexArgs try { - if ($pipCmd -like "* -m pip") { - $output = & $pythonCmd -m pip install --user @pipIndexArgs apm-cli 2>&1 - $pipExitCode = $LASTEXITCODE - $output | Write-Host - } else { - $output = & $pipCmd install --user @pipIndexArgs apm-cli 2>&1 - $pipExitCode = $LASTEXITCODE - $output | Write-Host + $previousErrorActionPreference = $ErrorActionPreference + try { + $ErrorActionPreference = "Continue" + if ($pipCmd -like "* -m pip") { + $output = & $pythonCmd -m pip install --user @pipIndexArgs apm-cli 2>&1 + $pipExitCode = $LASTEXITCODE + $output | Write-Host + } else { + $output = & $pipCmd install --user @pipIndexArgs apm-cli 2>&1 + $pipExitCode = $LASTEXITCODE + $output | Write-Host + } + } finally { + $ErrorActionPreference = $previousErrorActionPreference } if ($pipExitCode -ne 0) { Write-ErrorText "pip install failed (exit code $pipExitCode)." diff --git a/tests/unit/test_enterprise_bootstrap_installers.py b/tests/unit/test_enterprise_bootstrap_installers.py index d2562c433..8c8027314 100644 --- a/tests/unit/test_enterprise_bootstrap_installers.py +++ b/tests/unit/test_enterprise_bootstrap_installers.py @@ -169,6 +169,31 @@ def test_unix_installer_fail_closed_asset_exit_code() -> None: assert "APM_RELEASE_BASE_URL is not configured" in combined +def test_windows_pip_fallback_scopes_native_stderr_error_action_guard() -> None: + """install.ps1 must not let native pip stderr terminate fallback.""" + text = _read_repo_file("install.ps1") + body = text.split("function Install-ViaPip {", 1)[1].split( + "function Write-ManualInstallHelp {", 1 + )[0] + + previous_guard = "$previousErrorActionPreference = $ErrorActionPreference" + continue_guard = '$ErrorActionPreference = "Continue"' + restore_guard = "$ErrorActionPreference = $previousErrorActionPreference" + python_pip_call = "$output = & $pythonCmd -m pip install --user @pipIndexArgs apm-cli 2>&1" + pip_call = "$output = & $pipCmd install --user @pipIndexArgs apm-cli 2>&1" + + assert previous_guard in body + assert continue_guard in body + assert restore_guard in body + assert body.count(continue_guard) == 1 + assert body.index(previous_guard) < body.index(continue_guard) + assert body.index(continue_guard) < body.index(python_pip_call) + assert body.index(continue_guard) < body.index(pip_call) + assert body.index(python_pip_call) < body.index("finally {") + assert body.index(pip_call) < body.index("finally {") + assert body.index("finally {") < body.index(restore_guard) + + def test_windows_installer_uses_auth_on_first_ghes_metadata_fetch() -> None: """install.ps1 should not make an unauthenticated GHES metadata request first.""" text = _read_repo_file("install.ps1")