diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index 9a0511a93cc..feb511d109e 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -1152,6 +1152,11 @@ private static string AddToPath(string basePath, string pathToAdd, int insertPos else result.Append(Path.PathSeparator + subPathToAdd); } + else if (insertPosition > result.Length) + { + // handle case where path is a singleton with no path seperator already + result.Append(Path.PathSeparator).Append(subPathToAdd); + } else // insert at the requested location (this is used by DSC ( location) and by 'user-specific location' (SpecialFolder.MyDocuments or EVT.User)) { result.Insert(insertPosition, subPathToAdd + Path.PathSeparator); @@ -1164,9 +1169,7 @@ private static string AddToPath(string basePath, string pathToAdd, int insertPos } /// - /// Checks the various PSModulePath environment string and returns PSModulePath string as appropriate. Note - because these - /// strings go through the provider, we need to escape any wildcards before passing them - /// along. + /// Checks the various PSModulePath environment string and returns PSModulePath string as appropriate. /// public static string GetModulePath(string currentProcessModulePath, string hklmMachineModulePath, string hkcuUserModulePath) { @@ -1196,11 +1199,6 @@ public static string GetModulePath(string currentProcessModulePath, string hklmM { currentProcessModulePath += hklmMachineModulePath; // += EVT.Machine } - -#if !UNIX - // Add Windows Modules path - currentProcessModulePath = $"{currentProcessModulePath}{Path.PathSeparator}{s_windowsPowerShellPSHomeModulePath}"; -#endif } // EVT.Process exists // Now handle the case where the environment variable is already set. @@ -1281,9 +1279,16 @@ internal static string GetWindowsPowerShellModulePath() private static string SetModulePath() { string currentModulePath = GetExpandedEnvironmentVariable(Constants.PSModulePathEnvVar, EnvironmentVariableTarget.Process); +#if !UNIX + // if the current process and user env vars are the same, it means we need to append the machine one as it's incomplete + // otherwise, the user modified it and we should use the process one + if (string.CompareOrdinal(GetExpandedEnvironmentVariable(Constants.PSModulePathEnvVar, EnvironmentVariableTarget.User), currentModulePath) == 0) + { + currentModulePath = currentModulePath + Path.PathSeparator + GetExpandedEnvironmentVariable(Constants.PSModulePathEnvVar, EnvironmentVariableTarget.Machine); + } +#endif string allUsersModulePath = PowerShellConfig.Instance.GetModulePath(ConfigScope.AllUsers); string personalModulePath = PowerShellConfig.Instance.GetModulePath(ConfigScope.CurrentUser); - string newModulePathString = GetModulePath(currentModulePath, allUsersModulePath, personalModulePath); if (!string.IsNullOrEmpty(newModulePathString)) diff --git a/test/powershell/engine/Module/ModulePath.Tests.ps1 b/test/powershell/engine/Module/ModulePath.Tests.ps1 index c124a8ed17b..ccd7a04f00f 100644 --- a/test/powershell/engine/Module/ModulePath.Tests.ps1 +++ b/test/powershell/engine/Module/ModulePath.Tests.ps1 @@ -70,12 +70,19 @@ Describe "SxS Module Path Basic Tests" -tags "CI" { $env:PSModulePath = "" $defaultModulePath = & $powershell -nopro -c '$env:PSModulePath' + $pathSeparator = [System.IO.Path]::PathSeparator - $paths = $defaultModulePath -split [System.IO.Path]::PathSeparator + $paths = $defaultModulePath.Replace("$pathSeparator$pathSeparator", "$pathSeparator") -split $pathSeparator if ($IsWindows) { - $paths.Count | Should -Be 4 + $expectedPaths = 3 # user, shared, pshome + $userPaths = [System.Environment]::GetEnvironmentVariable("PSModulePath", [System.EnvironmentVariableTarget]::User) + $expectedPaths += $userPaths ? $userPaths.Split($pathSeparator).Count : 0 + $machinePaths = [System.Environment]::GetEnvironmentVariable("PSModulePath", [System.EnvironmentVariableTarget]::Machine) + $expectedPaths += $machinePaths ? $machinePaths.Split($pathSeparator).Count : 0 + + $paths.Count | Should -Be $expectedPaths } else { @@ -85,10 +92,7 @@ Describe "SxS Module Path Basic Tests" -tags "CI" { $paths[0].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedUserPath $paths[1].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedSharedPath $paths[2].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedSystemPath - if ($IsWindows) - { - $paths[3].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedWindowsPowerShellPSHomePath - } + $defaultModulePath | Should -BeLike "*$expectedWindowsPowerShellPSHomePath*" } It "Works with pshome module path derived from a different PowerShell instance" -Skip:(!$IsCoreCLR -or $skipNoPwsh) { @@ -104,18 +108,11 @@ Describe "SxS Module Path Basic Tests" -tags "CI" { $env:PSModulePath = $fakePSHomeModuleDir $newModulePath = & $powershell -nopro -c '$env:PSModulePath' $paths = $newModulePath -split [System.IO.Path]::PathSeparator - $paths.Count | Should -Be 4 - $paths[0] | Should -Be $expectedUserPath $paths[1] | Should -Be $expectedSharedPath $paths[2] | Should -Be $expectedSystemPath $paths[3] | Should -Be $fakePSHomeModuleDir - if ($IsWindows) - { - $expectedWindowsPowerShellPSHomePath | Should -Not -BeIn $paths - } - } finally { ## Remove 'powershell' and 'pwsh.deps.json' from the fake PSHome folder @@ -180,9 +177,14 @@ Describe "SxS Module Path Basic Tests" -tags "CI" { } It 'Windows PowerShell does not inherit path defined in powershell.config.json' -Skip:(!$IsWindows) { - $userConfig = '{ "PSModulePath": "myUserPath" }' - Set-Content -Path $userConfigPath -Value $userConfig -Force - $out = pwsh -noprofile -command 'powershell.exe -noprofile -command $env:PSModulePath' - $out | Should -Not -BeLike 'myUserPath;*' + try { + $userConfig = '{ "PSModulePath": "myUserPath" }' + Set-Content -Path $userConfigPath -Value $userConfig -Force + $out = pwsh -noprofile -command 'powershell.exe -noprofile -command $env:PSModulePath' + $out | Should -Not -BeLike 'myUserPath;*' + } + finally { + Remove-Item -Path $userConfigPath -Force + } } }