diff --git a/cli/dispatcher-contract.json b/cli/dispatcher-contract.json index a99c6b77df..efaceaec01 100644 --- a/cli/dispatcher-contract.json +++ b/cli/dispatcher-contract.json @@ -1,5 +1,5 @@ { "schemaVersion": 1, - "dispatcherVersion": "3.0.1-beta.8", + "dispatcherVersion": "3.0.1-beta.9", "dispatcherContractVersion": 1 } diff --git a/cli/internal/install/command.go b/cli/internal/install/command.go index d85d43c2ac..01c32435e0 100644 --- a/cli/internal/install/command.go +++ b/cli/internal/install/command.go @@ -122,21 +122,6 @@ function Test-LegacyNpmUloopPath { function Write-LegacyNpmMultilineArgumentWarning { Write-Host 'Legacy npm shims can alter multiline PowerShell arguments before the native CLI receives them.' } -function Write-LegacyNpmManualRemoval { - param([string]$LegacyUloopPath, [string]$LegacyPrefix) - Write-Host 'Could not remove the legacy npm package automatically.' - Write-LegacyNpmMultilineArgumentWarning - if ($LegacyUloopPath) { - Write-Host "Legacy uloop command: $LegacyUloopPath" - } - if ($LegacyPrefix) { - Write-Host 'Run this manually if that command still shadows the native CLI:' - Write-Host (' npm uninstall -g --prefix "' + $LegacyPrefix + '" uloop-cli') - return - } - Write-Host 'Run this manually if the old npm command still shadows the native CLI:' - Write-Host ' npm uninstall -g uloop-cli' -} function Remove-LegacyNpmArtifacts { param([string]$LegacyUloopPath, [string]$LegacyPrefix) if ($LegacyUloopPath -and (Test-Path $LegacyUloopPath -PathType Leaf)) { @@ -193,7 +178,6 @@ function Invoke-LegacyNpmPackageRemoval { $LegacyPrefix = Get-NpmPrefixFromUloopPath -CommandPath $LegacyUloopPath } if (-not $LegacyPrefix) { - Write-LegacyNpmManualRemoval -LegacyUloopPath $LegacyUloopPath -LegacyPrefix $LegacyPrefix return $false } if ($NpmCommand) { @@ -204,7 +188,6 @@ function Invoke-LegacyNpmPackageRemoval { Remove-LegacyNpmArtifacts -LegacyUloopPath $LegacyUloopPath -LegacyPrefix $LegacyPrefix } if (Test-LegacyNpmArtifactsExist -LegacyUloopPath $LegacyUloopPath -LegacyPrefix $LegacyPrefix) { - Write-LegacyNpmManualRemoval -LegacyUloopPath $LegacyUloopPath -LegacyPrefix $LegacyPrefix return $false } Write-Host 'Removed legacy npm package: uloop-cli' @@ -253,20 +236,10 @@ function Get-LegacyNpmUloopPathsFromPath { } function Invoke-AllLegacyNpmPackageRemoval { param([string]$ExpectedUloopPath) - $RemovedAll = $true foreach ($LegacyUloopPath in (Get-LegacyNpmUloopPathsFromPath)) { - if (-not (Invoke-LegacyNpmPackageRemoval -LegacyUloopPath $LegacyUloopPath -ExpectedUloopPath $ExpectedUloopPath)) { - $RemovedAll = $false - } + Invoke-LegacyNpmPackageRemoval -LegacyUloopPath $LegacyUloopPath -ExpectedUloopPath $ExpectedUloopPath | Out-Null } Invoke-DefaultLegacyNpmPackageRemoval - foreach ($LegacyUloopPath in (Get-LegacyNpmUloopPathsFromPath)) { - if (-not [string]::Equals($LegacyUloopPath, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase)) { - Write-LegacyNpmManualRemoval -LegacyUloopPath $LegacyUloopPath -LegacyPrefix (Get-NpmPrefixFromUloopPath -CommandPath $LegacyUloopPath) - $RemovedAll = $false - } - } - return $RemovedAll } function Set-UserPathWithInstallDirectoryFirst { param([string]$Directory) @@ -296,11 +269,15 @@ function Get-FirstUloopCommandFromPath { return $null } foreach ($PathEntry in ($PathValue -split ';')) { - if (-not $PathEntry) { + $NormalizedPathEntry = & $NormalizePath $PathEntry + if (-not $NormalizedPathEntry) { continue } + if ($NormalizedPathEntry -match '^[A-Za-z]:$') { + $NormalizedPathEntry = $NormalizedPathEntry + '\' + } foreach ($ShimName in @('uloop.exe', 'uloop.cmd', 'uloop.ps1', 'uloop')) { - $CandidatePath = Join-Path $PathEntry $ShimName + $CandidatePath = Join-Path $NormalizedPathEntry $ShimName if (Test-Path $CandidatePath -PathType Leaf) { return $CandidatePath } @@ -308,36 +285,25 @@ function Get-FirstUloopCommandFromPath { } return $null } -function Test-PersistedUloopCommandResolvesToExpectedPath { - param([string]$ExpectedUloopPath) +function Report-PathShadowing { $MachinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine') $UserPath = [Environment]::GetEnvironmentVariable('Path', 'User') $ResolvedPath = Get-FirstUloopCommandFromPath -PathValue ([string]::Join(';', @($MachinePath, $UserPath))) if (-not $ResolvedPath) { - return $false - } - return [string]::Equals($ResolvedPath, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase) -} -function Report-PathShadowing { - $ResolvedCommand = Get-Command uloop -ErrorAction SilentlyContinue | Select-Object -First 1 - if (-not $ResolvedCommand) { return } - if ([string]::Equals($ResolvedCommand.Source, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase)) { + if ([string]::Equals($ResolvedPath, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase)) { return } Write-Host "Installed uloop to $ExpectedUloopPath, but PATH resolves uloop to:" - Write-Host " $($ResolvedCommand.Source)" - if (Test-LegacyNpmUloopPath -CommandPath $ResolvedCommand.Source) { + Write-Host " $ResolvedPath" + if (Test-LegacyNpmUloopPath -CommandPath $ResolvedPath) { Write-LegacyNpmMultilineArgumentWarning } Write-Host "Move $InstallDir earlier in PATH, or remove the legacy installation if it owns that command." } Set-UserPathWithInstallDirectoryFirst -Directory $InstallDir -$LegacyCleanupSucceeded = Invoke-AllLegacyNpmPackageRemoval -ExpectedUloopPath $ExpectedUloopPath -if ((-not $LegacyCleanupSucceeded) -and (-not (Test-PersistedUloopCommandResolvesToExpectedPath -ExpectedUloopPath $ExpectedUloopPath))) { - throw 'Failed to remove the legacy npm uloop-cli package.' -} +Invoke-AllLegacyNpmPackageRemoval -ExpectedUloopPath $ExpectedUloopPath | Out-Null Report-PathShadowing `, powerShellSingleQuote(installDir), diff --git a/cli/internal/install/command_test.go b/cli/internal/install/command_test.go index bcd93d5689..d5bb4e00e4 100644 --- a/cli/internal/install/command_test.go +++ b/cli/internal/install/command_test.go @@ -33,12 +33,17 @@ func TestCommandForWindowsConfiguresUserPathAndLegacyCleanup(t *testing.T) { "GetExtension($CommandPath), '.exe'", "foreach ($ShimName in @('uloop', 'uloop.cmd', 'uloop.ps1'))", "Invoke-AllLegacyNpmPackageRemoval -ExpectedUloopPath $ExpectedUloopPath", - "npm uninstall -g --prefix", - "npm uninstall -g uloop-cli", + "$NpmArgs = @('uninstall', '-g', '--prefix', $LegacyPrefix, 'uloop-cli')", + "$null = & $NpmCommand.Source @('uninstall', '-g', 'uloop-cli')", "Report-PathShadowing", + "function Get-FirstUloopCommandFromPath", + "$NormalizedPathEntry = & $NormalizePath $PathEntry", + "if ($NormalizedPathEntry -match '^[A-Za-z]:$') {\n $NormalizedPathEntry = $NormalizedPathEntry + '\\'\n }", + "$CandidatePath = Join-Path $NormalizedPathEntry $ShimName", + "$MachinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')", + "$ResolvedPath = Get-FirstUloopCommandFromPath -PathValue ([string]::Join(';', @($MachinePath, $UserPath)))", "function Write-LegacyNpmMultilineArgumentWarning", - "if (Test-LegacyNpmUloopPath -CommandPath $ResolvedCommand.Source) {\n Write-LegacyNpmMultilineArgumentWarning\n }", - "foreach ($ShimName in @('uloop.exe', 'uloop.cmd', 'uloop.ps1', 'uloop'))", + "if (Test-LegacyNpmUloopPath -CommandPath $ResolvedPath) {\n Write-LegacyNpmMultilineArgumentWarning\n }", "Legacy npm shims can alter multiline PowerShell arguments before the native CLI receives them.", } { if !strings.Contains(setupScript, expected) { @@ -60,8 +65,17 @@ func TestCommandForWindowsConfiguresUserPathAndLegacyCleanup(t *testing.T) { if count := strings.Count(setupScript, "Legacy npm shims can alter multiline PowerShell arguments before the native CLI receives them."); count != 1 { t.Fatalf("legacy npm multiline warning should have one message definition, got %d", count) } - if count := strings.Count(setupScript, "Write-LegacyNpmMultilineArgumentWarning"); count != 3 { - t.Fatalf("legacy npm multiline warning should be centralized and called from two sites, got %d occurrences", count) + if count := strings.Count(setupScript, "Write-LegacyNpmMultilineArgumentWarning"); count != 2 { + t.Fatalf("legacy npm multiline warning should be centralized and called from one site, got %d occurrences", count) + } + if strings.Contains(setupScript, "Failed to remove the legacy npm uloop-cli package.") { + t.Fatal("legacy npm cleanup failure should not fail installation") + } + if strings.Contains(setupScript, "Get-Command uloop -ErrorAction SilentlyContinue") { + t.Fatal("Windows path shadowing should inspect persisted PATH instead of the current process PATH") + } + if strings.Contains(setupScript, "$RemovedAll") { + t.Fatal("legacy npm cleanup aggregate result should not be tracked when it is intentionally ignored") } } @@ -511,8 +525,8 @@ func TestPosixInstallScriptRemovesAbsoluteLegacyNpmShimBeforePrependingPath(t *t } } -func TestPosixInstallScriptReportsManualRemovalWhenLegacyShimRemains(t *testing.T) { - // Verifies macOS legacy cleanup does not report success when npm leaves the old shim behind. +func TestPosixInstallScriptSilencesLegacyFailureWhenLegacyShimRemains(t *testing.T) { + // Verifies macOS legacy cleanup stays quiet when npm leaves the old shim behind. if runtime.GOOS == "windows" { t.Skip("POSIX shell setup is not available on Windows") } @@ -561,11 +575,11 @@ func TestPosixInstallScriptReportsManualRemovalWhenLegacyShimRemains(t *testing. if strings.Contains(outputText, "Removed legacy npm package: uloop-cli") { t.Fatalf("cleanup should not report success while the legacy shim remains:\n%s", outputText) } - if !strings.Contains(outputText, "Could not remove the legacy npm package automatically.") { - t.Fatalf("cleanup should print manual removal guidance:\n%s", outputText) + if strings.Contains(outputText, "Could not remove the legacy npm package automatically.") { + t.Fatalf("cleanup should not report legacy npm removal failure:\n%s", outputText) } - if !strings.Contains(outputText, "npm uninstall -g --prefix \""+legacyPrefix+"\" uloop-cli") { - t.Fatalf("manual removal guidance should include the inferred prefix:\n%s", outputText) + if strings.Contains(outputText, "npm uninstall -g --prefix \""+legacyPrefix+"\" uloop-cli") { + t.Fatalf("cleanup should not print manual removal guidance:\n%s", outputText) } if _, err := os.Stat(legacyUloop); err != nil { t.Fatalf("legacy uloop should remain for this scenario: %v", err) diff --git a/cli/internal/install/posix_install.go b/cli/internal/install/posix_install.go index efd89290e3..df0a610332 100644 --- a/cli/internal/install/posix_install.go +++ b/cli/internal/install/posix_install.go @@ -227,22 +227,6 @@ is_legacy_npm_uloop_path() { return 1 } -print_legacy_npm_manual_removal() { - legacy_uloop=$1 - legacy_prefix=$2 - echo "Could not remove the legacy npm package automatically." - if [ -n "$legacy_uloop" ]; then - echo "Legacy uloop command: $legacy_uloop" - fi - if [ -n "$legacy_prefix" ]; then - echo "Run this manually if that command still shadows the native CLI:" - echo " npm uninstall -g --prefix \"$legacy_prefix\" uloop-cli" - return - fi - echo "Run this manually if the old npm command still shadows the native CLI:" - echo " npm uninstall -g uloop-cli" -} - try_remove_legacy_npm_package() { legacy_uloop=$1 legacy_prefix="" @@ -251,11 +235,9 @@ try_remove_legacy_npm_package() { fi legacy_prefix=$(infer_npm_prefix_from_uloop_path "$legacy_uloop") if [ -z "$legacy_prefix" ]; then - print_legacy_npm_manual_removal "$legacy_uloop" "" return fi if ! command -v npm >/dev/null 2>&1; then - print_legacy_npm_manual_removal "$legacy_uloop" "$legacy_prefix" return fi if npm uninstall -g --prefix "$legacy_prefix" uloop-cli; then @@ -264,7 +246,6 @@ try_remove_legacy_npm_package() { return fi fi - print_legacy_npm_manual_removal "$legacy_uloop" "$legacy_prefix" } try_remove_default_legacy_npm_package() { diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 7a9dcf7886..08fed7a511 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -202,27 +202,6 @@ function Test-LegacyNpmUloopPath { return $CommandContent.Contains("node_modules/uloop-cli") -or $CommandContent.Contains("node_modules\uloop-cli") } -function Write-LegacyNpmManualRemoval { - param( - [string]$LegacyUloopPath, - [string]$LegacyPrefix - ) - - Write-Host "Could not remove the legacy npm package automatically." - if ($LegacyUloopPath) { - Write-Host "Legacy uloop command: $LegacyUloopPath" - } - - if ($LegacyPrefix) { - Write-Host "Run this manually if that command still shadows the native CLI:" - Write-Host (' npm uninstall -g --prefix "' + $LegacyPrefix + '" uloop-cli') - return - } - - Write-Host "Run this manually if the old npm command still shadows the native CLI:" - Write-Host " npm uninstall -g uloop-cli" -} - function Remove-LegacyNpmArtifacts { param( [string]$LegacyUloopPath, @@ -303,7 +282,6 @@ function Invoke-LegacyNpmPackageRemoval { } if (-not $LegacyPrefix) { - Write-LegacyNpmManualRemoval -LegacyUloopPath $LegacyUloopPath -LegacyPrefix $LegacyPrefix return $false } @@ -317,7 +295,6 @@ function Invoke-LegacyNpmPackageRemoval { } if (Test-LegacyNpmArtifactsExist -LegacyUloopPath $LegacyUloopPath -LegacyPrefix $LegacyPrefix) { - Write-LegacyNpmManualRemoval -LegacyUloopPath $LegacyUloopPath -LegacyPrefix $LegacyPrefix return $false } @@ -380,23 +357,11 @@ function Invoke-AllLegacyNpmPackageRemoval { [string]$ExpectedUloopPath ) - $RemovedAll = $true foreach ($LegacyUloopPath in (Get-LegacyNpmUloopPathsFromPath)) { - if (-not (Invoke-LegacyNpmPackageRemoval -LegacyUloopPath $LegacyUloopPath -ExpectedUloopPath $ExpectedUloopPath)) { - $RemovedAll = $false - } + Invoke-LegacyNpmPackageRemoval -LegacyUloopPath $LegacyUloopPath -ExpectedUloopPath $ExpectedUloopPath | Out-Null } Invoke-DefaultLegacyNpmPackageRemoval - - foreach ($LegacyUloopPath in (Get-LegacyNpmUloopPathsFromPath)) { - if (-not [string]::Equals($LegacyUloopPath, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase)) { - Write-LegacyNpmManualRemoval -LegacyUloopPath $LegacyUloopPath -LegacyPrefix (Get-NpmPrefixFromUloopPath -CommandPath $LegacyUloopPath) - $RemovedAll = $false - } - } - - return $RemovedAll } function Set-CurrentPathWithInstallDirectoryFirst { @@ -446,12 +411,16 @@ function Get-FirstUloopCommandFromPath { } foreach ($PathEntry in ($PathValue -split ";")) { - if (-not $PathEntry) { + $NormalizedPathEntry = ConvertTo-NormalizedPath -Path $PathEntry + if (-not $NormalizedPathEntry) { continue } + if ($NormalizedPathEntry -match "^[A-Za-z]:$") { + $NormalizedPathEntry = $NormalizedPathEntry + "\" + } foreach ($ShimName in @("uloop.exe", "uloop.cmd", "uloop.ps1", "uloop")) { - $CandidatePath = Join-Path $PathEntry $ShimName + $CandidatePath = Join-Path $NormalizedPathEntry $ShimName if (Test-Path $CandidatePath -PathType Leaf) { return $CandidatePath } @@ -461,8 +430,9 @@ function Get-FirstUloopCommandFromPath { return $null } -function Test-PersistedUloopCommandResolvesToExpectedPath { +function Report-PathShadowing { param( + [string]$Directory, [string]$ExpectedUloopPath ) @@ -470,29 +440,15 @@ function Test-PersistedUloopCommandResolvesToExpectedPath { $UserPath = [Environment]::GetEnvironmentVariable("Path", "User") $ResolvedPath = Get-FirstUloopCommandFromPath -PathValue ([string]::Join(";", @($MachinePath, $UserPath))) if (-not $ResolvedPath) { - return $false - } - - return [string]::Equals($ResolvedPath, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase) -} - -function Report-PathShadowing { - param( - [string]$Directory, - [string]$ExpectedUloopPath - ) - - $ResolvedCommand = Get-Command uloop -ErrorAction SilentlyContinue | Select-Object -First 1 - if (-not $ResolvedCommand) { return } - if ([string]::Equals($ResolvedCommand.Source, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase)) { + if ([string]::Equals($ResolvedPath, $ExpectedUloopPath, [System.StringComparison]::OrdinalIgnoreCase)) { return } Write-Host "Installed uloop to $ExpectedUloopPath, but PATH resolves uloop to:" - Write-Host " $($ResolvedCommand.Source)" + Write-Host " $ResolvedPath" Write-Host "Move $Directory earlier in PATH, or remove the legacy installation if it owns that command." } @@ -506,10 +462,7 @@ function Invoke-CompatibilityWindowsInstall { Write-Host "Configuring global uloop launcher..." Set-UserPathWithInstallDirectoryFirst -Directory $Directory - $LegacyCleanupSucceeded = Invoke-AllLegacyNpmPackageRemoval -ExpectedUloopPath $ExpectedUloopPath - if ((-not $LegacyCleanupSucceeded) -and (-not (Test-PersistedUloopCommandResolvesToExpectedPath -ExpectedUloopPath $ExpectedUloopPath))) { - throw "Failed to remove the legacy npm uloop-cli package." - } + Invoke-AllLegacyNpmPackageRemoval -ExpectedUloopPath $ExpectedUloopPath | Out-Null Report-PathShadowing -Directory $Directory -ExpectedUloopPath $ExpectedUloopPath Write-Host "The package-owned User PATH entry was configured." diff --git a/scripts/install.sh b/scripts/install.sh index de2426ee08..cf8549dbd4 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -96,25 +96,6 @@ is_legacy_npm_uloop_path() { return 1 } -print_legacy_npm_manual_removal() { - legacy_uloop=$1 - legacy_prefix=$2 - - echo "Could not remove the legacy npm package automatically." - if [ -n "$legacy_uloop" ]; then - echo "Legacy uloop command: $legacy_uloop" - fi - - if [ -n "$legacy_prefix" ]; then - echo "Run this manually if that command still shadows the native CLI:" - echo " npm uninstall -g --prefix \"$legacy_prefix\" uloop-cli" - return - fi - - echo "Run this manually if the old npm command still shadows the native CLI:" - echo " npm uninstall -g uloop-cli" -} - quote_for_single_quoted_shell() { printf "%s" "$1" | sed "s/'/'\\\\''/g" } @@ -239,26 +220,23 @@ try_remove_legacy_npm_package() { fi if [ -z "$legacy_prefix" ]; then - print_legacy_npm_manual_removal "$legacy_uloop" "" return fi if ! command -v npm >/dev/null 2>&1; then - print_legacy_npm_manual_removal "$legacy_uloop" "$legacy_prefix" return fi if [ -n "$legacy_prefix" ]; then if npm uninstall -g --prefix "$legacy_prefix" uloop-cli; then if [ -e "$legacy_uloop" ] || [ -L "$legacy_uloop" ]; then - print_legacy_npm_manual_removal "$legacy_uloop" "$legacy_prefix" + return else echo "Removed legacy npm package: uloop-cli" fi return fi - print_legacy_npm_manual_removal "$legacy_uloop" "$legacy_prefix" return fi } diff --git a/scripts/test-install-release-filter.sh b/scripts/test-install-release-filter.sh index 00a2816f8a..3ab9ebc122 100755 --- a/scripts/test-install-release-filter.sh +++ b/scripts/test-install-release-filter.sh @@ -769,7 +769,7 @@ test_posix_does_not_infer_npm_prefix_from_non_npm_command() { fi } -test_posix_prints_prefix_manual_cleanup_when_npm_is_unavailable() { +test_posix_silences_legacy_cleanup_when_npm_is_unavailable() { work_dir="$TMP_DIR/posix-no-npm" mock_bin="$work_dir/bin" tool_bin="$work_dir/tools" @@ -801,11 +801,12 @@ test_posix_prints_prefix_manual_cleanup_when_npm_is_unavailable() { LEGACY_ULOOP="$legacy_uloop" \ "$ROOT_DIR/scripts/install.sh" > "$work_dir/output.txt" 2> "$work_dir/stderr.txt" - assert_contains "$work_dir/output.txt" "npm uninstall -g --prefix \"$work_dir/npm-global\" uloop-cli" + assert_not_contains "$work_dir/output.txt" "Could not remove the legacy npm package automatically." + assert_not_contains "$work_dir/output.txt" "npm uninstall -g --prefix \"$work_dir/npm-global\" uloop-cli" assert_not_contains "$work_dir/output.txt" "Run this manually if the old npm command still shadows the native CLI:" } -test_posix_prints_manual_cleanup_when_npm_prefix_cannot_be_inferred() { +test_posix_silences_legacy_cleanup_when_npm_prefix_cannot_be_inferred() { work_dir="$TMP_DIR/posix-unknown-npm-prefix" mock_bin="$work_dir/bin" legacy_bin="$work_dir/custom-shims" @@ -834,9 +835,9 @@ test_posix_prints_manual_cleanup_when_npm_prefix_cannot_be_inferred() { assert_not_contains "$npm_log" "uninstall -g uloop-cli" assert_not_contains "$npm_log" "uninstall -g --prefix" - assert_contains "$work_dir/output.txt" "Could not remove the legacy npm package automatically." - assert_contains "$work_dir/output.txt" "Legacy uloop command: $legacy_uloop" - assert_contains "$work_dir/output.txt" "Run this manually if the old npm command still shadows the native CLI:" + assert_not_contains "$work_dir/output.txt" "Could not remove the legacy npm package automatically." + assert_not_contains "$work_dir/output.txt" "Legacy uloop command: $legacy_uloop" + assert_not_contains "$work_dir/output.txt" "Run this manually if the old npm command still shadows the native CLI:" } test_posix_removes_npm_package_before_replacing_same_bin_path() { @@ -947,6 +948,18 @@ test_powershell_native_probe_restores_error_action_preference() { assert_contains "$ROOT_DIR/scripts/install.ps1" '$ErrorActionPreference = $PreviousErrorActionPreference' } +test_powershell_reports_persisted_path_shadowing() { + assert_contains "$ROOT_DIR/scripts/install.ps1" 'function Get-FirstUloopCommandFromPath' + assert_contains "$ROOT_DIR/scripts/install.ps1" '$NormalizedPathEntry = ConvertTo-NormalizedPath -Path $PathEntry' + assert_contains "$ROOT_DIR/scripts/install.ps1" 'if ($NormalizedPathEntry -match "^[A-Za-z]:$")' + assert_contains "$ROOT_DIR/scripts/install.ps1" '$NormalizedPathEntry = $NormalizedPathEntry + "\"' + assert_contains "$ROOT_DIR/scripts/install.ps1" '$CandidatePath = Join-Path $NormalizedPathEntry $ShimName' + assert_contains "$ROOT_DIR/scripts/install.ps1" '[Environment]::GetEnvironmentVariable("Path", "Machine")' + assert_contains "$ROOT_DIR/scripts/install.ps1" '$ResolvedPath = Get-FirstUloopCommandFromPath -PathValue ([string]::Join(";", @($MachinePath, $UserPath)))' + assert_not_contains "$ROOT_DIR/scripts/install.ps1" 'Get-Command uloop -ErrorAction SilentlyContinue' + assert_not_contains "$ROOT_DIR/scripts/install.ps1" '$RemovedAll' +} + test_posix_latest_skips_prerelease_assets test_posix_latest_beta_selects_prerelease_assets test_posix_invokes_native_install_setup @@ -959,11 +972,12 @@ test_posix_prints_fish_path_guidance_without_writing_profile test_posix_prints_generic_path_guidance_for_unknown_shell test_posix_skips_default_npm_cleanup_when_native_command_is_first test_posix_does_not_infer_npm_prefix_from_non_npm_command -test_posix_prints_prefix_manual_cleanup_when_npm_is_unavailable -test_posix_prints_manual_cleanup_when_npm_prefix_cannot_be_inferred +test_posix_silences_legacy_cleanup_when_npm_is_unavailable +test_posix_silences_legacy_cleanup_when_npm_prefix_cannot_be_inferred test_posix_removes_npm_package_before_replacing_same_bin_path test_powershell_latest_skips_prerelease_assets test_git_bash_latest_installs_windows_zip_asset test_powershell_installer_avoids_optional_archive_cmdlets test_powershell_installer_uses_non_installer_staged_executable_name test_powershell_native_probe_restores_error_action_preference +test_powershell_reports_persisted_path_shadowing