Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/bugfix-update-40840.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "bugfix",
"category": "``update``",
"description": "Reset PSModulePath before launching new PowerShell console to avoid inheriting an incompatible PSModulePath"
}
15 changes: 11 additions & 4 deletions awscli/customizations/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ def _do_update(self):
f.write('set AWS_CLI_DISTRIBUTION_SOURCE_OVERRIDE=update-exe\n')
if self._no_color:
f.write('set NO_COLOR=1\n')
# Clear the inherited PSModulePath so the launched PowerShell
# rebuilds its own default.
# https://github.com/aws/aws-cli/issues/10532
f.write('set PSModulePath=\n')
f.write('ping -n 3 127.0.0.1 >nul 2>&1\n')
f.write(f'"{ps_exe}" {ps_args}\n')

Expand All @@ -228,10 +232,13 @@ def _run_install(self, cmd):
)

def _find_powershell(self):
path = shutil.which('powershell')
if not path:
raise UpdateError('powershell.exe not found on PATH.')
return path
for name in ('powershell', 'pwsh'):
path = shutil.which(name)
if path:
return path
raise UpdateError(
'Neither powershell.exe nor pwsh.exe was found on PATH.'
)

def _is_system_install(self, install_metadata):
if 'script_install' in install_metadata:
Expand Down
51 changes: 49 additions & 2 deletions tests/unit/customizations/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,22 @@ def test_resolves_symlink_before_checking_install_dir(


class TestWindowsUpdateCommand:
def _command(self, install, elevated=True, runner=None, downloader=None):
def _command(
self,
install,
elevated=True,
runner=None,
downloader=None,
powershell_path='powershell.exe',
):
return WindowsUpdateCommand(
mock_session(),
source='exe',
install_metadata=install,
downloader=downloader or mock.Mock(),
is_elevated=elevated,
runner=runner or mock.Mock(),
powershell_path='powershell.exe',
powershell_path=powershell_path,
)

def _run(self, install, color='auto', **kwargs):
Expand Down Expand Up @@ -275,6 +282,46 @@ def test_wrapper_sets_no_color_when_color_off(self):

assert 'set NO_COLOR=1' in wrapper

def test_wrapper_clears_psmodulepath(self):
_, wrapper = self._run(USER_INSTALL)

assert 'set PSModulePath=\n' in wrapper

def test_prefers_windows_powershell_when_available(self, monkeypatch):
def which(name):
return {
'powershell': 'C:\\powershell.exe',
'pwsh': 'C:\\pwsh.exe',
}.get(name)

monkeypatch.setattr(update_module.shutil, 'which', which)

_, wrapper = self._run(USER_INSTALL, powershell_path=None)

assert '"C:\\powershell.exe" -NoProfile' in wrapper

def test_falls_back_to_pwsh_when_windows_powershell_missing(
self, monkeypatch
):
def which(name):
return 'C:\\pwsh.exe' if name == 'pwsh' else None

monkeypatch.setattr(update_module.shutil, 'which', which)

_, wrapper = self._run(USER_INSTALL, powershell_path=None)

assert '"C:\\pwsh.exe" -NoProfile' in wrapper

def test_raises_when_no_powershell_found(self, monkeypatch):
def which(name):
return None

monkeypatch.setattr(update_module.shutil, 'which', which)
command = self._command(USER_INSTALL, powershell_path=None)

with pytest.raises(UpdateError, match='Neither powershell'):
command([], global_args())

def test_system_install_requires_elevation(self):
runner = mock.Mock()
command = self._command(SYSTEM_INSTALL, elevated=False, runner=runner)
Expand Down
Loading