Skip to content
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

New PowerShell compatibility rules #1133

Merged
merged 195 commits into from
Feb 28, 2019

Conversation

rjmholt
Copy link
Contributor

@rjmholt rjmholt commented Jan 29, 2019

PR Summary

Resolves #805.

Adds three new rules:

  • UseCompatibleCommands: checks the compatibility of cmdlets and functions available by default with different PowerShell versions/platforms (effectively outmodes UseCompatibleCmdlets)
  • UseCompatibleTypes: checks the compatibility of types/.NET APIs available by default with different PowerShell versions/platforms
  • UseCompatibleSyntax: a simpler, by-hand rule that checks for known bad syntaxes (like [type]::new() and using module MyModule) when given PowerShell versions are targeted

The first two rules rely on profiles collected from PowerShell environments. The tools to collect this are included with the PR.

Done since opening PR

  • Fix typename serialisation in JSON
  • Add type compatibility check rule
  • Add more testing
  • Document code properly
  • Collect profiles on more platforms

PR Checklist

@bergmeister
Copy link
Collaborator

bergmeister commented Jan 29, 2019

@rjmholt Please resolve the merge conflict or let me know if you need help with that.
That's a lot of code to review, please be patient. Can you make a statement whether this includes breaking changes in terms of the way the rule is being configured?
Also do not hesitate to ask if you have questions/problems getting CI to pass, I myself have spent quite a bit with it in the past and might be able to help or give pointer, the same applies also to running tests locally as there are 2 ways (.\build.ps1 -Test or installing the module manually and then invoking Pester like it is done in CI can lead to different results sometimes depending on various things like OS, PS edition, locale, etc.).

@rjmholt rjmholt requested a review from SteveL-MSFT January 29, 2019 19:46
@rjmholt
Copy link
Contributor Author

rjmholt commented Jan 29, 2019

Hi @bergmeister, sorry this is so big. Wasn't meant to be at the outset. When I'm closer to being done, I'll try and comment on areas where I think I need extra scrutiny.

Please resolve the merge conflict

Easy! Will do.

That's a lot of code to review, please be patient

Yes, absolutely.

Can you make a statement whether this includes breaking changes in terms of the way the rule is being configured?

My immediate intention is to break nothing. For now there's a totally new rule called UseCompatibleCmdlets2. I figured I would worry about the name later. But the only real "breakage" is that you might get double diagnostics for compatibility.

Also do not hesitate to ask if you have questions/problems getting CI to pass, I myself have spent quite a bit with it in the past and might be able to help or give pointer, the same applies also to running tests locally as there are 2 ways (.\build.ps1 -Test or installing the module manually and then invoking Pester like it is done in CI can lead to different results sometimes depending on various things like OS, PS edition, locale, etc.).

Will do! I'm not quite there yet, but haven't had too much trouble figuring out the build or testing yet. I'll let you know if I'm hitting my head against anything.

@rjmholt rjmholt requested a review from SydneyhSmith January 29, 2019 21:14
@rjmholt rjmholt force-pushed the compatibility-rules branch from 27bc01f to 4420f0f Compare January 29, 2019 22:17
Copy link
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will take some time to review the whole thing, some initial comments

CrossCompatibility/CrossCompatibility.psd1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
@rjmholt rjmholt changed the title WIP: New PowerShell compatibility rules New PowerShell compatibility rules Feb 5, 2019
Copy link
Contributor Author

@rjmholt rjmholt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done all the code changes I think I need at present. Looking for input on:

  • What other tests I should write
  • How to fix my current build breakages (documentation tests, etc.)
  • Feedback on profile collection stuff

Ideally the only changes I make at this point are:

  • More tests
  • More documentation
  • More comments
  • Possible profile updates

However, I'm removing the WIP tag mainly so that I can get feedback

@@ -0,0 +1,1610 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is quite important, since it's used for the profile collection itself. So it's worth looking over to see if I'm making any mistakes and either missing or including commands/types I shouldn't be.


public string Id { get; }

public HashSet<string> ConstituentProfiles { get; }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have preferred this to be something readonly, but there's no way to wrap a HashSet<string> in a way that's both readonly and work with .NET452

CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
Tests/Rules/UseCompatibleTypes.Tests.ps1 Show resolved Hide resolved
build.psm1 Outdated Show resolved Hide resolved
build.psm1 Show resolved Hide resolved
build.psm1 Show resolved Hide resolved
build.psm1 Show resolved Hide resolved
.gitignore Outdated Show resolved Hide resolved
Copy link
Collaborator

@bergmeister bergmeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a first glance at it and made some comments and commits myself. Looks OK so far but I am a bit worried about performance since I have done a prototype last year to include script based rules in PSSA itself and found that execution time went up by a factor of 10. Similar to the already existing PSUseCompatibleCmdlets rule, it is probably best to run it not by default unless it is configured.
In terms of the build, have a look at the structure for PS v3/4 in the output and build.psm1 to get the WMF4 build to work as well, it is best to partially integrate your crosscompatibilty build.ps1 the main build.ps1 and then everything should just work fine end-to-end.
Tip: Focus first on the Windows PowerShell build, there can sometimes be quirks with some PowerShell Core tests in AppVeyor (due to the environment), I can help you with that once the Windows PowerShell build is green.
I am focusing on the integration of this rule into PSSA, not the actual rule itself. Especially since I am not an expert on most of the stuff that you did, please get someone from MSFT to review your main code.
Let me know if that is OK so far in terms of feedback/progression from my side. We can also schedule a Skype call later if it is easier to discuss/debug things together...
One final question: Is there a reason we keep the old rule (i.e. can the old rule do something that your new rules can't?)

CrossCompatibility/CrossCompatibility.psd1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility/Query/RuntimeData.cs Outdated Show resolved Hide resolved
CrossCompatibility/Tests/UtilityApi.Tests.ps1 Outdated Show resolved Hide resolved
Rules/CompatibilityRules/CompatibilityRule.cs Outdated Show resolved Hide resolved
global.json Show resolved Hide resolved
Engine/Engine.csproj Outdated Show resolved Hide resolved
Rules/CompatibilityRules/CompatibilityRule.cs Show resolved Hide resolved
Copy link
Collaborator

@bergmeister bergmeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rjmholt Awesome to see the progress from day to day. I think we are getting closer to the finish and see the light at the end of the tunnel, if it continues like this then we could maybe merge next week (assuming other MSFT people will have reviewed the core code as well). I will also take some more time the next days to review some more details of the changes in the core PSSA code that look like general improvements to me.
I now see that the crosscompat dll and rules do not get loaded automatically when running the default rules, that is good and important for performance (especially thinking of users of the ps vscode extension).
The PowerShell 5 and 6 builds are now green, thumbs up. The failing test on Ubuntu is a known culprit that sometimes starts to appear when updating the .Net Core SDK for some reason. Was there a reason why you upgraded PSSA from 2.2.102 to 2.2.103? Reverting to the old version would fix this test and make the Ubuntu build green.
Running it locally using PowerShell 6 seems to be fine but when using PowerShell 5.1 (Windows 10 1809), I see this error in the verbose log when running it the first time (does not appear when re-running the command in the same shell). The same error is visible also in the currently failing WMF4 build so I think it is definitely worthwhile investigating and fixing

C:\Users\cberg\git\PSSA_rjimholt\out\PSScriptAnalyzer [compatibility-rules ≡]> ipmo .\PSScriptAnalyzer.psd1
C:\Users\cberg\git\PSSA_rjimholt\out\PSScriptAnalyzer [compatibility-rules ≡]> Invoke-ScriptAnalyzer -ScriptDefinition foo -Verbose
VERBOSE: Settings object could not be resolved.
WARNING: 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)
VERBOSE: Analyzing Script Definition.
...

Engine/ScriptAnalyzer.cs Outdated Show resolved Hide resolved
@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 7, 2019

@bergmeister Just looking into the WMF 4 problem. We haven't had any ideas on what might be causing it (the hashes of the all the Newtonsoft.Json.dll assemblies seem to be the same everywhere). I'm currently setting up a Win2012 machine to investigate.

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 8, 2019

Not sure what's going on with this Newtonsoft.Json issue. @JamesWTruher and I fired up a Win2012 VM and got all the tests passing. I think we may need to go over the csproj with a fine-toothed comb.

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 8, 2019

Also struggling to reproduce the Ubuntu error -- not getting it in my VM.

@bergmeister
Copy link
Collaborator

bergmeister commented Feb 11, 2019

@rjmholt If it is easier then you could exclude your rule from compilation for v3/4. There are already some rules and functionality that is not available in ps v3/4. One rule is even missing in ps 6 (avoidusingsinularNouns) due to missing .net APIs anyway.

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 13, 2019

testpass

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 13, 2019

I'm still not able to replicate this problem in 2012R2

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 13, 2019

@rjmholt rjmholt force-pushed the compatibility-rules branch from c02d143 to 2ff8b5d Compare February 14, 2019 18:55
Engine/PSScriptAnalyzer.psm1 Outdated Show resolved Hide resolved
@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 15, 2019

Ok @bergmeister, @JamesWTruher, @SteveL-MSFT I have the build passing and in a state to review now. It's also building and testing the CrossCompatibility module. I've added 272 tests.

//cc @TylerLeonhardt + @daxian-dbw (if you've got time to give a quick review as well)

Also @SydneyhSmith - this is now ready.

[EnumMember]
Set
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eol

throw new ArgumentNullException(nameof(versionStr));
}

int[] versionParts = new int[3] { -1, -1, -1 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be simpler like this: (sudo code in PS )

$versionStr = "6.2.0-preview.2"
$parts = $versionStr.Split("-")
if ($parts.Count -gt 1) { $label = $parts[1] }
$versionParts = $parts[0].Split('.') | % { [int]::Parse($_) }


Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed this would be simpler. I'm going to leave it as is for now since it's currently working in its present form with some tests. We can change it later if required I think.

Copy link
Contributor

@JamesWTruher JamesWTruher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked through the .psm1 file - just a couple of things that really need to be fixed, I'll continue to review, but it's pretty big

CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
CrossCompatibility/CrossCompatibility.psm1 Outdated Show resolved Hide resolved
Copy link
Collaborator

@bergmeister bergmeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rjmholt Sounds good. I assume nothing has changed substantially since I last tested it locally as the build is still green, do you want me to give it a final(!) check (otherwise I'd trust you)? As long as one other member of the PS team signs this PR off as well, then feel free to merge. Yes, I agree to raise items for other possible improvements but we can just take the 'minimum viable' and 'YAGNI' approach and improve later based on feedback IMHO.

Copy link
Collaborator

@bergmeister bergmeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a quick look at the changes again and they still look good but I found 2 issues when running ipmo .\out\PSScriptAnalyzer\PSScriptAnalyzer.psd1; Invoke-ScriptAnalyzer -ScriptDefinition gci -Verbose

  • The Verbose log tells me that the new rules don't get run by default, which is good. But it tells me that the CrossCompatibility DLL gets loaded. If we cannot avoid this then this is fine. Just something that I noticed. Performance wise, I don't see a negative impact so it is probably fine
  • When run in Windows PowerShell 5.1 (Windows 1 1809), I still get the following ReflectionTypeLoadException the first time it runs in the verbose log (you can ignore the verbose message about the settings object btw, this is just a red herring message that always shows up when using the -ScriptDefinitiion parameter). This does not happen in PowerShell Core btw.
VERBOSE: Settings object could not be resolved.
WARNING: 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)

At least the exception should be fixed or explained before merging, the rest looks fine to me.

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 27, 2019

Ok, I've just gotten signing and release building working all the way through, so that the compatibility module (now called "PSCompatibilityAnalyzer") can be released independently on the gallery

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 27, 2019

When run in Windows PowerShell 5.1 (Windows 1 1809), I still get the following ReflectionTypeLoadException the first time it runs in the verbose log (you can ignore the verbose message about the settings object btw, this is just a red herring message that always shows up when using the -ScriptDefinitiion parameter). This does not happen in PowerShell Core btw.

I suspect this is the problem occurring when MEF doesn't understand DLL loading the way PowerShell does.

The Add-Type I added fixes it, but also breaks the whole lazy load concept with MEF (the [Export] attributes)... My personal feeling is that MEF just needs to be removed in favour of how PSCore loads things, but that's a discussion for another time.

I'll just expand the Add-Type and see if that works.

This issue is probably also related to the CrossCompatibility.dll being loaded by default. That would be easy to fix if we fix the loading logic.

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 27, 2019

@JamesWTruher, @SteveL-MSFT, @daxian-dbw this PR is now in a "complete" state and is ready for signoff. Please take a quick look if you can.

@rjmholt
Copy link
Contributor Author

rjmholt commented Feb 27, 2019

@bergmeister looks like that fix did the trick

Copy link
Contributor

@JamesWTruher JamesWTruher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for addressing my issues

@JamesWTruher
Copy link
Contributor

@SteveL-MSFT , @bergmeister if you guys are ok with this - I will merge

@bergmeister
Copy link
Collaborator

LGTM. Creating an issue in PSSA or the PowerShell docs to explain the MEF issue of WPS would be good though.

@@ -0,0 +1,57 @@
using System;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that all files have the copyright header

@@ -0,0 +1,398 @@
using System;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing copyright header

@rjmholt rjmholt requested a review from SteveL-MSFT February 28, 2019 21:12
Copy link
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

BeforeAll {
if ($IsCoreCLR)
{
$pwshExe = (Get-Process -Id $PID).Path
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this, now this test should not fail any more when updating the .Net Core SDK :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rules to check compatibility with PSCore6
5 participants