Skip to content

Commit 0eee586

Browse files
author
Kapil Borle
authored
Merge pull request #803 from PowerShell/kapilmb/update-settings
Add CustomRulePath property to settings file
2 parents 447eca6 + 4027f9a commit 0eee586

9 files changed

+221
-13
lines changed

.build.ps1

+6-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ function Get-ResourceTaskParam($project) {
113113
@{
114114
Inputs = "$project/Strings.resx"
115115
Outputs = "$project/Strings.cs"
116-
Jobs = {& "$resourceScript $project"}
116+
Data = $project
117+
Jobs = {
118+
Push-Location $BuildRoot
119+
& $resourceScript $Task.Data
120+
Pop-Location
121+
}
117122
Before = "$project/build"
118123
}
119124
}

.vscode/settings.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Place your settings in this file to overwrite default and user settings.
2+
{
3+
"editor.tabSize": 4,
4+
"powershell.codeFormatting.preset": "Allman"
5+
}

Engine/Commands/InvokeScriptAnalyzerCommand.cs

+35-2
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ protected override void BeginProcessing()
265265
ProcessPath();
266266
}
267267

268+
string[] combRulePaths = null;
269+
var combRecurseCustomRulePath = RecurseCustomRulePath.IsPresent;
270+
var combIncludeDefaultRules = IncludeDefaultRules.IsPresent;
268271
try
269272
{
270273
var settingsObj = PSSASettings.Create(
@@ -274,7 +277,30 @@ protected override void BeginProcessing()
274277
if (settingsObj != null)
275278
{
276279
ScriptAnalyzer.Instance.UpdateSettings(settingsObj);
280+
281+
// For includeDefaultRules and RecurseCustomRulePath we override the value in the settings file by
282+
// command line argument.
283+
combRecurseCustomRulePath = OverrideSwitchParam(
284+
settingsObj.RecurseCustomRulePath,
285+
"RecurseCustomRulePath");
286+
combIncludeDefaultRules = OverrideSwitchParam(
287+
settingsObj.IncludeDefaultRules,
288+
"IncludeDefaultRules");
277289
}
290+
291+
// Ideally we should not allow the parameter to be set from settings and command line
292+
// simultaneously. But since, this was done before with IncludeRules, ExcludeRules and Severity,
293+
// we use the same strategy for CustomRulePath. So, we take the union of CustomRulePath provided in
294+
// the settings file and if provided on command line.
295+
var settingsCustomRulePath = Helper.ProcessCustomRulePaths(
296+
settingsObj?.CustomRulePath?.ToArray(),
297+
this.SessionState,
298+
combRecurseCustomRulePath);
299+
combRulePaths = rulePaths == null
300+
? settingsCustomRulePath
301+
: settingsCustomRulePath == null
302+
? rulePaths
303+
: rulePaths.Concat(settingsCustomRulePath).ToArray();
278304
}
279305
catch
280306
{
@@ -285,11 +311,11 @@ protected override void BeginProcessing()
285311

286312
ScriptAnalyzer.Instance.Initialize(
287313
this,
288-
rulePaths,
314+
combRulePaths,
289315
this.includeRule,
290316
this.excludeRule,
291317
this.severity,
292-
null == rulePaths ? true : this.includeDefaultRules,
318+
combRulePaths == null || combIncludeDefaultRules,
293319
this.suppressedOnly);
294320
}
295321

@@ -388,6 +414,13 @@ private bool IsFileParameterSet()
388414
return String.Equals(this.ParameterSetName, "File", StringComparison.OrdinalIgnoreCase);
389415
}
390416

417+
private bool OverrideSwitchParam(bool paramValue, string paramName)
418+
{
419+
return MyInvocation.BoundParameters.ContainsKey(paramName)
420+
? ((SwitchParameter)MyInvocation.BoundParameters[paramName]).ToBool()
421+
: paramValue;
422+
}
423+
391424
#endregion // Private Methods
392425
}
393426
}

Engine/Settings.cs

+32-5
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,23 @@ internal enum SettingsMode { None = 0, Auto, File, Hashtable, Preset };
2828
/// </summary>
2929
public class Settings
3030
{
31+
private bool recurseCustomRulePath = false;
32+
private bool includeDefaultRules = false;
3133
private string filePath;
3234
private List<string> includeRules;
3335
private List<string> excludeRules;
3436
private List<string> severities;
37+
private List<string> customRulePath;
3538
private Dictionary<string, Dictionary<string, object>> ruleArguments;
3639

37-
public string FilePath { get { return filePath; } }
38-
public IEnumerable<string> IncludeRules { get { return includeRules; } }
39-
public IEnumerable<string> ExcludeRules { get { return excludeRules; } }
40-
public IEnumerable<string> Severities { get { return severities; } }
41-
public Dictionary<string, Dictionary<string, object>> RuleArguments { get { return ruleArguments; } }
40+
public bool RecurseCustomRulePath => recurseCustomRulePath;
41+
public bool IncludeDefaultRules => includeDefaultRules;
42+
public string FilePath => filePath;
43+
public IEnumerable<string> IncludeRules => includeRules;
44+
public IEnumerable<string> ExcludeRules => excludeRules;
45+
public IEnumerable<string> Severities => severities;
46+
public IEnumerable<string> CustomRulePath => customRulePath;
47+
public Dictionary<string, Dictionary<string, object>> RuleArguments => ruleArguments;
4248

4349
/// <summary>
4450
/// Create a settings object from the input object.
@@ -400,6 +406,27 @@ private void parseSettingsHashtable(Hashtable settingsHashtable)
400406
excludeRules = GetData(val, key);
401407
break;
402408

409+
case "customrulepath":
410+
customRulePath = GetData(val, key);
411+
break;
412+
413+
case "includedefaultrules":
414+
case "recursecustomrulepath":
415+
if (!(val is bool))
416+
{
417+
throw new InvalidDataException(string.Format(
418+
CultureInfo.CurrentCulture,
419+
Strings.SettingsValueTypeMustBeBool,
420+
settingKey));
421+
}
422+
423+
var booleanVal = (bool)val;
424+
var field = this.GetType().GetField(
425+
key,
426+
BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic);
427+
field.SetValue(this, booleanVal);
428+
break;
429+
403430
case "rules":
404431
try
405432
{

Engine/Strings.resx

+3
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@
273273
<data name="SettingsNotParsable" xml:space="preserve">
274274
<value>Cannot parse settings. Will abort the invocation.</value>
275275
</data>
276+
<data name="SettingsValueTypeMustBeBool" xml:space="preserve">
277+
<value>{0} property must be of type bool.</value>
278+
</data>
276279
<data name="ModuleDepHandlerTempLocation" xml:space="preserve">
277280
<value>Temporary module location: {0}.</value>
278281
</data>

New-StronglyTypedCsFileForResx.ps1

+4-4
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ internal class {0} {{
113113
$entry -f $name,$val
114114
}
115115
} | Out-String
116-
116+
117117
$bodyCode = $body -f $shortClassName,$ModuleName,$entries,$ClassName
118118

119119
if ($NamespaceName)
@@ -126,10 +126,10 @@ internal class {0} {{
126126
return $resultCode -replace "`r`n?|`n","`r`n"
127127
}
128128

129-
$projectRoot = Split-Path $MyInvocation.InvocationName
129+
$projectRoot = $PWD
130130
if (-not (Test-Path "$projectRoot/global.json"))
131131
{
132-
throw "Not in solution root"
132+
throw "Not in solution root: $projectRoot"
133133
}
134134
$inputFilePath = Join-Path $projectRoot "$project/Strings.resx"
135135
$outputFilePath = Join-Path $projectRoot "$project/Strings.cs"
@@ -141,4 +141,4 @@ if ($project -eq "Rules")
141141
$className += ".Strings"
142142
$xml = [xml](Get-Content -raw $inputFilePath)
143143
$genSource = Get-StronglyTypeCsFileForResx -xml $xml -ModuleName Foo -ClassName $className
144-
Set-Content -Encoding Ascii -Path $outputFilePath -Value $genSource
144+
Set-Content -Encoding Ascii -Path $outputFilePath -Value $genSource

Tests/Engine/InvokeScriptAnalyzer.tests.ps1

+66-1
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,74 @@ Describe "Test CustomizedRulePath" {
380380
$customizedRulePath = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -CustomRulePath ("$directory\CommunityAnalyzerRules", "$directory\samplerule", "$directory\samplerule\samplerule2")
381381
$customizedRulePath.Count | Should Be 3
382382
}
383-
384383
}
385384

385+
if (!$testingLibraryUsage)
386+
{
387+
Context "When used from settings file" {
388+
It "Should use the CustomRulePath parameter" {
389+
$settings = @{
390+
CustomRulePath = "$directory\CommunityAnalyzerRules"
391+
IncludeDefaultRules = $false
392+
RecurseCustomRulePath = $false
393+
}
394+
395+
$v = Invoke-ScriptAnalyzer -Path $directory\TestScript.ps1 -Settings $settings
396+
$v.Count | Should Be 1
397+
}
398+
399+
It "Should use the IncludeDefaultRulePath parameter" {
400+
$settings = @{
401+
CustomRulePath = "$directory\CommunityAnalyzerRules"
402+
IncludeDefaultRules = $true
403+
RecurseCustomRulePath = $false
404+
}
405+
406+
$v = Invoke-ScriptAnalyzer -Path $directory\TestScript.ps1 -Settings $settings
407+
$v.Count | Should Be 2
408+
}
409+
410+
It "Should use the RecurseCustomRulePath parameter" {
411+
$settings = @{
412+
CustomRulePath = "$directory\samplerule"
413+
IncludeDefaultRules = $false
414+
RecurseCustomRulePath = $true
415+
}
416+
417+
$v = Invoke-ScriptAnalyzer -Path $directory\TestScript.ps1 -Settings $settings
418+
$v.Count | Should Be 3
419+
}
420+
}
421+
422+
Context "When used from settings file and command line simulataneusly" {
423+
BeforeAll {
424+
$settings = @{
425+
CustomRulePath = "$directory\samplerule"
426+
IncludeDefaultRules = $false
427+
RecurseCustomRulePath = $false
428+
}
429+
$isaParams = @{
430+
Path = "$directory\TestScript.ps1"
431+
Settings = $settings
432+
}
433+
}
434+
435+
It "Should combine CustomRulePaths" {
436+
$v = Invoke-ScriptAnalyzer @isaParams -CustomRulePath "$directory\CommunityAnalyzerRules"
437+
$v.Count | Should Be 2
438+
}
439+
440+
It "Should override the settings IncludeDefaultRules parameter" {
441+
$v = Invoke-ScriptAnalyzer @isaParams -IncludeDefaultRules
442+
$v.Count | Should Be 2
443+
}
444+
445+
It "Should override the settings RecurseCustomRulePath parameter" {
446+
$v = Invoke-ScriptAnalyzer @isaParams -RecurseCustomRulePath
447+
$v.Count | Should Be 3
448+
}
449+
}
450+
}
386451

387452
Context "When used incorrectly" {
388453
It "file cannot be found" {

Tests/Engine/Settings.tests.ps1

+65
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,69 @@ Describe "Settings Class" {
114114
$settings.RuleArguments["PSProvideCommentHelp"]["Placement"] | Should Be 'end'
115115
}
116116
}
117+
118+
Context "When CustomRulePath parameter is provided" {
119+
It "Should return an array of 1 item when only 1 path is given in a hashtable" {
120+
$rulePath = "C:\rules\module1"
121+
$settingsHashtable = @{
122+
CustomRulePath = $rulePath
123+
}
124+
125+
$settings = New-Object -TypeName $settingsTypeName -ArgumentList $settingsHashtable
126+
$settings.CustomRulePath.Count | Should Be 1
127+
$settings.CustomRulePath[0] | Should be $rulePath
128+
}
129+
130+
It "Should return an array of n items when n items are given in a hashtable" {
131+
$rulePaths = @("C:\rules\module1", "C:\rules\module2")
132+
$settingsHashtable = @{
133+
CustomRulePath = $rulePaths
134+
}
135+
136+
$settings = New-Object -TypeName $settingsTypeName -ArgumentList $settingsHashtable
137+
$settings.CustomRulePath.Count | Should Be $rulePaths.Count
138+
0..($rulePaths.Count - 1) | ForEach-Object { $settings.CustomRulePath[$_] | Should be $rulePaths[$_] }
139+
140+
}
141+
142+
It "Should detect the parameter in a settings file" {
143+
$settings = New-Object -TypeName $settingsTypeName `
144+
-ArgumentList ([System.IO.Path]::Combine($project1Root, "CustomRulePathSettings.psd1"))
145+
$settings.CustomRulePath.Count | Should Be 2
146+
}
147+
}
148+
149+
@("IncludeDefaultRules", "RecurseCustomRulePath") | ForEach-Object {
150+
$paramName = $_
151+
Context "When $paramName parameter is provided" {
152+
It "Should correctly set the value if a boolean is given - true" {
153+
$settingsHashtable = @{}
154+
$settingsHashtable.Add($paramName, $true)
155+
156+
$settings = New-Object -TypeName $settingsTypeName -ArgumentList $settingsHashtable
157+
$settings."$paramName" | Should Be $true
158+
}
159+
160+
It "Should correctly set the value if a boolean is given - false" {
161+
$settingsHashtable = @{}
162+
$settingsHashtable.Add($paramName, $false)
163+
164+
$settings = New-Object -TypeName $settingsTypeName -ArgumentList $settingsHashtable
165+
$settings."$paramName" | Should Be $false
166+
}
167+
168+
It "Should throw if a non-boolean value is given" {
169+
$settingsHashtable = @{}
170+
$settingsHashtable.Add($paramName, "some random string")
171+
172+
{ New-Object -TypeName $settingsTypeName -ArgumentList $settingsHashtable } | Should Throw
173+
}
174+
175+
It "Should detect the parameter in a settings file" {
176+
$settings = New-Object -TypeName $settingsTypeName `
177+
-ArgumentList ([System.IO.Path]::Combine($project1Root, "CustomRulePathSettings.psd1"))
178+
$settings."$paramName" | Should Be $true
179+
}
180+
}
181+
}
117182
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@{
2+
"CustomRulePath" = @("C:\rules\module1", "C:\rules\module2")
3+
"IncludeDefaultRules" = $true
4+
"RecurseCustomRulePath" = $true
5+
}

0 commit comments

Comments
 (0)