From d9697f07ba379a1aff937cc03eaa24db72593a54 Mon Sep 17 00:00:00 2001 From: "James Truher [MSFT]" Date: Mon, 22 May 2023 17:02:31 -0700 Subject: [PATCH] Add a public API for getting locations of `PSModulePath` elements (#19422) --- .../engine/Modules/ModuleIntrinsics.cs | 36 ++++++++++++++ .../engine/Module/ModulePath.Tests.ps1 | 45 ++++++++++++++++++ .../assets/PowerShell.TestPackage.3.2.1.nupkg | Bin 0 -> 3225 bytes 3 files changed, 81 insertions(+) create mode 100644 test/powershell/engine/Module/assets/PowerShell.TestPackage.3.2.1.nupkg diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index e2c446f5ac..fde3618e56 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -1172,6 +1172,42 @@ namespace System.Management.Automation return result.ToString(); } + /// + /// The available module path scopes. + /// + public enum PSModulePathScope + { + /// The users module path. + User, + + /// The Builtin module path. This is where PowerShell is installed (PSHOME). + Builtin, + + /// The machine module path. This is the shared location for all users of the system. + Machine + } + + /// + /// Retrieve the current PSModulePath for the specified scope. + /// + /// The scope of module path to retrieve. This can be User, Builtin, or Machine. + /// The string representing the requested module path type. + public static string GetPSModulePath(PSModulePathScope scope) + { + if (scope == PSModulePathScope.User) + { + return GetPersonalModulePath(); + } + else if (scope == PSModulePathScope.Builtin) + { + return GetPSHomeModulePath(); + } + else + { + return GetSharedModulePath(); + } + } + /// /// Checks the various PSModulePath environment string and returns PSModulePath string as appropriate. /// diff --git a/test/powershell/engine/Module/ModulePath.Tests.ps1 b/test/powershell/engine/Module/ModulePath.Tests.ps1 index a18b4fc3ff..aabbb28950 100644 --- a/test/powershell/engine/Module/ModulePath.Tests.ps1 +++ b/test/powershell/engine/Module/ModulePath.Tests.ps1 @@ -202,3 +202,48 @@ Describe "SxS Module Path Basic Tests" -tags "CI" { $out.Split([System.IO.Path]::PathSeparator, [System.StringSplitOptions]::RemoveEmptyEntries) | Should -Not -BeLike $validation } } + +Describe "ModuleIntrinsics.GetPSModulePath API tests" -tag @('CI', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { + BeforeAll { + # create a local repostory and install a module + $localSourceName = [Guid]::NewGuid().ToString("n") + $localSourceLocation = Join-Path $PSScriptRoot assets + Register-PSRepository -Name $localSourceName -SourceLocation $localSourceLocation -InstallationPolicy Trusted -ErrorAction SilentlyContinue + Install-Module -Force -Scope AllUsers -Name PowerShell.TestPackage -Repository $localSourceName -ErrorAction SilentlyContinue + Install-Module -Force -Scope CurrentUser -Name PowerShell.TestPackage -Repository $localSourceName -ErrorAction SilentlyContinue + + $testCases = @( + @{ Name = "User" ; Expected = $IsWindows ? + (Resolve-Path ([Environment]::GetFolderPath("Personal") + "\PowerShell\Modules")).Path : + (Resolve-Path ([System.Management.Automation.Platform]::SelectProductNameForDirectory("USER_MODULES"))).Path + } + @{ Name = "Machine" ; Expected = $IsWindows ? + [Environment]::GetFolderPath("ProgramFiles") + "\PowerShell\Modules" : + (Resolve-Path ([System.Management.Automation.Platform]::SelectProductNameForDirectory("SHARED_MODULES"))).Path + } + @{ Name = "Builtin" ; Expected = (Resolve-Path (Join-Path $PSHOME Modules)).Path } + ) + # resolve the paths to ensure they are in the correct format + $currentModulePathElements = $env:PSModulePath -split [System.IO.Path]::PathSeparator | Foreach-Object { (Resolve-Path $_).Path } + } + + AfterAll { + Unregister-PSRepository -Name $localSourceName -ErrorAction SilentlyContinue + } + + It "The value '' should return the proper value" -testcase $testCases { + param ( $Name, $Expected ) + $result = [System.Management.Automation.ModuleIntrinsics]::GetPSModulePath($name) + $result | Should -not -BeNullOrEmpty + # spot check pshome, the user and shared paths may not be present + if ( $name -eq "PSHOME") { + $result | Should -Be $Expected + } + } + + It "The current module path should contain the expected paths for ''" -testcase $testCases { + param ( $Name, $Expected ) + $mPath = (Resolve-Path ([System.Management.Automation.ModuleIntrinsics]::GetPSModulePath($name))).Path + $currentModulePathElements | Should -Contain $mPath + } +} diff --git a/test/powershell/engine/Module/assets/PowerShell.TestPackage.3.2.1.nupkg b/test/powershell/engine/Module/assets/PowerShell.TestPackage.3.2.1.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..bc3a6d9b094ff479b0acef2b1e54415e559c339d GIT binary patch literal 3225 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 literal 0 HcmV?d00001