-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Check CompatiblePSEditions manifest field for modules loaded from Windows PowerShell $PSHOME #7183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c97a13d
ee98971
9b752c2
ed14c89
5535978
d8779c6
f7cba10
eea20c3
f31cfcb
eba3949
ee167a5
94394aa
ab70254
9860817
03a239c
0dc4be2
78f33be
5ccab3c
ae6501a
02b19d0
95f2c8e
e708507
8f835d0
f619d15
fc7d33b
90eb1d0
d9b0f9d
073800e
f8ccb9f
26af658
21f287a
3c59b55
a0e1ca4
81cb110
87a6c32
6d72598
d0ac17a
e434a14
020123e
a034a4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -398,7 +398,7 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun | |
|
|
||
| #region Module Names | ||
|
|
||
| internal static List<CompletionResult> CompleteModuleName(CompletionContext context, bool loadedModulesOnly) | ||
| internal static List<CompletionResult> CompleteModuleName(CompletionContext context, bool loadedModulesOnly, bool skipEditionCheck = false) | ||
| { | ||
| var moduleName = context.WordToComplete ?? string.Empty; | ||
| var result = new List<CompletionResult>(); | ||
|
|
@@ -413,6 +413,12 @@ internal static List<CompletionResult> CompleteModuleName(CompletionContext cont | |
| if (!loadedModulesOnly) | ||
| { | ||
| powershell.AddParameter("ListAvailable", true); | ||
|
|
||
| // -SkipEditionCheck should only be set or apply to -ListAvailable | ||
| if (skipEditionCheck) | ||
| { | ||
| powershell.AddParameter("SkipEditionCheck", true); | ||
| } | ||
| } | ||
|
|
||
| Exception exceptionThrown; | ||
|
|
@@ -2101,17 +2107,19 @@ private static void NativeCommandArgumentCompletion( | |
| case "Get-Module": | ||
| { | ||
| bool loadedModulesOnly = boundArguments == null || !boundArguments.ContainsKey("ListAvailable"); | ||
| NativeCompletionModuleCommands(context, parameterName, loadedModulesOnly, /* isImportModule: */ false, result); | ||
| bool skipEditionCheck = !loadedModulesOnly && boundArguments.ContainsKey("SkipEditionCheck"); | ||
| NativeCompletionModuleCommands(context, parameterName, result, loadedModulesOnly, skipEditionCheck: skipEditionCheck); | ||
| break; | ||
| } | ||
| case "Remove-Module": | ||
| { | ||
| NativeCompletionModuleCommands(context, parameterName, /* loadedModulesOnly: */ true, /* isImportModule: */ false, result); | ||
| NativeCompletionModuleCommands(context, parameterName, result, loadedModulesOnly: true); | ||
| break; | ||
| } | ||
| case "Import-Module": | ||
| { | ||
| NativeCompletionModuleCommands(context, parameterName, /* loadedModulesOnly: */ false, /* isImportModule: */ true, result); | ||
| bool skipEditionCheck = boundArguments != null && boundArguments.ContainsKey("SkipEditionCheck"); | ||
| NativeCompletionModuleCommands(context, parameterName, result, isImportModule: true, skipEditionCheck: skipEditionCheck); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Named parameters look more readable (what is "result" here?). |
||
| break; | ||
| } | ||
| case "Debug-Process": | ||
|
|
@@ -3046,7 +3054,13 @@ private static void NativeCompletionScheduledJobCommands(CompletionContext conte | |
| } | ||
| } | ||
|
|
||
| private static void NativeCompletionModuleCommands(CompletionContext context, string paramName, bool loadedModulesOnly, bool isImportModule, List<CompletionResult> result) | ||
| private static void NativeCompletionModuleCommands( | ||
| CompletionContext context, | ||
| string paramName, | ||
| List<CompletionResult> result, | ||
| bool loadedModulesOnly = false, | ||
| bool isImportModule = false, | ||
| bool skipEditionCheck = false) | ||
| { | ||
| if (string.IsNullOrEmpty(paramName)) | ||
| { | ||
|
|
@@ -3067,7 +3081,7 @@ private static void NativeCompletionModuleCommands(CompletionContext context, st | |
| StringLiterals.PowerShellILAssemblyExtension, | ||
| StringLiterals.PowerShellCmdletizationFileExtension | ||
| }; | ||
| var moduleFilesResults = new List<CompletionResult>(CompleteFilename(context, /* containerOnly: */ false, moduleExtensions)); | ||
| var moduleFilesResults = new List<CompletionResult>(CompleteFilename(context, containerOnly: false, moduleExtensions)); | ||
| if (moduleFilesResults.Count > 0) | ||
| result.AddRange(moduleFilesResults); | ||
|
|
||
|
|
@@ -3079,7 +3093,7 @@ private static void NativeCompletionModuleCommands(CompletionContext context, st | |
| } | ||
| } | ||
|
|
||
| var moduleResults = CompleteModuleName(context, loadedModulesOnly); | ||
| var moduleResults = CompleteModuleName(context, loadedModulesOnly, skipEditionCheck); | ||
| if (moduleResults != null && moduleResults.Count > 0) | ||
| result.AddRange(moduleResults); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,6 +84,18 @@ public sealed class GetModuleCommand : ModuleCmdletBase, IDisposable | |
| [ArgumentCompleter(typeof(PSEditionArgumentCompleter))] | ||
| public string PSEdition { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// When set, CompatiblePSEditions checking is disabled for modules in the System32 (Windows PowerShell) module directory. | ||
| /// </summary> | ||
| [Parameter(ParameterSetName = ParameterSet_AvailableLocally)] | ||
| [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession)] | ||
| [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession)] | ||
| public SwitchParameter SkipEditionCheck | ||
| { | ||
| get { return (SwitchParameter)BaseSkipEditionCheck; } | ||
| set { BaseSkipEditionCheck = value; } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// If specified, then Get-Module refreshes the internal cmdlet analysis cache | ||
| /// </summary> | ||
|
|
@@ -341,6 +353,18 @@ protected override void ProcessRecord() | |
| ThrowTerminatingError(error); | ||
| } | ||
|
|
||
| // -SkipEditionCheck only makes sense for -ListAvailable (otherwise the module is already loaded) | ||
| if (SkipEditionCheck && !ListAvailable) | ||
| { | ||
| ErrorRecord error = new ErrorRecord( | ||
| new InvalidOperationException(Modules.SkipEditionCheckNotSupportedWithoutListAvailable), | ||
| nameof(Modules.SkipEditionCheckNotSupportedWithoutListAvailable), | ||
| ErrorCategory.InvalidOperation, | ||
| targetObject: null); | ||
|
|
||
| ThrowTerminatingError(error); | ||
| } | ||
|
|
||
| var strNames = new List<string>(); | ||
| if (Name != null) | ||
| { | ||
|
|
@@ -423,27 +447,12 @@ private void AssertNameDoesNotResolveToAPath(string[] names, string stringFormat | |
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Determine whether a module info matches a given module specification table and specified PSEdition value. | ||
| /// </summary> | ||
| /// <param name="moduleInfo"></param> | ||
| /// <param name="moduleSpecTable"></param> | ||
| /// <param name="edition"></param> | ||
| /// <returns></returns> | ||
| private static bool ModuleMatch(PSModuleInfo moduleInfo, IDictionary<string, ModuleSpecification> moduleSpecTable, string edition) | ||
| { | ||
| ModuleSpecification moduleSpecification; | ||
| return (String.IsNullOrEmpty(edition) || moduleInfo.CompatiblePSEditions.Contains(edition, StringComparer.OrdinalIgnoreCase)) && | ||
| (!moduleSpecTable.TryGetValue(moduleInfo.Name, out moduleSpecification) || ModuleIntrinsics.IsModuleMatchingModuleSpec(moduleInfo, moduleSpecification)); | ||
| } | ||
|
|
||
| private void GetAvailableViaCimSession(IEnumerable<string> names, IDictionary<string, ModuleSpecification> moduleSpecTable, | ||
| CimSession cimSession, Uri resourceUri, string cimNamespace) | ||
| { | ||
| var remoteModules = GetAvailableViaCimSessionCore(names, cimSession, resourceUri, cimNamespace); | ||
| IEnumerable<PSModuleInfo> remoteModules = GetAvailableViaCimSessionCore(names, cimSession, resourceUri, cimNamespace); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why change this from
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can change it back if desired, but my reasoning is:
But that is definitely all opinion! As I say, I can revert it if the previous style is preferred. |
||
|
|
||
| foreach (var remoteModule in remoteModules.Where(remoteModule => ModuleMatch(remoteModule, moduleSpecTable, PSEdition)) | ||
| ) | ||
| foreach (PSModuleInfo remoteModule in FilterModulesForEditionAndSpecification(remoteModules, moduleSpecTable)) | ||
| { | ||
| RemoteDiscoveryHelper.AssociatePSModuleInfoWithSession(remoteModule, cimSession, resourceUri, | ||
| cimNamespace); | ||
|
|
@@ -453,10 +462,9 @@ private void GetAvailableViaCimSession(IEnumerable<string> names, IDictionary<st | |
|
|
||
| private void GetAvailableViaPsrpSession(string[] names, IDictionary<string, ModuleSpecification> moduleSpecTable, PSSession session) | ||
| { | ||
| var remoteModules = GetAvailableViaPsrpSessionCore(names, session.Runspace); | ||
| IEnumerable<PSModuleInfo> remoteModules = GetAvailableViaPsrpSessionCore(names, session.Runspace); | ||
|
|
||
| foreach (var remoteModule in remoteModules.Where(remoteModule => ModuleMatch(remoteModule, moduleSpecTable, PSEdition)) | ||
| ) | ||
| foreach (PSModuleInfo remoteModule in FilterModulesForEditionAndSpecification(remoteModules, moduleSpecTable)) | ||
| { | ||
| RemoteDiscoveryHelper.AssociatePSModuleInfoWithSession(remoteModule, session); | ||
| this.WriteObject(remoteModule); | ||
|
|
@@ -465,14 +473,10 @@ private void GetAvailableViaPsrpSession(string[] names, IDictionary<string, Modu | |
|
|
||
| private void GetAvailableLocallyModules(string[] names, IDictionary<string, ModuleSpecification> moduleSpecTable, bool all) | ||
| { | ||
| var refresh = Refresh.IsPresent; | ||
| var modules = GetModule(names, all, refresh); | ||
|
|
||
| foreach ( | ||
| var psModule in | ||
| modules.Where(module => ModuleMatch(module, moduleSpecTable, PSEdition)).Select(module => new PSObject(module)) | ||
| ) | ||
| IEnumerable<PSModuleInfo> modules = GetModule(names, all, Refresh); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we use
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Used
But, perhaps there are benefits to using a
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see IEnumerable benefits:
I believe we get more simple (readable and debuggable) code with List or Collection. |
||
| foreach (PSModuleInfo module in FilterModulesForEditionAndSpecification(modules, moduleSpecTable)) | ||
| { | ||
| var psModule = new PSObject(module); | ||
| psModule.TypeNames.Insert(0, "ModuleInfoGrouping"); | ||
| WriteObject(psModule); | ||
| } | ||
|
|
@@ -482,14 +486,74 @@ private void GetLoadedModules(string[] names, IDictionary<string, ModuleSpecific | |
| { | ||
| var modulesToWrite = Context.Modules.GetModules(names, all); | ||
|
|
||
| foreach ( | ||
| var moduleInfo in | ||
| modulesToWrite.Where(moduleInfo => ModuleMatch(moduleInfo, moduleSpecTable, PSEdition)) | ||
| ) | ||
| foreach (PSModuleInfo moduleInfo in FilterModulesForEditionAndSpecification(modulesToWrite, moduleSpecTable)) | ||
| { | ||
| WriteObject(moduleInfo); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Filter an enumeration of PowerShell modules based on the required PowerShell edition | ||
| /// and the module specification constraints set for each module (if any). | ||
| /// </summary> | ||
| /// <param name="modules">The modules to filter through.</param> | ||
| /// <param name="moduleSpecificationTable">Module constraints, keyed by module name, to filter modules of that name by.</param> | ||
| /// <returns>All modules from the original input that meet both any module edition and module specification constraints provided.</returns> | ||
| private IEnumerable<PSModuleInfo> FilterModulesForEditionAndSpecification( | ||
| IEnumerable<PSModuleInfo> modules, | ||
| IDictionary<string, ModuleSpecification> moduleSpecificationTable) | ||
| { | ||
| #if !UNIX | ||
| // Edition check only applies to Windows System32 module path | ||
| if (!SkipEditionCheck && ListAvailable && !All) | ||
| { | ||
| modules = modules.Where(module => module.IsConsideredEditionCompatible); | ||
| } | ||
| #endif | ||
|
|
||
| if (!String.IsNullOrEmpty(PSEdition)) | ||
| { | ||
| modules = modules.Where(module => module.CompatiblePSEditions.Contains(PSEdition, StringComparer.OrdinalIgnoreCase)); | ||
| } | ||
|
|
||
| if (moduleSpecificationTable != null && moduleSpecificationTable.Count > 0) | ||
| { | ||
| modules = FilterModulesForSpecificationMatch(modules, moduleSpecificationTable); | ||
| } | ||
|
|
||
| return modules; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Take an enumeration of modules and only return those that match a specification | ||
| /// in the given specification table, or have no corresponding entry in the specification table. | ||
| /// </summary> | ||
| /// <param name="modules">The modules to filter by specification match.</param> | ||
| /// <param name="moduleSpecificationTable">The specification lookup table to filter the modules on.</param> | ||
| /// <returns>The modules that match their corresponding table entry, or which have no table entry.</returns> | ||
| private static IEnumerable<PSModuleInfo> FilterModulesForSpecificationMatch( | ||
| IEnumerable<PSModuleInfo> modules, | ||
| IDictionary<string, ModuleSpecification> moduleSpecificationTable) | ||
| { | ||
| Dbg.Assert(moduleSpecificationTable != null, $"Caller to verify that {nameof(moduleSpecificationTable)} is not null"); | ||
| Dbg.Assert(moduleSpecificationTable.Count != 0, $"Caller to verify that {nameof(moduleSpecificationTable)} is not empty"); | ||
|
|
||
| foreach (PSModuleInfo module in modules) | ||
| { | ||
| // 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 | ||
| if (ModuleIntrinsics.IsModuleMatchingModuleSpec(module, moduleSpecification)) | ||
| { | ||
| yield return module; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -513,5 +577,4 @@ public IEnumerable<CompletionResult> CompleteArgument(string commandName, string | |
| } | ||
| } | ||
| } | ||
| } // Microsoft.PowerShell.Commands | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Named parameters look more readable (what is "result" here?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very much agree with you here. Sadly the parameter name is also
result.