PowerShell / PowerShell/PSScriptAnalyzer

Migrate away from MEF for loading DLLs in Windows PowerShell

オープン
#1,154 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Area - Rules Issue - Enhancement
主要言語
C#
スター
2.2k
フォーク
414
平均マージ
13時間 1分
マージ済み PR(30日)
2

説明

PSScriptAnalyzer's .NET Framework builds depend on a lesser known technology called the Managed Extensibility Framework (namespaced to System.ComponentModel.Composition) to do a kind of lazy loading of assemblies for rules, presumably for custom rule sets.

This seems to be a kind of inversion of control framework, although we use it in a less typical way than a web server might:

https://github.com/PowerShell/PSScriptAnalyzer/blob/cfeb7d5f510763ba33a9a4ec73259d7d6ea86fdb/Engine/ScriptAnalyzer.cs#L930-L969

It also means that for rules to be discoverable they need to be decorated with attributes like here:
https://github.com/PowerShell/PSScriptAnalyzer/blob/cfeb7d5f510763ba33a9a4ec73259d7d6ea86fdb/Rules/AvoidGlobalFunctions.cs#L18-L20

All of that is fine, and it may be that PSScriptAnalyzer users are having success with it, but I'm not really sure if anyone is using it.

There are two problems:

  • We aren't using (and don't seem to need) MEF in PowerShell Core (it apparently has been ported, but it's not clear to what extent).
  • MEF loads assemblies differently to PowerShell

This problem raised itself in https://github.com/PowerShell/PSScriptAnalyzer/pull/1133, where a rule that depended on an external assembly, which in turn depended on a third assembly failed to load only in .NET Framework (and behaved differently across PowerShell versions).

The error looked like this:

System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
   at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   at System.Reflection.Assembly.GetTypes()
   at System.ComponentModel.Composition.Hosting.AssemblyCatalog.get_InnerCatalog()
   at System.ComponentModel.Composition.Hosting.AssemblyCatalog.GetEnumerator()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Microsoft.Windows.PowerShell.ScriptAnalyzer.SafeDirectoryCatalog..ctor(String folderLocation, IOutputWriter outputWriter)
System.ComponentModel.Composition.CompositionException: The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) Could not load file or assembly 'Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
Resulting in: An exception occurred while trying to create an instance of type 'Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands'.
Resulting in: Cannot activate part 'Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands'.
Element: Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands -->  Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands -->  AssemblyCatalog (Assembly="Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules, Version=1.17.1.0, Culture=neutral, PublicKeyToken=null")
Resulting in: Cannot get export 'Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands (ContractName="Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.IScriptRule")' from part 'Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands'.
Element: Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands (ContractName="Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.IScriptRule") -->  Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseCompatibleCommands -->  AssemblyCatalog (Assembly="Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules, Version=1.17.1.0, Culture=neutral, PublicKeyToken=null")
Resulting in: Cannot set import 'Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer.ScriptRules (ContractName="Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.IScriptRule")' on part 'Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer'.
Element: Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer.ScriptRules (ContractName="Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.IScriptRule") -->  Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer
   at System.ComponentModel.Composition.CompositionResult.ThrowOnErrors(AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ComposablePartExportProvider.Compose(CompositionBatch batch)
   at Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer.LoadRules(Dictionary`2 result, CommandInvocationIntrinsics invokeCommand, Boolean loadBuiltInRules)

At some point, the compatibility rule would depend on Microsoft.PowerShell.CrossCompatibility.dll, which in turn depended on Newtonsoft.Json.dll.

The first load would succeed, but the second would fail because it would only look for Newtonsoft.Json.dll in the directory of powershell.exe rather than in the same directory as Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll.

This was resolved by adding Add-Type $newtonsoftDllPath to ScriptAnalyzer.psm1.

This may not actually be due to MEF, but is worth investigating in any case. The DLL loading differences between Windows PS and PS Core are here:

https://github.com/PowerShell/PSScriptAnalyzer/blob/cfeb7d5f510763ba33a9a4ec73259d7d6ea86fdb/Engine/ScriptAnalyzer.cs#L930-L969

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、930-969行付近の Engine/ScriptAnalyzer.cs にある DLL 読み込みパスを確認し、次に ScriptAnalyzer.psm1 と Rules/AvoidGlobalFunctions.cs のルールメタデータを読みます。Issue に記載されている Windows PowerShell と PowerShell Core の動作を、pull request 1133 で発生した失敗と比較します。完了の条件は、文書化されテスト済みの置き換えが用意されるか、MEF を削除すべきかどうかが明確に解決されることです。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
csharp, powershell
領域
devtools
issue の種類
リファクタリング
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
説明が足りない
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。