-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Closed
Labels
Issue-Enhancementthe issue is more of a feature request than a bugthe issue is more of a feature request than a bugResolution-No ActivityIssue has had no activity for 6 months or moreIssue has had no activity for 6 months or moreWG-Cmdlets-Corecmdlets in the Microsoft.PowerShell.Core modulecmdlets in the Microsoft.PowerShell.Core module
Description
Opened out of #8218.
We do very little santisation/normalisation on paths passed in as module names in ModuleSpecification types to various cmdlets.
Not many users rely directly on this functionality, but things like RequiredModules in module manifests do.
We currently don't have any layering or type API (that I know of) to reflect that a path passed in has been converted by PowerShell to one that's safe for .NET to operate on, and the ideal scenario would be for us to change that. Otherwise we are doomed to repeat the same path checks/resolutions at every layer in the code, either hurting perf or causing serious bugs.
Steps to reproduce
Some examples of this problem:
- Remove module using a path in a qualified name doesn't work:
Describe "Remove-Module works with FullyQualifiedName using a path for the name" { BeforeAll { $moduleName = 'rmomod' $moduleVersion = '1.2' $modulePath = Join-Path $TestDrive $moduleName $manifestPath = Join-Path $modulePath "$moduleName.psd1" New-Item -ItemType Directory -Path $modulePath New-ModuleManifest -Path $manifestPath -ModuleVersion $moduleVersion if ($IsWindows) { $sep = '\' $altSep = '/' } else { $sep = '/' $altSep = '\' } $absoluteTestCases = @( @{ ModuleName = $moduleName; Case = 'module name' } @{ ModuleName = $modulePath; Case = 'absolute module dir path' } @{ ModuleName = "$TestDrive${altSep}$moduleName"; Case = 'absolute module dir path with alt dir separators' } @{ ModuleName = $manifestPath; Case = 'absolute manifest path' } @{ ModuleName = "$TestDrive${altSep}$moduleName${sep}$moduleName.psd1"; Case = 'absolute manifest path with alt dir separators' } ) $relativeTestCases = @( @{ Case = 'relative path to module dir'; Location = $TestDrive; ModuleName = "./$moduleName" } @{ Case = 'relative path to module dir with alt sep'; Location = "$TestDrive/duck"; ModuleName = "..${altSep}moduleName" } @{ Case = 'relative path to manifest with alt sep'; Location = "$TestDrive/$moduleName"; ModuleName = "./$moduleName.psd1" } @{ Case = 'relative path to manifest with alt sep'; Location = $TestDrive; ModuleName = ".${sep}$moduleName{$altSep}$moduleName.psd1" } @{ Case = 'current dir being module dir'; Location = "$TestDrive/$moduleName"; ModuleName = "." } ) } BeforeEach { Import-Module $modulePath } AfterEach { Remove-Module $moduleName -ErrorAction SilentlyContinue } It "Removes the module by <Case>" -TestCases $absoluteTestCases { param([string]$ModuleName, [string]$Case) $fqn = @{ ModuleName = $ModuleName ModuleVersion = $moduleVersion } Remove-Module -FullyQualifiedName $fqn -ErrorAction Stop Get-Module $moduleName | Should -HaveCount 0 } It "Removes the module by <Case>" -TestCases $relativeTestCases { param([string]$Location, [string]$ModuleName, [string]$Case) $fqn = @{ ModuleName = $ModuleName ModuleVersion = $moduleVersion } if (-not (Test-Path $Location)) { New-Item -ItemType Directory -Path $Location } Push-Location $Location try { Remove-Module -FullyQualifiedName $fqn -ErrorAction Stop Get-Module $moduleName | Should -HaveCount 0 } finally { Pop-Location } } }
- Required modules in a script:
(This second case actually supports alt separators, which I didn't expect. But still no module dir. And no relative paths either)
Describe "Requiring modules by absolute path" { BeforeAll { $scriptPath = Join-Path $TestDrive "script.ps1" $success = 'SUCCESS' $moduleName = 'reqmod' $modulePath = Join-Path $TestDrive $moduleName $manifestPath = Join-Path $modulePath "$moduleName.psd1" New-Item -ItemType Directory $modulePath -Force New-ModuleManifest -Path $manifestPath -ModuleVersion '3.2.4' if ($IsWindows) { $sep = '\' $altSep = '/' } else { $sep = '/' $altSep = '\' } $oldModulePath = $env:PSModulePath $env:PSModulePath += [System.IO.Path]::PathSeparator + $TestDrive $testCases = @( @{ Case = 'module name, with module on path'; ModuleName = $moduleName } @{ Case = 'absolute path to module dir'; ModuleName = $modulePath } @{ Case = 'absolute path to module dir with alt sep'; ModuleName = "$TestDrive${altSep}$moduleName" } @{ Case = 'absolute path to manifest'; ModuleName = $manifestPath } @{ Case = 'absolute path to manifest with alt sep'; ModuleName = "$TestDrive${sep}$moduleName${altSep}$moduleName.psd1" } ) } AfterAll { $env:PSModulePath = $oldModulePath } Context "Autoload unloaded required module" { AfterEach { Remove-Module $moduleName -ErrorAction SilentlyContinue } It "Autoloads the required module by <Case>" -TestCases $testCases { param([string]$ModuleName, [string]$Case) $script = @" #requires -Modules @{ ModuleName = '$ModuleName'; ModuleVersion = '$moduleVersion' } '$success' "@ New-Item -Path $scriptPath -Value $script -Force & $scriptPath | Should -BeExactly $success } } Context "Verify required module is already loaded" { BeforeAll { Import-Module $modulePath } AfterAll { Remove-Module $moduleName -ErrorAction SilentlyContinue } It "Verifies that the required module is loaded by <Case>" -TestCases $testCases { param([string]$ModuleName, [string]$Case) $script = @" #requires -Modules @{ ModuleName = '$ModuleName'; ModuleVersion = '$moduleVersion' } '$success' "@ New-Item -Path $scriptPath -Value $script -Force & $scriptPath | Should -BeExactly $success } } }
Metadata
Metadata
Assignees
Labels
Issue-Enhancementthe issue is more of a feature request than a bugthe issue is more of a feature request than a bugResolution-No ActivityIssue has had no activity for 6 months or moreIssue has had no activity for 6 months or moreWG-Cmdlets-Corecmdlets in the Microsoft.PowerShell.Core modulecmdlets in the Microsoft.PowerShell.Core module