Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(#32) Add PSScriptAnalyzer #73

Merged
merged 1 commit into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Chocolatey.Cake.Recipe/Content/analyzing.cake
Original file line number Diff line number Diff line change
Expand Up @@ -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)) });
})
);
Expand All @@ -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");
70 changes: 70 additions & 0 deletions Chocolatey.Cake.Recipe/Content/formatting-settings.psd1
Original file line number Diff line number Diff line change
@@ -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
}
}
}
34 changes: 34 additions & 0 deletions Chocolatey.Cake.Recipe/Content/install-module.ps1
Original file line number Diff line number Diff line change
@@ -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
}
15 changes: 14 additions & 1 deletion Chocolatey.Cake.Recipe/Content/parameters.cake
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public static class BuildParameters
public static Func<FilePathCollection> GetFilesToObfuscate { get; private set; }
public static Func<FilePathCollection> GetFilesToSign { get; private set; }
public static Func<List<ILMergeConfig>> GetILMergeConfigs { get; private set; }
public static Func<List<PSScriptAnalyzerSettings>> GetPSScriptAnalyzerSettings { get; private set; }
public static Func<FilePathCollection> GetMsisToSign { get; private set; }
public static Func<FilePathCollection> GetProjectsToPack { get; private set; }
public static Func<FilePathCollection> GetScriptsToSign { get; private set; }
Expand Down Expand Up @@ -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; }
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -353,6 +356,7 @@ public static class BuildParameters
Func<FilePathCollection> getFilesToObfuscate = null,
Func<FilePathCollection> getFilesToSign = null,
Func<List<ILMergeConfig>> getILMergeConfigs = null,
Func<List<PSScriptAnalyzerSettings>> getPSScriptAnalyzerSettings = null,
Func<FilePathCollection> getMsisToSign = null,
Func<FilePathCollection> getProjectsToPack = null,
Func<FilePathCollection> getScriptsToSign = null,
Expand Down Expand Up @@ -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<BuildVersion, object[]> slackMessageArguments = null,
Expand Down Expand Up @@ -485,6 +490,7 @@ public static class BuildParameters
GetFilesToObfuscate = getFilesToObfuscate;
GetFilesToSign = getFilesToSign;
GetILMergeConfigs = getILMergeConfigs;
GetPSScriptAnalyzerSettings = getPSScriptAnalyzerSettings;
GetMsisToSign = getMsisToSign;
GetProjectsToPack = getProjectsToPack;
GetScriptsToSign = getScriptsToSign;
Expand Down Expand Up @@ -743,6 +749,13 @@ public static class BuildParameters
ShouldRunOpenCover = context.Argument<bool>("shouldRunOpenCover");
}

ShouldRunPSScriptAnalyzer = shouldRunPSScriptAnalyzer;

if (context.HasArgument("shouldRunPSScriptAnalyzer"))
{
ShouldRunPSScriptAnalyzer = context.Argument<bool>("shouldRunPSScriptAnalyzer");
}

ShouldRunReportGenerator = shouldRunReportGenerator;

if (context.HasArgument("shouldRunReportGenerator"))
Expand Down Expand Up @@ -779,7 +792,7 @@ public static class BuildParameters
}

ShouldRunxUnit = shouldRunxUnit;

if (context.HasArgument("shouldRunxUnit"))
{
ShouldRunxUnit = context.Argument<bool>("shouldRunxUnit");
Expand Down
5 changes: 5 additions & 0 deletions Chocolatey.Cake.Recipe/Content/paths.cake
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -75,6 +76,7 @@ public class BuildPaths
nugetNuspecDirectory,
testResultsDirectory,
inspectCodeResultsDirectory,
scriptAnalyzerResultsDirectory,
NUnitTestResultsDirectory,
xUnitTestResultsDirectory,
testCoverageDirectory,
Expand Down Expand Up @@ -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; }
Expand All @@ -188,6 +191,7 @@ public class BuildDirectories
DirectoryPath nugetNuspecDirectory,
DirectoryPath testResults,
DirectoryPath inspectCodeTestResults,
DirectoryPath scriptAnalyzerResults,
DirectoryPath nunitTestResults,
DirectoryPath xunitTestResults,
DirectoryPath testCoverage,
Expand All @@ -210,6 +214,7 @@ public class BuildDirectories
NuGetNuspecDirectory = nugetNuspecDirectory;
TestResults = testResults;
InspectCodeTestResults = inspectCodeTestResults;
PSScriptAnalyzerResults = scriptAnalyzerResults;
NUnitTestResults = nunitTestResults;
xUnitTestResults = xunitTestResults;
TestCoverage = testCoverage;
Expand Down
124 changes: 124 additions & 0 deletions Chocolatey.Cake.Recipe/Content/psscriptanalyzer.cake
Original file line number Diff line number Diff line change
@@ -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", () =>
gep13 marked this conversation as resolved.
Show resolved Hide resolved
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<String> ExcludePaths { get; set; }
public string Name { get; set; }

public PSScriptAnalyzerSettings()
{
Name = "Unnamed";
}

public PSScriptAnalyzerSettings(FilePath analysisPath,
FilePath settingsPath,
List<String> excludePaths = null,
string name = "Unnamed")
{
AnalysisPath = analysisPath;
SettingsPath = settingsPath;
ExcludePaths = excludePaths;
Name = name;
}
}
Loading