Skip to content

Commit

Permalink
[CI Example Analyzer] Support analysis for any PowerShell script (Azu…
Browse files Browse the repository at this point in the history
…re#19191)

* support analyze any powershell script

* fix a bug

* update

* change clean script to not clean
  • Loading branch information
MoChilia authored Aug 12, 2022
1 parent d5455f6 commit 2a60e0f
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 53 deletions.
65 changes: 35 additions & 30 deletions tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@
.PARAMETER MarkdownPaths
Markdown searching paths. Empty for current path. Supports wildcard.
.PARAMETER ScriptPath
PowerShell script searching path.
PowerShell script searching paths. Empty for current path. Supports wildcard.
.PARAMETER RulePaths
PSScriptAnalyzer custom rules paths. Empty for current path. Supports wildcard.
.PARAMETER CodeMapPath
Code map path bound with the PowerShell script.
.PARAMETER Recurse
To search markdowns recursively in the folders.
To search files recursively in the folders.
.PARAMETER IncludeDefaultRules
To analyze default rules provided by PSScriptAnalyzer.
.PARAMETER OutputFolder
Folder path storing output files.
.PARAMETER SkipAnalyzing
To skip analyzing step. Only extracting example codes from markdowns to the temp script.
.PARAMETER CleanScripts
To clean the temp script.
.PARAMETER NotCleanScripts
Do not clean the temp script.
.NOTES
File Name: Measure-MarkdownOrScript.ps1
#>
Expand All @@ -32,17 +30,14 @@ param (
[string[]]$MarkdownPaths,
[Parameter(Mandatory, ParameterSetName = "Script")]
[AllowEmptyString()]
[string[]]$ScriptPath,
[string[]]$ScriptPaths,
[string[]]$RulePaths,
[Parameter(Mandatory, ParameterSetName = "Script")]
[string]$CodeMapPath,
[Parameter(ParameterSetName = "Markdown")]
[switch]$Recurse,
[switch]$IncludeDefaultRules,
[string]$OutputFolder = "$PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleAnalysis",
[Parameter(ParameterSetName = "Markdown")]
[switch]$SkipAnalyzing,
[switch]$CleanScripts
[switch]$NotCleanScripts
)

. $PSScriptRoot\utils.ps1
Expand All @@ -51,17 +46,24 @@ $analysisResultsTable = @()
$codeMap = @()
$totalLine = 1

$tempScript = "TempScript.ps1"
$tempScriptMap = "TempScript.Map.csv"
$TempScriptPath = "$OutputFolder\$tempScript"
$TempScriptMapPath = "$OutputFolder\$tempScriptMap"

# Clean caches, remove files in "output" folder
Remove-Item $TempScriptPath -ErrorAction SilentlyContinue
Remove-Item $TempScriptMapPath -ErrorAction SilentlyContinue
Remove-Item $PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleIssues.csv -ErrorAction SilentlyContinue
Remove-Item $OutputFolder -ErrorAction SilentlyContinue
# Create output folder and temp script
$null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue
$null = New-Item -ItemType File $TempScriptPath

# Find examples in "help\*.md", output ".ps1"
if ($PSCmdlet.ParameterSetName -eq "Markdown") {
# Clean caches, remove files in "output" folder
Remove-Item $OutputFolder\TempScript.ps1 -ErrorAction SilentlyContinue
Remove-Item $OutputFolder\TempCodeMap.csv -ErrorAction SilentlyContinue
Remove-Item $PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleIssues.csv -ErrorAction SilentlyContinue
Remove-Item $OutputFolder -ErrorAction SilentlyContinue
$null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue
$null = New-Item -ItemType File $OutputFolder\TempScript.ps1
# When the input $MarkdownPaths is the path of txt file
if ($MarkdownPaths -cmatch ".*\.txt") {
# When the input $MarkdownPaths is the path of txt file contained markdown paths
if ((Test-Path $MarkdownPaths -PathType Leaf) -and $MarkdownPaths.EndsWith(".txt")) {
$MarkdownPath = Get-Content $MarkdownPaths
}
# When the input $MarkdownPaths is the path of a folder
Expand All @@ -83,27 +85,29 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") {
}
$cmdlet = $_.BaseName
$result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName `
-OutputFolder $OutputFolder `
-TempScriptPath $TempScriptPath `
-TotalLine $totalLine
$analysisResultsTable += $result.Errors
$codeMap += $result.CodeMap
$totalLine = $result.TotalLine
}
}
$codeMap| Export-Csv "$OutputFolder\TempCodeMap.csv" -NoTypeInformation
if (!$SkipAnalyzing.IsPresent) {
$ScriptPath = "$OutputFolder\TempScript.ps1"
$CodeMapPath = "$OutputFolder\TempCodeMap.csv"
if(!$NotCleanScripts.IsPresent){
$codeMap| Export-Csv $TempScriptMapPath -NoTypeInformation
}
}

# Analyze scripts
if ($PSCmdlet.ParameterSetName -eq "Script" -or !$SkipAnalyzing.IsPresent) {
# Read code map from file
$codeMap = Import-Csv $CodeMapPath
if ($PSCmdlet.ParameterSetName -eq "Script"){
$codeMap = Merge-Scripts -ScriptPaths $ScriptPaths -Recurse:$Recurse.IsPresent -TempScriptPath $TempScriptPath
if(!$NotCleanScripts.IsPresent){
$codeMap| Export-Csv $TempScriptMapPath -NoTypeInformation
}
}
# Read and analyze ".ps1" in \ScriptsByExample
Write-Output "Analyzing file ..."
$analysisResultsTable += Get-ScriptAnalyzerResult (Get-Item -Path $ScriptPath) $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent $codeMap -ErrorAction Continue
$analysisResultsTable += Get-ScriptAnalyzerResult -ScriptPath $TempScriptPath -RulePaths $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -CodeMap $codeMap -ErrorAction Continue

# Summarize analysis results, output in Result.csv
if($analysisResultsTable){
Expand All @@ -112,6 +116,7 @@ if ($PSCmdlet.ParameterSetName -eq "Script" -or !$SkipAnalyzing.IsPresent) {
}

# Clean caches
if ($CleanScripts.IsPresent) {
Remove-Item $ScriptPath -Exclude *.csv -Recurse -ErrorAction Continue
if (!$NotCleanScripts.IsPresent) {
Remove-Item $TempScriptPath -ErrorAction Continue
Remove-Item $OutputFolder -ErrorAction SilentlyContinue
}
96 changes: 73 additions & 23 deletions tools/StaticAnalysis/ExampleAnalyzer/utils.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
Get-NonExceptionRecord
Get-RecordsNotInAllowList
Measure-SectionMissingAndOutputScript
Merge-Contents
Merge-Scripts
Get-ScriptAnalyzerResult
Set-AnalysisOutput
Set-ExampleProperties
Expand Down Expand Up @@ -234,7 +236,7 @@ function Measure-SectionMissingAndOutputScript {
[string]$Module,
[string]$Cmdlet,
[string]$MarkdownPath,
[string]$OutputFolder,
[string]$TempScriptPath,
[int]$TotalLine
)
$missingSeverity = 1
Expand Down Expand Up @@ -357,27 +359,11 @@ function Measure-SectionMissingAndOutputScript {
}
}


# Output example codes to "TempScript.ps1"
if ($missingExampleCode -eq 0) {
$cmdletExamplesScriptPath = "$OutputFolder\TempScript.ps1"
$line = $exampleCodes.Count
if($line -ne 0){
$functionHead = "function $Module-$Cmdlet-$exampleNumber{"
Add-Content -Path (Get-Item $cmdletExamplesScriptPath).FullName -Value $functionHead
Add-Content -Path (Get-Item $cmdletExamplesScriptPath).FullName -Value $exampleCodes
$functionTail = "}"
Add-Content -Path (Get-Item $cmdletExamplesScriptPath).FullName -Value $functionTail
for($i = 0; $i -le $line + 1; $i++){
$codeMap += @{
TotalLine = $TotalLine + $i
Module = $Module
Cmdlet = $Cmdlet
Example = $exampleNumber
Line = $i
}
}
$TotalLine = $TotalLine + $line + 2
($tempCodeMap, $TotalLine) = Merge-Contents -Content $exampleCodes -Module $Module -Cmdlet $Cmdlet -Example $exampleNumber -TotalLine $TotalLine -TempScriptPath $TempScriptPath
$codeMap += $tempCodeMap
}
}
}
Expand All @@ -394,6 +380,68 @@ function Measure-SectionMissingAndOutputScript {
}
}

<#
.SYNOPSIS
Merge the example codes or scripts into one PowerShell script and generate the code map.
#>
function Merge-Contents {
param(
[string[]]$Contents,
[string]$Module,
[string]$Cmdlet,
[int]$Example,
[int]$TotalLine,
[string]$TempScriptPath
)
$codeMap =@()
$line = $Contents.Count
$functionHead = "function $Module"
if($null -ne $Cmdlet){
$functionHead += "-$Cmdlet"
}
if($null -ne $exampleNumber){
$functionHead += "-$exampleNumber"
}
$functionHead += "{"
Add-Content -Path (Get-Item $TempScriptPath).FullName -Value $functionHead
Add-Content -Path (Get-Item $TempScriptPath).FullName -Value $Contents
Add-Content -Path (Get-Item $TempScriptPath).FullName -Value "}"
for($i = 0; $i -le $line + 1; $i++){
$codeMap += @{
TotalLine = $TotalLine + $i
Module = $Module
Cmdlet = $Cmdlet
Example = $Example
Line = $i
}
}
$TotalLine = $TotalLine + $line + 2
return ($codeMap, $TotalLine)
}

<#
.SYNOPSIS
Merge PowerShell scripts into one and generate the code map.
#>
function Merge-Scripts {
param(
[string]$ScriptPaths,
[switch]$Recurse,
[string]$TempScriptPath
)
$TotalLine = 1
$codeMap = @()
foreach($_ in Get-ChildItem $ScriptPaths -Recurse:$Recurse.IsPresent){
if((Test-Path $_ -PathType Leaf) -and $_.FullName.EndsWith(".ps1")){
$fileName = (Get-Item -Path $_.FullName).Name
$scriptContent = Get-Content $_
($tempCodeMap, $TotalLine) = Merge-Contents -Content $scriptContent -Module $fileName -TotalLine $TotalLine -TempScriptPath $TempScriptPath
$codeMap += $tempCodeMap
}
}
return $codeMap
}

<#
.SYNOPSIS
Set properties for an AnalysisOutput object.
Expand Down Expand Up @@ -429,12 +477,14 @@ function Set-AnalysisOutput {
<#
.SYNOPSIS
Invoke PSScriptAnalyzer with custom rules, return the error set.
.PARAMETER RulePath
PSScriptAnalyzer custom rules path. Supports wildcard.
#>
function Get-ScriptAnalyzerResult {
param (
[string]$ScriptPath,
[Parameter(Mandatory, HelpMessage = "PSScriptAnalyzer custom rules path. Supports wildcard.")]
[string[]]$RulePath,
[Parameter(Mandatory)]
[string[]]$RulePaths,
[switch]$IncludeDefaultRules,
[Object[]]$CodeMap
)
Expand All @@ -444,11 +494,11 @@ function Get-ScriptAnalyzerResult {
}

# Invoke PSScriptAnalyzer : input scriptblock, output error set in $result with property: RuleName, Message, Extent
if ($null -eq $RulePath) {
if ($null -eq $RulePaths) {
$analysisResults = Invoke-ScriptAnalyzer -Path $ScriptPath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent
}
else {
$analysisResults = Invoke-ScriptAnalyzer -Path $ScriptPath -CustomRulePath $RulePath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent
$analysisResults = Invoke-ScriptAnalyzer -Path $ScriptPath -CustomRulePath $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent
}
$errors = @()
foreach($analysisResult in $analysisResults){
Expand Down

0 comments on commit 2a60e0f

Please sign in to comment.