-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Proposal: Checkstyle Standards for Powershell Modules
Author: Jordan Borean @jborean93
Date: 2017/05/21
- Status: New
- Proposal type: core design
- Targeted Release: 2.4
- Associated PR: TBC
- Estimated time to implement: 4 weeks
Motivation
Trying to keep the Powershell modules consistent with each other and define
a standard that is enforcable without human intervention.
Problems
Some of the current problems we have today are;
- There is a mixture of ways Powershell modules are written which causes confusion and fragmentation
- The Python modules use PEP for standards but no automated checked are in place for Powershell
- There is also no official standard for how we write these modules, this proposal will both create that standard and enforce it
Solution proposal
The proposed solution is to use 2 Powershell modules called PSScriptAnalyzer
and Pester to run a check style analysis
as well as generate reports on this analysis in a CI like fashion. These tools
should run on the module affected on every pull request.
PSScriptAnalyzer is a tool that can run checkstyle analysis on powershell
scripts based on a custom ruleset. It has the ability to specify rules that you
wish to scan for as well as writting custom rules if the in built ones don't
match your requirements.
Pester is a framework for running tests that can easily integrate in a CI
system. While it has a lot of features we can use in the future like unit
testing for powershell modules and code coverage reporting this proposal is
limited to using Pester to run PSScriptAnalyzer and generate an NUnit XML
report at the end of the process. There are numerous tools out there to convert
an NUnit xml to HTML for reporting purposes.
While these modules need to run in Powershell with the ability to install
Powershell 5 on both Windows and Unix we have various options available to us
when running these tests.
The following rule settings for PSScriptAnalyzer is as follows
@{
IncludeRules = @(
'PSAvoidUsingCmdletAliases',
'PSAvoidUsingEmptyCatchBlock',
'PSAvoidUsingPositionalParameters',
'PSAvoidUsingWMICmdlet',
'PSAvoidUsingWriteHost',
'PSMisleadingBacktick',
'PSPlaceCloseBrace',
'PSPlaceOpenBrace',
'PSReservedCmdletChar',
'PSReservedParams',
'PSUseConsistentIndentation',
'PSUseConsistentWhitespace'
)
Rules = @{
PSAvoidUsingCmdletAliases = @{
Whitelist = @('')
}
PSPlaceCloseBrace = @{
Enable = $true
NoEmptyLineBefore = $true
IgnoreOneLineBlock = $false
NewLineAfter = $false
}
PSPlaceOpenBrace = @{
Enable = $true
OnSameLine = $true
IgnoreOneLineBlock = $false
NewLineAfter = $true
}
PSUseConsistentIndentation = @{
Enable = $true
IndentationSize = 4
}
PSUseConsistentWhitespace = @{
Enable = $true
CheckOpenBrace = $true
CheckOpenParen = $true
CheckOperator = $true
CheckSeparator = $true
}
}
}
The documentation for these rules can be found here. These rules are
open to debug they are just what I believe we should be aiming for but happy to
add/remove/modify rules based on a consensus with the community.
The second part of this proposal is to go through our existing modules and
ensure they conform to this new standard. Because of the different coding
styles in place it is hard to.
As well as implementing these checks into the existing testing process we will
also need to modify the existing modules to ensure they pass the new checks
added.
An example of the output of running this check on an existing module like
win_acl
PS C:\Users\jbore> powershell.exe -File C:\dev\ansible\test\runner\powershell-pester.ps1
Describing Testing against PSSA rules
Context PSSA Ansible Rules
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
PSAvoidUsingCmdletAliases Warning win_acl.ps 67 'where' is an alias of 'Where-Object'. Alias can
1 introduce possible problems and make scripts hard to
maintain. Please consider changing alias to its full
content.
[-] Should pass PSAvoidUsingCmdletAliases 1.03s
Expected: {0}
But was: {1}
13: $failures.Count | Should Be 0
at <ScriptBlock>, C:\dev\ansible\test\runner\pester-tests.ps1: line 13
[+] Should pass PSAvoidUsingEmptyCatchBlock 127ms
[+] Should pass PSAvoidUsingPositionalParameters 19ms
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
PSAvoidUsingWMICmdlet Warning win_acl.ps 67 File 'win_acl.ps1' uses WMI cmdlet. For PowerShell 3.0 and
1 above, use CIM cmdlet which perform the same tasks as the
WMI cmdlets. The CIM cmdlets comply with WS-Management
(WSMan) standards and with the Common Information Model
(CIM) standard, which enables the cmdlets to use the same
techniques to manage Windows computers and those running
other operating systems.
[-] Should pass PSAvoidUsingWMICmdlet 30ms
Expected: {0}
But was: {1}
13: $failures.Count | Should Be 0
at <ScriptBlock>, C:\dev\ansible\test\runner\pester-tests.ps1: line 13
[+] Should pass PSAvoidUsingWriteHost 25ms
[+] Should pass PSMisleadingBacktick 20ms
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
PSPlaceCloseBrace Warning win_acl.ps 41 Close brace does not follow a non-empty line.
1
[-] Should pass PSPlaceCloseBrace 28ms
Expected: {0}
But was: {1}
13: $failures.Count | Should Be 0
at <ScriptBlock>, C:\dev\ansible\test\runner\pester-tests.ps1: line 13
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
PSPlaceOpenBrace Warning win_acl.ps 29 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 37 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 39 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 43 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 48 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 54 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 59 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 65 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 69 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 74 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 78 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 82 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 100 Open brace not on same line as preceding keyword. It should
1 be on the same line.
PSPlaceOpenBrace Warning win_acl.ps 238 Open brace not on same line as preceding keyword. It should
1 be on the same line.
[-] Should pass PSPlaceOpenBrace 75ms
Expected: {0}
But was: {14}
13: $failures.Count | Should Be 0
at <ScriptBlock>, C:\dev\ansible\test\runner\pester-tests.ps1: line 13
[+] Should pass PSReservedCmdletChar 28ms
[+] Should pass PSReservedParams 20ms
[+] Should pass PSUseConsistentIndentation 23ms
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
PSUseConsistentWhitespace Warning win_acl.ps 29 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 37 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 39 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 43 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 48 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 54 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 59 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 65 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 69 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 74 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 78 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 82 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 87 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 100 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 238 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 282 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 297 Use space before open brace.
1
PSUseConsistentWhitespace Warning win_acl.ps 282 Use space before open parenthesis.
1
PSUseConsistentWhitespace Warning win_acl.ps 297 Use space before open parenthesis.
1
PSUseConsistentWhitespace Warning win_acl.ps 198 Use space before and after binary and assignment operators.
1
PSUseConsistentWhitespace Warning win_acl.ps 199 Use space before and after binary and assignment operators.
1
PSUseConsistentWhitespace Warning win_acl.ps 200 Use space before and after binary and assignment operators.
1
PSUseConsistentWhitespace Warning win_acl.ps 264 Use space before and after binary and assignment operators.
1
PSUseConsistentWhitespace Warning win_acl.ps 267 Use space before and after binary and assignment operators.
1
PSUseConsistentWhitespace Warning win_acl.ps 107 Use space after a comma.
1
PSUseConsistentWhitespace Warning win_acl.ps 225 Use space after a comma.
1
PSUseConsistentWhitespace Warning win_acl.ps 226 Use space after a comma.
1
PSUseConsistentWhitespace Warning win_acl.ps 229 Use space after a comma.
1
PSUseConsistentWhitespace Warning win_acl.ps 229 Use space after a comma.
1
PSUseConsistentWhitespace Warning win_acl.ps 295 Use space after a comma.
1
[-] Should pass PSUseConsistentWhitespace 142ms
Expected: {0}
But was: {30}
13: $failures.Count | Should Be 0
at <ScriptBlock>, C:\dev\ansible\test\runner\pester-tests.ps1: line 13
Tests completed in 1.58s
Passed: 7, Failed: 5, Skipped: 0, Pending: 0, Inconclusive: 0
PS C:\Users\jbore> $LASTEXITCODE
5
There are some very basic scripts I created for the output above. You can see
them here.
Dependencies
- Powershell 5.0
- PSScriptAnalyzer
- Pester
Testing
These tests only need to run on the module that is affected in the PR, there is
no reason that we need to run it on all modules but that is possible if we wish
to go down that path.
Documentation
New docs page created that will show the rules and examples that we are
enforcing as well as some guidelines on how to run these tests locally
before raising a PR.
Anything else?
While this is not part of the scope for this proposal I believe using a library
like Pester brings us a step forward to implementing unit tests on our
powershell modules and bringing more in line with our processes around Python
modules.
This is my first proposal so let me know if I have missed anything or whether I
have done something wrong.