From 5f1a4d35a196ef7bb7c05cd080a93049052c643c Mon Sep 17 00:00:00 2001 From: TheCakeIsNaOH Date: Tue, 16 Aug 2022 12:08:47 -0500 Subject: [PATCH] (#32) Add PSScriptAnalyzer This adds a task to run PSScriptAnalyzer. It utilizes the Cake.Powershell module to run a script that will get ps1/psm1 files that are not excluded and run them through the specified settings file. Either a default formatting only run can be done, or custom run(s) can be specified. A custom base analysis path, setting file and set of folder exclusions can be specified for each run. For example, a custom run can be created to check that the Chocolatey powershell helpers use only the PSv2 cmdlets. The default run is for formatting, it checks files against the build in formatting related rules. This run can be expanded in the future, once enough projects are up to a baseline of good scripting practices. --- Chocolatey.Cake.Recipe/Content/analyzing.cake | 3 +- .../Content/formatting-settings.psd1 | 70 ++++++++++ .../Content/install-module.ps1 | 34 +++++ .../Content/parameters.cake | 15 ++- Chocolatey.Cake.Recipe/Content/paths.cake | 5 + .../Content/psscriptanalyzer.cake | 124 ++++++++++++++++++ .../Content/run-psscriptanalyzer.ps1 | 120 +++++++++++++++++ Chocolatey.Cake.Recipe/Content/tasks.cake | 1 + Chocolatey.Cake.Recipe/Content/tools.cake | 12 ++ .../Content/toolsettings.cake | 3 + 10 files changed, 385 insertions(+), 2 deletions(-) create mode 100644 Chocolatey.Cake.Recipe/Content/formatting-settings.psd1 create mode 100644 Chocolatey.Cake.Recipe/Content/install-module.ps1 create mode 100644 Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake create mode 100644 Chocolatey.Cake.Recipe/Content/run-psscriptanalyzer.ps1 diff --git a/Chocolatey.Cake.Recipe/Content/analyzing.cake b/Chocolatey.Cake.Recipe/Content/analyzing.cake index fcdac68..675ffdb 100644 --- a/Chocolatey.Cake.Recipe/Content/analyzing.cake +++ b/Chocolatey.Cake.Recipe/Content/analyzing.cake @@ -114,7 +114,7 @@ BuildParameters.Tasks.DotNetFormatTask = Task("Run-DotNetFormat") { dotNetFormatTool = Context.Tools.Resolve("dotnet-format"); } - + StartProcess(dotNetFormatTool, new ProcessSettings{ Arguments = string.Format("{0} --report {1} --no-restore", MakeAbsolute(BuildParameters.SolutionFilePath), MakeAbsolute(BuildParameters.Paths.Files.DotNetFormatOutputFilePath)) }); }) ); @@ -123,4 +123,5 @@ BuildParameters.Tasks.AnalyzeTask = Task("Analyze") .IsDependentOn("InspectCode") .IsDependentOn("Run-DotNetFormatCheck") .IsDependentOn("CreateIssuesReport") + .IsDependentOn("Run-PSScriptAnalyzer") .WithCriteria(() => BuildParameters.ShouldRunAnalyze, "Skipping because running analysis tasks is not enabled"); diff --git a/Chocolatey.Cake.Recipe/Content/formatting-settings.psd1 b/Chocolatey.Cake.Recipe/Content/formatting-settings.psd1 new file mode 100644 index 0000000..0e23cf7 --- /dev/null +++ b/Chocolatey.Cake.Recipe/Content/formatting-settings.psd1 @@ -0,0 +1,70 @@ +@{ + IncludeRules = @( + 'PSUseBOMForUnicodeEncodedFile', + 'PSMisleadingBacktick', + 'PSAvoidUsingCmdletAliases', + 'PSAvoidTrailingWhitespace', + 'PSAvoidSemicolonsAsLineTerminators', + 'PSUseCorrectCasing', + 'PSPlaceOpenBrace', + 'PSPlaceCloseBrace', + 'PSAlignAssignmentStatement', + 'PSUseConsistentWhitespace', + 'PSUseConsistentIndentation' + ) + + Rules = @{ + + <# + PSAvoidUsingCmdletAliases = @{ + 'allowlist' = @('') + }#> + + PSAvoidSemicolonsAsLineTerminators = @{ + Enable = $true + } + + PSUseCorrectCasing = @{ + Enable = $true + } + + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $false + } + + PSPlaceCloseBrace = @{ + Enable = $true + NewLineAfter = $true + IgnoreOneLineBlock = $false + NoEmptyLineBefore = $true + } + + PSAlignAssignmentStatement = @{ + Enable = $true + CheckHashtable = $true + } + + PSUseConsistentIndentation = @{ + Enable = $true + Kind = 'space' + PipelineIndentation = 'IncreaseIndentationForFirstPipeline' + IndentationSize = 4 + } + + PSUseConsistentWhitespace = @{ + Enable = $true + CheckInnerBrace = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $true + CheckPipe = $true + CheckPipeForRedundantWhitespace = $false + CheckSeparator = $true + CheckParameter = $false + IgnoreAssignmentOperatorInsideHashTable = $true + } + } +} \ No newline at end of file diff --git a/Chocolatey.Cake.Recipe/Content/install-module.ps1 b/Chocolatey.Cake.Recipe/Content/install-module.ps1 new file mode 100644 index 0000000..2a79778 --- /dev/null +++ b/Chocolatey.Cake.Recipe/Content/install-module.ps1 @@ -0,0 +1,34 @@ +# Copyright © 2023 Chocolatey Software, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[cmdletBinding()] +Param( + [Parameter()] + [String] + $ModuleName, + + [Parameter()] + [String] + $RequiredVersion +) + +$FullyQualifiedName = @{ModuleName="$ModuleName";RequiredVersion="$RequiredVersion"} +if (Get-Module -ListAvailable -FullyQualifiedName $FullyQualifiedName) { + Write-Host "The $ModuleName PowerShell Module with version $RequiredVersion is already installed." +} +else { + Write-Host "Install Module $ModuleName with version $RequiredVersion..." + Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force +} \ No newline at end of file diff --git a/Chocolatey.Cake.Recipe/Content/parameters.cake b/Chocolatey.Cake.Recipe/Content/parameters.cake index a6b891c..3399320 100644 --- a/Chocolatey.Cake.Recipe/Content/parameters.cake +++ b/Chocolatey.Cake.Recipe/Content/parameters.cake @@ -104,6 +104,7 @@ public static class BuildParameters public static Func GetFilesToObfuscate { get; private set; } public static Func GetFilesToSign { get; private set; } public static Func> GetILMergeConfigs { get; private set; } + public static Func> GetPSScriptAnalyzerSettings { get; private set; } public static Func GetMsisToSign { get; private set; } public static Func GetProjectsToPack { get; private set; } public static Func GetScriptsToSign { get; private set; } @@ -181,6 +182,7 @@ public static class BuildParameters public static bool ShouldRunTests { get; private set ;} public static bool ShouldRunTransifex { get; set; } public static bool ShouldRunxUnit { get; private set; } + public static bool ShouldRunPSScriptAnalyzer { get; private set; } public static bool ShouldStrongNameOutputAssemblies { get; private set; } public static bool ShouldStrongNameSignDependentAssemblies { get; private set; } public static SlackCredentials Slack { get; private set; } @@ -313,6 +315,7 @@ public static class BuildParameters context.Information("ShouldRunTests: {0}", BuildParameters.ShouldRunTests); context.Information("ShouldRunTransifex: {0}", BuildParameters.ShouldRunTransifex); context.Information("ShouldRunxUnit: {0}", BuildParameters.ShouldRunxUnit); + context.Information("ShouldRunPSScriptAnalyzer: {0}", BuildParameters.ShouldRunPSScriptAnalyzer); context.Information("ShouldStrongNameOutputAssemblies: {0}", BuildParameters.ShouldStrongNameOutputAssemblies); context.Information("ShouldStrongNameSignDependentAssemblies: {0}", BuildParameters.ShouldStrongNameSignDependentAssemblies); context.Information("SolutionDirectoryPath: {0}", context.MakeAbsolute((DirectoryPath)SolutionDirectoryPath)); @@ -353,6 +356,7 @@ public static class BuildParameters Func getFilesToObfuscate = null, Func getFilesToSign = null, Func> getILMergeConfigs = null, + Func> getPSScriptAnalyzerSettings = null, Func getMsisToSign = null, Func getProjectsToPack = null, Func getScriptsToSign = null, @@ -421,6 +425,7 @@ public static class BuildParameters bool shouldRunTests = true, bool? shouldRunTransifex = null, bool shouldRunxUnit = true, + bool shouldRunPSScriptAnalyzer = true, bool shouldStrongNameOutputAssemblies = true, bool shouldStrongNameSignDependentAssemblies = true, Func slackMessageArguments = null, @@ -485,6 +490,7 @@ public static class BuildParameters GetFilesToObfuscate = getFilesToObfuscate; GetFilesToSign = getFilesToSign; GetILMergeConfigs = getILMergeConfigs; + GetPSScriptAnalyzerSettings = getPSScriptAnalyzerSettings; GetMsisToSign = getMsisToSign; GetProjectsToPack = getProjectsToPack; GetScriptsToSign = getScriptsToSign; @@ -743,6 +749,13 @@ public static class BuildParameters ShouldRunOpenCover = context.Argument("shouldRunOpenCover"); } + ShouldRunPSScriptAnalyzer = shouldRunPSScriptAnalyzer; + + if (context.HasArgument("shouldRunPSScriptAnalyzer")) + { + ShouldRunPSScriptAnalyzer = context.Argument("shouldRunPSScriptAnalyzer"); + } + ShouldRunReportGenerator = shouldRunReportGenerator; if (context.HasArgument("shouldRunReportGenerator")) @@ -779,7 +792,7 @@ public static class BuildParameters } ShouldRunxUnit = shouldRunxUnit; - + if (context.HasArgument("shouldRunxUnit")) { ShouldRunxUnit = context.Argument("shouldRunxUnit"); diff --git a/Chocolatey.Cake.Recipe/Content/paths.cake b/Chocolatey.Cake.Recipe/Content/paths.cake index 33940ab..d718a1b 100644 --- a/Chocolatey.Cake.Recipe/Content/paths.cake +++ b/Chocolatey.Cake.Recipe/Content/paths.cake @@ -38,6 +38,7 @@ public class BuildPaths var testResultsDirectory = buildDirectoryPath + "/TestResults"; var inspectCodeResultsDirectory = testResultsDirectory + "/InspectCode"; + var scriptAnalyzerResultsDirectory = testResultsDirectory + "/PSScriptAnalyzer"; var NUnitTestResultsDirectory = testResultsDirectory + "/NUnit"; var xUnitTestResultsDirectory = testResultsDirectory + "/xUnit"; @@ -75,6 +76,7 @@ public class BuildPaths nugetNuspecDirectory, testResultsDirectory, inspectCodeResultsDirectory, + scriptAnalyzerResultsDirectory, NUnitTestResultsDirectory, xUnitTestResultsDirectory, testCoverageDirectory, @@ -165,6 +167,7 @@ public class BuildDirectories public DirectoryPath NuGetNuspecDirectory { get; private set; } public DirectoryPath TestResults { get; private set; } public DirectoryPath InspectCodeTestResults { get; private set; } + public DirectoryPath PSScriptAnalyzerResults { get; private set; } public DirectoryPath NUnitTestResults { get; private set; } public DirectoryPath xUnitTestResults { get; private set; } public DirectoryPath TestCoverage { get; private set; } @@ -188,6 +191,7 @@ public class BuildDirectories DirectoryPath nugetNuspecDirectory, DirectoryPath testResults, DirectoryPath inspectCodeTestResults, + DirectoryPath scriptAnalyzerResults, DirectoryPath nunitTestResults, DirectoryPath xunitTestResults, DirectoryPath testCoverage, @@ -210,6 +214,7 @@ public class BuildDirectories NuGetNuspecDirectory = nugetNuspecDirectory; TestResults = testResults; InspectCodeTestResults = inspectCodeTestResults; + PSScriptAnalyzerResults = scriptAnalyzerResults; NUnitTestResults = nunitTestResults; xUnitTestResults = xunitTestResults; TestCoverage = testCoverage; diff --git a/Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake b/Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake new file mode 100644 index 0000000..524a03a --- /dev/null +++ b/Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake @@ -0,0 +1,124 @@ +// Copyright © 2023 Chocolatey Software, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +BuildParameters.Tasks.PSScriptAnalyzerTask = Task("Run-PSScriptAnalyzer") + .WithCriteria(() => BuildParameters.BuildAgentOperatingSystem == PlatformFamily.Windows, "Skipping due to not running on Windows") + .WithCriteria(() => BuildParameters.ShouldRunPSScriptAnalyzer, "Skipping because PSScriptAnalyzer is not enabled") + .WithCriteria(() => BuildParameters.ShouldRunAnalyze, "Skipping because running analysis tasks is not enabled") + .Does(() => RequirePSModule("PSScriptAnalyzer", "1.21.0", () => + RequirePSModule("ConvertToSARIF", "1.0.0", () => { + var powerShellAnalysisScript = GetFiles("./tools/Chocolatey.Cake.Recipe*/Content/run-psscriptanalyzer.ps1").FirstOrDefault(); + + if (powerShellAnalysisScript == null) + { + Warning("Unable to find PowerShell Analysis script, so unable to run analysis."); + return; + } + + var outputFolder = MakeAbsolute(BuildParameters.Paths.Directories.PSScriptAnalyzerResults).FullPath; + EnsureDirectoryExists(outputFolder); + + if (BuildParameters.GetPSScriptAnalyzerSettings != null) + { + foreach (var PSScriptAnalyzerSetting in BuildParameters.GetPSScriptAnalyzerSettings()) + { + Information(string.Format("Running PSScriptAnalyzer {0}", PSScriptAnalyzerSetting.Name)); + + StartPowershellFile(MakeAbsolute(powerShellAnalysisScript), new PowershellSettings() + .WithModule("PSScriptAnalyzer") + .WithModule("ConvertToSARIF") + .WithModule("Microsoft.PowerShell.Management") + .WithModule("Microsoft.PowerShell.Utility") + .SetFormatOutput(false) + .SetLogOutput(true) + .OutputToAppConsole(true) + .WithArguments(args => { + args.AppendQuoted("AnalyzePath", PSScriptAnalyzerSetting.AnalysisPath.ToString()) + .AppendQuoted("SettingsPath", PSScriptAnalyzerSetting.SettingsPath.ToString()) + .AppendQuoted("OutputPath", outputFolder) + .AppendArray("ExcludePaths", PSScriptAnalyzerSetting.ExcludePaths); + })); + } + } + else + { + Information("There are no PSScriptAnalyzer Settings defined for this build, running with default format checking settings."); + + var settingsFile = GetFiles("./tools/Chocolatey.Cake.Recipe*/Content/formatting-settings.psd1").FirstOrDefault(); + + if (settingsFile == null) + { + Warning("Unable to find PowerShell Analysis settings, so unable to run analysis."); + return; + } + + var pwshSettings = new PowershellSettings() + .WithModule("PSScriptAnalyzer") + .WithModule("ConvertToSARIF") + .WithModule("Microsoft.PowerShell.Management") + .WithModule("Microsoft.PowerShell.Utility") + .SetFormatOutput(false) + .SetLogOutput(true) + .OutputToAppConsole(true) + .WithArguments(args => { + args.AppendQuoted("AnalyzePath", BuildParameters.RootDirectoryPath.ToString()) + .AppendQuoted("SettingsPath", settingsFile.ToString()) + .AppendQuoted("OutputPath", outputFolder) + .AppendArray("ExcludePaths", ToolSettings.PSScriptAnalyzerExcludePaths); + }); + + pwshSettings.ExceptionOnScriptError = false; + + var resultCollection = StartPowershellFile(MakeAbsolute(powerShellAnalysisScript), pwshSettings); + var returnCode = int.Parse(resultCollection[0].BaseObject.ToString()); + + Information("Result: {0}", returnCode); + + // NOTE: Ideally, we would have this throw an exception, however, during testing, invoking PSScriptAnalyzer + // sometimes caused random "The term 'Get-Command' is not recognized as the name of a cmdlet, function, script + // file, or operable program." errors, which meant that we can't rely on this returnCode. For now, we + // only want PSScriptAnalyzer to warn on violations, so we don't need to worry about this just now. + //if (returnCode != 0) + //{ + // throw new ApplicationException("Script failed to execute"); + //} + } + }) + ) +); + +public class PSScriptAnalyzerSettings +{ + public FilePath AnalysisPath { get; set; } + public FilePath SettingsPath { get; set; } + public List ExcludePaths { get; set; } + public string Name { get; set; } + + public PSScriptAnalyzerSettings() + { + Name = "Unnamed"; + } + + public PSScriptAnalyzerSettings(FilePath analysisPath, + FilePath settingsPath, + List excludePaths = null, + string name = "Unnamed") + { + AnalysisPath = analysisPath; + SettingsPath = settingsPath; + ExcludePaths = excludePaths; + Name = name; + } +} \ No newline at end of file diff --git a/Chocolatey.Cake.Recipe/Content/run-psscriptanalyzer.ps1 b/Chocolatey.Cake.Recipe/Content/run-psscriptanalyzer.ps1 new file mode 100644 index 0000000..fb32857 --- /dev/null +++ b/Chocolatey.Cake.Recipe/Content/run-psscriptanalyzer.ps1 @@ -0,0 +1,120 @@ +# Copyright © 2023 Chocolatey Software, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[cmdletBinding()] +Param( + [Parameter()] + [String] + $AnalyzePath, + + [Parameter()] + [String] + $SettingsPath, + + [Parameter()] + [String] + $OutputPath, + + [Parameter()] + [String[]] + $ExcludePaths +) + +#Requires -Modules PSScriptAnalyzer, ConvertToSARIF + +Push-Location -Path $AnalyzePath + +try { + if ($PSBoundParameters.ContainsKey('ExcludePaths')) { + $ExcludePaths = $ExcludePaths | Where-Object { Test-Path $_ } | ForEach-Object { (Resolve-Path -Path $_).Path } + } +} +finally { + Pop-Location +} + +$scripts = Get-ChildItem -Path $AnalyzePath -Filter "*.ps1" -Recurse | ForEach-Object { + $found = $false + if ($PSBoundParameters.ContainsKey('ExcludePaths')) { + foreach ($path in $ExcludePaths) { + if ($_.FullName.StartsWith($path)) { + $found = $true + } + } + } + if (-not $found) { + $_ + } +} + +$modules = Get-ChildItem -Path $AnalyzePath -Filter "*.psm1" -Recurse | ForEach-Object { + $found = $false + if ($PSBoundParameters.ContainsKey('ExcludePaths')) { + foreach ($path in $ExcludePaths) { + if ($_.FullName.StartsWith($path)) { + $found = $true + } + } + } + if (-not $found) { + $_ + } +} + +Write-Output "Analyzing module files..." + +$records = Start-Job -ArgumentList $modules, $SettingsPath { + Param( + $modules, + $SettingsPath + ) + $modules | Invoke-ScriptAnalyzer -Settings $SettingsPath | Select-Object RuleName, ScriptPath, Line, Message +} | Wait-Job | Receive-Job + +if (-not ($null -EQ $records)) { + Write-Output "Violations found in Module Files..." + $records | Format-List | Out-String + + Write-Output $OutputPath + + Write-Output "Writing violations to output file..." + $records | ConvertTo-SARIF -FilePath "$OutputPath\modules.sarif" +} +else { + Write-Output "No rule violations found in Module Files." +} + +Write-Output "Analyzing script files..." + +$records = Start-Job -ArgumentList $Scripts, $SettingsPath { + Param( + $Scripts, + $SettingsPath + ) + $Scripts | Invoke-ScriptAnalyzer -Settings $SettingsPath | Select-Object RuleName, ScriptPath, Line, Message +} | Wait-Job | Receive-Job + +if (-not ($null -EQ $records)) { + Write-Output "Violations found in Script Files..." + $records | Format-List | Out-String + + Write-Output "Writing violations to output file..." + $records | ConvertTo-SARIF -FilePath "$OutputPath\scripts.sarif" +} +else { + Write-Output "No rule violations found in Script Files." +} + +Write-Output "Analyzing complete." \ No newline at end of file diff --git a/Chocolatey.Cake.Recipe/Content/tasks.cake b/Chocolatey.Cake.Recipe/Content/tasks.cake index 133d25e..d5e4e33 100644 --- a/Chocolatey.Cake.Recipe/Content/tasks.cake +++ b/Chocolatey.Cake.Recipe/Content/tasks.cake @@ -39,6 +39,7 @@ public class BuildTasks public CakeTaskBuilder DotNetFormatCheckTask { get; set; } public CakeTaskBuilder DotNetFormatTask { get; set; } public CakeTaskBuilder AnalyzeTask { get; set; } + public CakeTaskBuilder PSScriptAnalyzerTask { get; set; } // Dependency-Check Tasks public CakeTaskBuilder DependencyCheckTask { get; set; } diff --git a/Chocolatey.Cake.Recipe/Content/tools.cake b/Chocolatey.Cake.Recipe/Content/tools.cake index 58510a7..eac1326 100644 --- a/Chocolatey.Cake.Recipe/Content/tools.cake +++ b/Chocolatey.Cake.Recipe/Content/tools.cake @@ -48,3 +48,15 @@ Action RequireTool = (tool, action) => { action(); }; + +Action RequirePSModule = (module, requiredVersion, action) => { + var powerShellModuleInstallationScript = GetFiles("./tools/Chocolatey.Cake.Recipe*/Content/install-module.ps1").FirstOrDefault(); + + StartPowershellFile(MakeAbsolute(powerShellModuleInstallationScript), args => + { + args.Append("ModuleName", module) + .Append("RequiredVersion", requiredVersion); + }); + + action(); +}; \ No newline at end of file diff --git a/Chocolatey.Cake.Recipe/Content/toolsettings.cake b/Chocolatey.Cake.Recipe/Content/toolsettings.cake index 26c6018..afc768f 100644 --- a/Chocolatey.Cake.Recipe/Content/toolsettings.cake +++ b/Chocolatey.Cake.Recipe/Content/toolsettings.cake @@ -43,6 +43,7 @@ public static class ToolSettings public static string ReportUnitTool { get; private set; } public static string ReSharperReportsTool { get; private set; } public static string ReSharperTools { get; private set; } + public static List PSScriptAnalyzerExcludePaths { get; private set; } public static string SonarQubeTool { get; private set; } public static string StrongNameSignerTool { get; private set; } public static string TestCoverageExcludeByAttribute { get; private set; } @@ -105,6 +106,7 @@ public static class ToolSettings FilePath eazfuscatorToolLocation = null, int? maxCpuCount = null, DirectoryPath outputDirectory = null, + List scriptAnalyzerExcludePaths = null, string testCoverageExcludeByAttribute = null, string testCoverageExcludeByFile = null, string testCoverageFilter = null @@ -118,6 +120,7 @@ public static class ToolSettings EazfuscatorToolLocation = eazfuscatorToolLocation ?? "./lib/Eazfuscator.NET/Eazfuscator.NET.exe"; MaxCpuCount = maxCpuCount ?? 0; OutputDirectory = outputDirectory; + PSScriptAnalyzerExcludePaths = scriptAnalyzerExcludePaths ?? new List { "tools", "code_drop", @"src\*\bin\Debug", @"Source\*\bin\Debug", @"src\*\bin\Release", @"Source\*\bin\Release", @"src\packages", @"Source\packages" }; TestCoverageExcludeByAttribute = testCoverageExcludeByAttribute ?? "*.ExcludeFromCodeCoverage*"; TestCoverageExcludeByFile = testCoverageExcludeByFile ?? "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs"; TestCoverageFilter = testCoverageFilter ?? string.Format("+[{0}*]* +[{1}*]* -[*.tests]* -[*.Tests]*", BuildParameters.Title, BuildParameters.Title.ToLowerInvariant());