diff --git a/Chocolatey.Cake.Recipe/Content/analyzing.cake b/Chocolatey.Cake.Recipe/Content/analyzing.cake index 83cbb82..b599293 100644 --- a/Chocolatey.Cake.Recipe/Content/analyzing.cake +++ b/Chocolatey.Cake.Recipe/Content/analyzing.cake @@ -76,4 +76,5 @@ BuildParameters.Tasks.CreateIssuesReportTask = Task("CreateIssuesReport") BuildParameters.Tasks.AnalyzeTask = Task("Analyze") .IsDependentOn("InspectCode") - .IsDependentOn("CreateIssuesReport"); + .IsDependentOn("CreateIssuesReport") + .IsDependentOn("Run-PSScriptAnalyzer"); 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/parameters.cake b/Chocolatey.Cake.Recipe/Content/parameters.cake index 7708b2c..686152c 100644 --- a/Chocolatey.Cake.Recipe/Content/parameters.cake +++ b/Chocolatey.Cake.Recipe/Content/parameters.cake @@ -28,6 +28,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; } @@ -89,6 +90,7 @@ public static class BuildParameters public static bool ShouldRunReportUnit { 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 DirectoryPath SolutionDirectoryPath { get; private set; } @@ -197,6 +199,7 @@ public static class BuildParameters context.Information("ShouldRunReportUnit: {0}", BuildParameters.ShouldRunReportUnit); 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("SolutionFilePath: {0}", context.MakeAbsolute((FilePath)SolutionFilePath)); @@ -231,6 +234,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, @@ -282,6 +286,7 @@ public static class BuildParameters bool shouldRunReportUnit = true, bool? shouldRunTransifex = null, bool shouldRunxUnit = true, + bool shouldRunPSScriptAnalyzer = true, bool shouldStrongNameOutputAssemblies = true, bool shouldStrongNameSignDependentAssemblies = true, DirectoryPath solutionDirectoryPath = null, @@ -337,6 +342,7 @@ public static class BuildParameters GetFilesToObfuscate = getFilesToObfuscate; GetFilesToSign = getFilesToSign; GetILMergeConfigs = getILMergeConfigs; + GetPSScriptAnalyzerSettings = getPSScriptAnalyzerSettings; GetMsisToSign = getMsisToSign; GetProjectsToPack = getProjectsToPack; GetScriptsToSign = getScriptsToSign; @@ -399,6 +405,7 @@ public static class BuildParameters ShouldRunReportUnit = shouldRunReportUnit; ShouldRunTransifex = shouldRunTransifex ?? TransifexIsConfiguredForRepository(context); ShouldRunxUnit = shouldRunxUnit; + ShouldRunPSScriptAnalyzer = shouldRunPSScriptAnalyzer; ShouldStrongNameOutputAssemblies = shouldStrongNameOutputAssemblies; ShouldStrongNameSignDependentAssemblies = shouldStrongNameSignDependentAssemblies; SolutionDirectoryPath = solutionDirectoryPath ?? sourceDirectoryPath.Combine(title); diff --git a/Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake b/Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake new file mode 100644 index 0000000..5e4724d --- /dev/null +++ b/Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake @@ -0,0 +1,76 @@ +BuildParameters.Tasks.PSScriptAnalyzerTask = Task("Run-PSScriptAnalyzer") + .WithCriteria(() => BuildParameters.ShouldRunPSScriptAnalyzer, "Skipping because PSScriptAnalyzer is not enabled") + .Does(() => +{ + 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; + } + + 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") + .SetFormatOutput(true) + .WithArguments(args => { + args.AppendQuoted("AnalyzePath", PSScriptAnalyzerSetting.AnalysisPath.ToString()) + .AppendQuoted("SettingsPath", PSScriptAnalyzerSetting.SettingsPath.ToString()) + .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 excludePaths = new List { "tools", "code_drop"}; + + StartPowershellFile(MakeAbsolute(powerShellAnalysisScript), new PowershellSettings() + .WithModule("PSScriptAnalyzer") + .SetFormatOutput(true) + .WithArguments(args => { + args.AppendQuoted("AnalyzePath", BuildParameters.RootDirectoryPath.ToString()) + .AppendQuoted("SettingsPath", settingsFile.ToString()) + .AppendArray("ExcludePaths", excludePaths); + })); + } +}); + +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..05477cc --- /dev/null +++ b/Chocolatey.Cake.Recipe/Content/run-psscriptanalyzer.ps1 @@ -0,0 +1,51 @@ +[cmdletBinding()] +Param( + [Parameter()] + [String] + $AnalyzePath, + + [Parameter()] + [String] + $SettingsPath, + + [Parameter()] + [String[]] + $ExcludePaths +) + +#Requires -Modules PSScriptAnalyzer + +Push-Location -Path $AnalyzePath + +try { + if ($PSBoundParameters.ContainsKey('ExcludePaths')) { + $ExcludePaths = $ExcludePaths | ForEach-Object { (Resolve-Path -Path $_).Path } + } + + $scripts = Get-ChildItem -Path $AnalyzePath -Filter "*.ps1" -Recurse | ForEach-Object { + if ($PSBoundParameters.ContainsKey('ExcludePaths')) { + foreach ($path in $ExcludePaths) { + if ($_.FullName.StartsWith($path)) { + return + } + } + } + $_ + } + $modules = Get-ChildItem -Path $AnalyzePath -Filter "*.psm1" -Recurse | ForEach-Object { + if ($PSBoundParameters.ContainsKey('ExcludePaths')) { + foreach ($path in $ExcludePaths) { + if ($_.FullName.StartsWith($path)) { + return + } + } + } + $_ + } + + $modules | Invoke-ScriptAnalyzer -Settings $SettingsPath | Select-Object RuleName, ScriptPath, Line, Message + $Scripts | Invoke-ScriptAnalyzer -Settings $SettingsPath | Select-Object RuleName, ScriptPath, Line, Message +} +finally { + Pop-Location +} \ No newline at end of file diff --git a/Chocolatey.Cake.Recipe/Content/tasks.cake b/Chocolatey.Cake.Recipe/Content/tasks.cake index 7cb12b4..e3c8e8b 100644 --- a/Chocolatey.Cake.Recipe/Content/tasks.cake +++ b/Chocolatey.Cake.Recipe/Content/tasks.cake @@ -22,6 +22,7 @@ public class BuildTasks public CakeTaskBuilder InspectCodeTask { get; set; } public CakeTaskBuilder CreateIssuesReportTask { get; set; } public CakeTaskBuilder AnalyzeTask { get; set; } + public CakeTaskBuilder PSScriptAnalyzerTask { get; set; } // Eazfuscator Tasks public CakeTaskBuilder ObfuscateAssembliesTask { get; set; }