Fix Get-Module -FullyQualifiedName option to work with paths (#9101)
This commit is contained in:
parent
17f5a5ccbe
commit
0ee5278b40
|
@ -375,10 +375,11 @@ namespace Microsoft.PowerShell.Commands
|
||||||
var moduleSpecTable = new Dictionary<string, ModuleSpecification>(StringComparer.OrdinalIgnoreCase);
|
var moduleSpecTable = new Dictionary<string, ModuleSpecification>(StringComparer.OrdinalIgnoreCase);
|
||||||
if (FullyQualifiedName != null)
|
if (FullyQualifiedName != null)
|
||||||
{
|
{
|
||||||
// TODO:
|
for (int modSpecIndex = 0; modSpecIndex < FullyQualifiedName.Length; modSpecIndex++)
|
||||||
// FullyQualifiedName.Name could be a path, in which case it will not match module.Name.
|
{
|
||||||
// This is potentially a bug (since version checks are ignored).
|
FullyQualifiedName[modSpecIndex] = FullyQualifiedName[modSpecIndex].WithNormalizedName(Context, SessionState.Path.CurrentLocation.Path);
|
||||||
// We should normalize FullyQualifiedName.Name here with ModuleIntrinsics.NormalizeModuleName().
|
}
|
||||||
|
|
||||||
moduleSpecTable = FullyQualifiedName.ToDictionary(moduleSpecification => moduleSpecification.Name, StringComparer.OrdinalIgnoreCase);
|
moduleSpecTable = FullyQualifiedName.ToDictionary(moduleSpecification => moduleSpecification.Name, StringComparer.OrdinalIgnoreCase);
|
||||||
strNames.AddRange(FullyQualifiedName.Select(spec => spec.Name));
|
strNames.AddRange(FullyQualifiedName.Select(spec => spec.Name));
|
||||||
}
|
}
|
||||||
|
@ -545,22 +546,36 @@ namespace Microsoft.PowerShell.Commands
|
||||||
|
|
||||||
foreach (PSModuleInfo module in modules)
|
foreach (PSModuleInfo module in modules)
|
||||||
{
|
{
|
||||||
// TODO:
|
IEnumerable<ModuleSpecification> candidateModuleSpecs = GetCandidateModuleSpecs(moduleSpecificationTable, module);
|
||||||
// moduleSpecification.Name may be a path and will not match module.Name when they refer to the same module.
|
|
||||||
// This actually causes the module to be returned always, so other specification checks are skipped erroneously.
|
|
||||||
// Instead we need to be able to look up or match modules by path as well (e.g. a new comparer for PSModuleInfo).
|
|
||||||
|
|
||||||
// No table entry means we return the module
|
|
||||||
if (!moduleSpecificationTable.TryGetValue(module.Name, out ModuleSpecification moduleSpecification))
|
|
||||||
{
|
|
||||||
yield return module;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modules with table entries only get returned if they match them
|
// Modules with table entries only get returned if they match them
|
||||||
if (ModuleIntrinsics.IsModuleMatchingModuleSpec(module, moduleSpecification))
|
// We skip the name check since modules have already been prefiltered base on the moduleSpec path/name
|
||||||
|
foreach (ModuleSpecification moduleSpec in candidateModuleSpecs)
|
||||||
{
|
{
|
||||||
yield return module;
|
if (ModuleIntrinsics.IsModuleMatchingModuleSpec(module, moduleSpec, skipNameCheck: true))
|
||||||
|
{
|
||||||
|
yield return module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Take a dictionary of module specifications and return those that potentially match the module
|
||||||
|
/// passed in as a parameter (checks on names and paths).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="moduleSpecTable">The module specifications to filter candidates from.</param>
|
||||||
|
/// <param name="module">The module to find candidates for from the module specification table.</param>
|
||||||
|
/// <returns>The module specifications matching the module based on name, path and subpath.</returns>
|
||||||
|
private static IEnumerable<ModuleSpecification> GetCandidateModuleSpecs(
|
||||||
|
IDictionary<string, ModuleSpecification> moduleSpecTable,
|
||||||
|
PSModuleInfo module)
|
||||||
|
{
|
||||||
|
foreach (ModuleSpecification moduleSpec in moduleSpecTable.Values)
|
||||||
|
{
|
||||||
|
if (moduleSpec.Name == module.Name || moduleSpec.Name == module.Path || module.Path.Contains(moduleSpec.Name))
|
||||||
|
{
|
||||||
|
yield return moduleSpec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -423,10 +423,14 @@ namespace System.Management.Automation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="moduleInfo">The module info object to check.</param>
|
/// <param name="moduleInfo">The module info object to check.</param>
|
||||||
/// <param name="moduleSpec">The module specification to match the module info object against.</param>
|
/// <param name="moduleSpec">The module specification to match the module info object against.</param>
|
||||||
|
/// <param name="skipNameCheck">True if we should skip the name check on the module specification.</param>
|
||||||
/// <returns>True if the module info object meets all the constraints on the module specification, false otherwise.</returns>
|
/// <returns>True if the module info object meets all the constraints on the module specification, false otherwise.</returns>
|
||||||
internal static bool IsModuleMatchingModuleSpec(PSModuleInfo moduleInfo, ModuleSpecification moduleSpec)
|
internal static bool IsModuleMatchingModuleSpec(
|
||||||
|
PSModuleInfo moduleInfo,
|
||||||
|
ModuleSpecification moduleSpec,
|
||||||
|
bool skipNameCheck = false)
|
||||||
{
|
{
|
||||||
return IsModuleMatchingModuleSpec(out ModuleMatchFailure matchFailureReason, moduleInfo, moduleSpec);
|
return IsModuleMatchingModuleSpec(out ModuleMatchFailure matchFailureReason, moduleInfo, moduleSpec, skipNameCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -435,8 +439,13 @@ namespace System.Management.Automation
|
||||||
/// <param name="matchFailureReason">The constraint that caused the match failure, if any.</param>
|
/// <param name="matchFailureReason">The constraint that caused the match failure, if any.</param>
|
||||||
/// <param name="moduleInfo">The module info object to check.</param>
|
/// <param name="moduleInfo">The module info object to check.</param>
|
||||||
/// <param name="moduleSpec">The module specification to match the module info object against.</param>
|
/// <param name="moduleSpec">The module specification to match the module info object against.</param>
|
||||||
|
/// <param name="skipNameCheck">True if we should skip the name check on the module specification.</param>
|
||||||
/// <returns>True if the module info object meets all the constraints on the module specification, false otherwise.</returns>
|
/// <returns>True if the module info object meets all the constraints on the module specification, false otherwise.</returns>
|
||||||
internal static bool IsModuleMatchingModuleSpec(out ModuleMatchFailure matchFailureReason, PSModuleInfo moduleInfo, ModuleSpecification moduleSpec)
|
internal static bool IsModuleMatchingModuleSpec(
|
||||||
|
out ModuleMatchFailure matchFailureReason,
|
||||||
|
PSModuleInfo moduleInfo,
|
||||||
|
ModuleSpecification moduleSpec,
|
||||||
|
bool skipNameCheck = false)
|
||||||
{
|
{
|
||||||
if (moduleSpec == null)
|
if (moduleSpec == null)
|
||||||
{
|
{
|
||||||
|
@ -447,7 +456,7 @@ namespace System.Management.Automation
|
||||||
return IsModuleMatchingConstraints(
|
return IsModuleMatchingConstraints(
|
||||||
out matchFailureReason,
|
out matchFailureReason,
|
||||||
moduleInfo,
|
moduleInfo,
|
||||||
moduleSpec.Name,
|
skipNameCheck ? null : moduleSpec.Name,
|
||||||
moduleSpec.Guid,
|
moduleSpec.Guid,
|
||||||
moduleSpec.RequiredVersion,
|
moduleSpec.RequiredVersion,
|
||||||
moduleSpec.Version,
|
moduleSpec.Version,
|
||||||
|
|
|
@ -27,8 +27,7 @@ Describe "Get-Module -ListAvailable" -Tags "CI" {
|
||||||
New-Item -ItemType File -Path "$testdrive\Modules\Az\Az.psm1" > $null
|
New-Item -ItemType File -Path "$testdrive\Modules\Az\Az.psm1" > $null
|
||||||
|
|
||||||
$fullyQualifiedPathTestCases = @(
|
$fullyQualifiedPathTestCases = @(
|
||||||
# The current behaviour in PowerShell is that version gets ignored when using Get-Module -FullyQualifiedName with a path
|
@{ ModPath = "$TestDrive/Modules\Foo"; Name = 'Foo'; Version = '2.0'; Count = 1 }
|
||||||
@{ ModPath = "$TestDrive/Modules\Foo"; Name = 'Foo'; Version = '2.0'; Count = 2 }
|
|
||||||
@{ ModPath = "$TestDrive\Modules/Foo\1.1/Foo.psd1"; Name = 'Foo'; Version = '1.1'; Count = 1 }
|
@{ ModPath = "$TestDrive\Modules/Foo\1.1/Foo.psd1"; Name = 'Foo'; Version = '1.1'; Count = 1 }
|
||||||
@{ ModPath = "$TestDrive\Modules/Bar.psd1"; Name = 'Bar'; Version = '0.0'; Count = 1 }
|
@{ ModPath = "$TestDrive\Modules/Bar.psd1"; Name = 'Bar'; Version = '0.0'; Count = 1 }
|
||||||
@{ ModPath = "$TestDrive\Modules\Zoo\Too\Zoo.psm1"; Name = 'Zoo'; Version = '0.0'; Count = 1 }
|
@{ ModPath = "$TestDrive\Modules\Zoo\Too\Zoo.psm1"; Name = 'Zoo'; Version = '0.0'; Count = 1 }
|
||||||
|
@ -225,3 +224,101 @@ Describe "Get-Module -ListAvailable" -Tags "CI" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Describe 'Get-Module -ListAvailable with path' -Tags "CI" {
|
||||||
|
BeforeAll {
|
||||||
|
$moduleName = 'Banana'
|
||||||
|
$modulePath = Join-Path $TestDrive $moduleName
|
||||||
|
$v1 = '1.2.3'
|
||||||
|
$v2 = '4.8.3'
|
||||||
|
$v1DirPath = Join-Path $modulePath $v1
|
||||||
|
$v2DirPath = Join-Path $modulePath $v2
|
||||||
|
$manifestV1Path = Join-Path $v1DirPath "$moduleName.psd1"
|
||||||
|
$manifestV2Path = Join-Path $v2DirPath "$moduleName.psd1"
|
||||||
|
|
||||||
|
New-Item -ItemType Directory $modulePath
|
||||||
|
New-Item -ItemType Directory -Path $v1DirPath
|
||||||
|
New-Item -ItemType Directory -Path $v2DirPath
|
||||||
|
New-ModuleManifest -Path $manifestV1Path -ModuleVersion $v1
|
||||||
|
New-ModuleManifest -Path $manifestV2Path -ModuleVersion $v2
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Gets all versions by path" {
|
||||||
|
$modules = Get-Module -ListAvailable $modulePath | Sort-Object -Property Version
|
||||||
|
|
||||||
|
$modules | Should -HaveCount 2
|
||||||
|
$modules[0].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[0].Path | Should -BeExactly $manifestV1Path
|
||||||
|
$modules[0].Version | Should -Be $v1
|
||||||
|
$modules[1].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[1].Path | Should -BeExactly $manifestV2Path
|
||||||
|
$modules[1].Version | Should -Be $v2
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Gets all versions by FullyQualifiedName with path with lower version" {
|
||||||
|
$modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; ModuleVersion = '0.0' } | Sort-Object -Property Version
|
||||||
|
|
||||||
|
$modules | Should -HaveCount 2
|
||||||
|
$modules[0].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[0].Path | Should -BeExactly $manifestV1Path
|
||||||
|
$modules[0].Version | Should -Be $v1
|
||||||
|
$modules[1].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[1].Path | Should -BeExactly $manifestV2Path
|
||||||
|
$modules[1].Version | Should -Be $v2
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Gets high version by FullyQualifiedName with path with high version" {
|
||||||
|
$modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; ModuleVersion = '2.0' } | Sort-Object -Property Version
|
||||||
|
|
||||||
|
$modules | Should -HaveCount 1
|
||||||
|
$modules[0].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[0].Path | Should -BeExactly $manifestV2Path
|
||||||
|
$modules[0].Version | Should -Be $v2
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Gets low version by FullyQualifiedName with path with low maximum version" {
|
||||||
|
$modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; MaximumVersion = '2.0' } | Sort-Object -Property Version
|
||||||
|
|
||||||
|
$modules | Should -HaveCount 1
|
||||||
|
$modules[0].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[0].Path | Should -BeExactly $manifestV1Path
|
||||||
|
$modules[0].Version | Should -Be $v1
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Gets low version by FullyQualifiedName with path with low maximum version and version" {
|
||||||
|
$modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; MaximumVersion = '2.0'; ModuleVersion = '1.0' } | Sort-Object -Property Version
|
||||||
|
|
||||||
|
$modules | Should -HaveCount 1
|
||||||
|
$modules[0].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[0].Path | Should -BeExactly $manifestV1Path
|
||||||
|
$modules[0].Version | Should -Be $v1
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Gets correct version by FullyQualifiedName with path with required version" -TestCases @(
|
||||||
|
@{ Version = $v1 }
|
||||||
|
@{ Version = $v2 }
|
||||||
|
) {
|
||||||
|
param([version]$Version)
|
||||||
|
|
||||||
|
switch ($Version)
|
||||||
|
{
|
||||||
|
$v1
|
||||||
|
{
|
||||||
|
$expectedPath = $manifestV1Path
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
$v2
|
||||||
|
{
|
||||||
|
$expectedPath = $manifestV2Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; RequiredVersion = $Version }
|
||||||
|
|
||||||
|
$modules | Should -HaveCount 1
|
||||||
|
$modules[0].Name | Should -BeExactly $moduleName
|
||||||
|
$modules[0].Path | Should -BeExactly $expectedPath
|
||||||
|
$modules[0].Version | Should -Be $Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue