Skip to content

Commit 1987389

Browse files
author
Kapil Borle
authored
Merge pull request #717 from PowerShell/kapilmb/settings
Improve settings implementation
2 parents 260a573 + 5c12137 commit 1987389

File tree

12 files changed

+903
-108
lines changed

12 files changed

+903
-108
lines changed

CHANGELOG.MD

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1-
## [1.10.0](https://github.com/PowerShell/PSScriptAnalyzer/tree/1.10.0) - 2017-01-19
1+
## unreleased
2+
3+
### Added
4+
- Built-in settings presets to specify settings from command line. Currently, PSSA ships with `PSGallery`, `CodeFormatting`, `DSC`, and other setting presets. All of them can be found in the `Settings/` directory in the module. To use them just pass them as an argument to the `Settings` parameters. For example, if you want to run rules that *powershellgallery* runs, then use the following command.
5+
```powershell
6+
PS> Invoke-ScriptAnalyzer -Path /path/to/your/module -Settings PSGallery
7+
```
8+
- Argument completion for built-in settings presets.
9+
- Argument completion for `IncludeRule` and `ExcludeRule` parameters.
10+
11+
### Fixed
12+
13+
### Changed
14+
- Settings implementation to decouple it from engine.
15+
16+
## [1.10.0](https://github.com/PowerShell/PSScriptAnalyzer/tree/1.10.0) - 2017-01-19
217
### Added
318
- Three rules to enable code formatting feature in vscode (#690)
419
- [PSPlaceOpenBrace](https://github.com/PowerShell/PSScriptAnalyzer/blob/03a6e2b4ee24894bf574a8a8ce911d03680da607/RuleDocumentation/PlaceOpenBrace.md)

Engine/Commands/InvokeScriptAnalyzerCommand.cs

Lines changed: 65 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,9 @@ public SwitchParameter SaveDscDependency
206206
}
207207
private bool saveDscDependency;
208208
#endif // !PSV3
209-
#endregion Parameters
209+
#endregion Parameters
210210

211-
#region Overrides
211+
#region Overrides
212212

213213
/// <summary>
214214
/// Imports all known rules and loggers.
@@ -227,67 +227,74 @@ protected override void BeginProcessing()
227227
Helper.Instance.SetPSVersionTable(psVersionTable);
228228
}
229229

230-
string[] rulePaths = Helper.ProcessCustomRulePaths(customRulePath,
231-
this.SessionState, recurseCustomRulePath);
230+
string[] rulePaths = Helper.ProcessCustomRulePaths(
231+
customRulePath,
232+
this.SessionState,
233+
recurseCustomRulePath);
234+
232235
if (IsFileParameterSet())
233236
{
234237
ProcessPath();
235238
}
236239

237-
var settingFileHasErrors = false;
238-
if (settings == null
239-
&& processedPaths != null
240-
&& processedPaths.Count == 1)
240+
object settingsFound;
241+
var settingsMode = PowerShell.ScriptAnalyzer.Settings.FindSettingsMode(
242+
this.settings,
243+
processedPaths == null || processedPaths.Count == 0 ? null : processedPaths[0],
244+
out settingsFound);
245+
246+
switch (settingsMode)
241247
{
242-
// add a directory separator character because if there is no trailing separator character, it will return the parent
243-
var directory = processedPaths[0].TrimEnd(System.IO.Path.DirectorySeparatorChar);
244-
if (File.Exists(directory))
245-
{
246-
// if given path is a file, get its directory
247-
directory = System.IO.Path.GetDirectoryName(directory);
248-
}
248+
case SettingsMode.Auto:
249+
this.WriteVerbose(
250+
String.Format(
251+
CultureInfo.CurrentCulture,
252+
Strings.SettingsNotProvided,
253+
path));
254+
this.WriteVerbose(
255+
String.Format(
256+
CultureInfo.CurrentCulture,
257+
Strings.SettingsAutoDiscovered,
258+
(string)settingsFound));
259+
break;
249260

250-
this.WriteVerbose(
251-
String.Format(
252-
"Settings not provided. Will look for settings file in the given path {0}.",
253-
path));
254-
var settingsFileAutoDiscovered = false;
255-
if (Directory.Exists(directory))
256-
{
257-
// if settings are not provided explicitly, look for it in the given path
258-
// check if pssasettings.psd1 exists
259-
var settingsFilename = "PSScriptAnalyzerSettings.psd1";
260-
var settingsFilepath = System.IO.Path.Combine(directory, settingsFilename);
261-
if (File.Exists(settingsFilepath))
262-
{
263-
settingsFileAutoDiscovered = true;
264-
this.WriteVerbose(
265-
String.Format(
266-
"Found {0} in {1}. Will use it to provide settings for this invocation.",
267-
settingsFilename,
268-
directory));
269-
settingFileHasErrors = !ScriptAnalyzer.Instance.ParseProfile(settingsFilepath, this.SessionState.Path, this);
270-
}
271-
}
261+
case SettingsMode.Preset:
262+
case SettingsMode.File:
263+
this.WriteVerbose(
264+
String.Format(
265+
CultureInfo.CurrentCulture,
266+
Strings.SettingsUsingFile,
267+
(string)settingsFound));
268+
break;
272269

273-
if (!settingsFileAutoDiscovered)
274-
{
270+
case SettingsMode.Hashtable:
275271
this.WriteVerbose(
276272
String.Format(
277-
"Cannot find a settings file in the given path {0}.",
278-
path));
279-
}
280-
}
281-
else
282-
{
283-
settingFileHasErrors = !ScriptAnalyzer.Instance.ParseProfile(this.settings, this.SessionState.Path, this);
273+
CultureInfo.CurrentCulture,
274+
Strings.SettingsUsingHashtable));
275+
break;
276+
277+
default: // case SettingsMode.None
278+
this.WriteVerbose(
279+
String.Format(
280+
CultureInfo.CurrentCulture,
281+
Strings.SettingsCannotFindFile));
282+
break;
284283
}
285284

286-
if (settingFileHasErrors)
285+
if (settingsMode != SettingsMode.None)
287286
{
288-
this.WriteWarning("Cannot parse settings. Will abort the invocation.");
289-
stopProcessing = true;
290-
return;
287+
try
288+
{
289+
var settingsObj = new Settings(settingsFound);
290+
ScriptAnalyzer.Instance.UpdateSettings(settingsObj);
291+
}
292+
catch
293+
{
294+
this.WriteWarning(String.Format(CultureInfo.CurrentCulture, Strings.SettingsNotParsable));
295+
stopProcessing = true;
296+
return;
297+
}
291298
}
292299

293300
ScriptAnalyzer.Instance.Initialize(
@@ -323,7 +330,8 @@ protected override void ProcessRecord()
323330
ScriptAnalyzer.Instance.ModuleHandler = moduleHandler;
324331
this.WriteVerbose(
325332
String.Format(
326-
"Temporary module location: {0}",
333+
CultureInfo.CurrentCulture,
334+
Strings.ModuleDepHandlerTempLocation,
327335
moduleHandler.TempModulePath));
328336
ProcessInput();
329337
}
@@ -346,9 +354,10 @@ protected override void StopProcessing()
346354
base.StopProcessing();
347355
}
348356

349-
#endregion
357+
#endregion
358+
359+
#region Private Methods
350360

351-
#region Methods
352361
private void ProcessInput()
353362
{
354363
IEnumerable<DiagnosticRecord> diagnosticsList = Enumerable.Empty<DiagnosticRecord>();
@@ -392,6 +401,7 @@ private bool IsFileParameterSet()
392401
{
393402
return String.Equals(this.ParameterSetName, "File", StringComparison.OrdinalIgnoreCase);
394403
}
395-
#endregion
404+
405+
#endregion // Private Methods
396406
}
397-
}
407+
}

Engine/Generic/CorrectionExtent.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
using System;
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
214
using System.Collections.Generic;
315
using System.IO;
416
using System.Linq;

Engine/Helper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
2222
using System.Management.Automation.Runspaces;
2323
using System.Collections;
24+
using System.Reflection;
2425

2526
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
2627
{

Engine/PSScriptAnalyzer.psm1

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ else
1818
{
1919
if ($PSVersionTable.PSVersion -lt [Version]'5.0') {
2020
$binaryModuleRoot = Join-Path -Path $PSModuleRoot -ChildPath 'PSv3'
21-
}
21+
}
2222
}
2323

2424
$binaryModulePath = Join-Path -Path $binaryModuleRoot -ChildPath 'Microsoft.Windows.PowerShell.ScriptAnalyzer.dll'
@@ -27,4 +27,27 @@ $binaryModule = Import-Module -Name $binaryModulePath -PassThru
2727
# When the module is unloaded, remove the nested binary module that was loaded with it
2828
$PSModule.OnRemove = {
2929
Remove-Module -ModuleInfo $binaryModule
30+
}
31+
32+
if (Get-Command Register-ArgumentCompleter -ErrorAction Ignore)
33+
{
34+
Register-ArgumentCompleter -CommandName 'Invoke-ScriptAnalyzer' -ParameterName 'Settings' -ScriptBlock {
35+
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParmeter)
36+
37+
[Microsoft.Windows.PowerShell.ScriptAnalyzer.Settings]::GetSettingPresets() | `
38+
Where-Object {$_ -like "$wordToComplete*"} | `
39+
ForEach-Object { New-Object System.Management.Automation.CompletionResult $_ }
40+
}
41+
42+
Function RuleNameCompleter
43+
{
44+
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParmeter)
45+
46+
Get-ScriptAnalyzerRule *$wordToComplete* | `
47+
ForEach-Object { New-Object System.Management.Automation.CompletionResult $_.RuleName }
48+
}
49+
50+
Register-ArgumentCompleter -CommandName 'Invoke-ScriptAnalyzer' -ParameterName 'IncludeRule' -ScriptBlock $Function:RuleNameCompleter
51+
Register-ArgumentCompleter -CommandName 'Invoke-ScriptAnalyzer' -ParameterName 'ExcludeRule' -ScriptBlock $Function:RuleNameCompleter
52+
Register-ArgumentCompleter -CommandName 'Get-ScriptAnalyzerRule' -ParameterName 'Name' -ScriptBlock $Function:RuleNameCompleter
3053
}

Engine/ScriptAnalyzer.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,30 @@ public void CleanUp()
198198
suppressedOnly = false;
199199
}
200200

201+
/// <summary>
202+
/// Update includerules, excluderules, severity and rule arguments.
203+
/// </summary>
204+
/// <param name="settings">An object of type Settings</param>
205+
public void UpdateSettings(Settings settings)
206+
{
207+
if (settings == null)
208+
{
209+
throw new ArgumentNullException(nameof(settings));
210+
}
211+
212+
this.severity = (!settings.Severities.Any()) ? null : settings.Severities.ToArray();
213+
this.includeRule = (!settings.IncludeRules.Any()) ? null : settings.IncludeRules.ToArray();
214+
this.excludeRule = (!settings.ExcludeRules.Any()) ? null : settings.ExcludeRules.ToArray();
215+
if (settings.RuleArguments != null)
216+
{
217+
Helper.Instance.SetRuleArguments(
218+
settings.RuleArguments.ToDictionary(
219+
kvp => kvp.Key,
220+
kvp => kvp.Value as object,
221+
StringComparer.OrdinalIgnoreCase));
222+
}
223+
}
224+
201225
internal bool ParseProfile(object profileObject, PathIntrinsics path, IOutputWriter writer)
202226
{
203227
// profile was not given

Engine/ScriptAnalyzerEngine.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<Compile Include="Generic\ConfigurableRule.cs" />
7575
<Compile Include="Generic\ModuleDependencyHandler.cs" />
7676
<Compile Include="Generic\CorrectionExtent.cs" />
77+
<Compile Include="Settings.cs" />
7778
<Compile Include="Generic\SuppressedRecord.cs" />
7879
<Compile Include="Generic\DiagnosticRecord.cs" />
7980
<Compile Include="Generic\ExternalRule.cs" />

0 commit comments

Comments
 (0)