microsoft/hve-core

Public

mirrored fromhttps://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
3a3a0fdf923d96a9e8a9ac734c73f24433b525e8

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/linting/Invoke-PSScriptAnalyzer.ps1

206lines Ā· modecode

1#!/usr/bin/env pwsh
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4#
5# Invoke-PSScriptAnalyzer.ps1
6#
7# Purpose: Wrapper for PSScriptAnalyzer with GitHub Actions integration
8# Author: HVE Core Team
9
10#Requires -Version 7.0
11
12[CmdletBinding()]
13param(
14 [Parameter(Mandatory = $false)]
15 [switch]$ChangedFilesOnly,
16
17 [Parameter(Mandatory = $false)]
18 [string]$BaseBranch = "origin/main",
19
20 [Parameter(Mandatory = $false)]
21 [string]$ConfigPath = (Join-Path $PSScriptRoot "PSScriptAnalyzer.psd1"),
22
23 [Parameter(Mandatory = $false)]
24 [string]$OutputPath = "logs/psscriptanalyzer-results.json"
25)
26
27$ErrorActionPreference = 'Stop'
28
29# Import shared helpers
30Import-Module (Join-Path $PSScriptRoot "Modules/LintingHelpers.psm1") -Force
31Import-Module (Join-Path $PSScriptRoot "../lib/Modules/CIHelpers.psm1") -Force
32
33#region Functions
34
35function Invoke-PSScriptAnalyzerCore {
36 [CmdletBinding()]
37 [OutputType([void])]
38 param(
39 [Parameter(Mandatory = $false)]
40 [switch]$ChangedFilesOnly,
41
42 [Parameter(Mandatory = $false)]
43 [string]$BaseBranch = "origin/main",
44
45 [Parameter(Mandatory = $false)]
46 [string]$ConfigPath = (Join-Path $PSScriptRoot "PSScriptAnalyzer.psd1"),
47
48 [Parameter(Mandatory = $false)]
49 [string]$OutputPath = "logs/psscriptanalyzer-results.json"
50 )
51
52 Write-Host "šŸ” Running PSScriptAnalyzer..." -ForegroundColor Cyan
53
54 # Ensure PSScriptAnalyzer is available
55 if (-not (Get-Module -ListAvailable -Name PSScriptAnalyzer)) {
56 Write-Host "Installing PSScriptAnalyzer module..." -ForegroundColor Yellow
57 Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser -Repository PSGallery
58 }
59
60 Import-Module PSScriptAnalyzer
61
62 # Get files to analyze
63 $filesToAnalyze = @()
64
65 if ($ChangedFilesOnly) {
66 Write-Host "Detecting changed PowerShell files..." -ForegroundColor Cyan
67 $filesToAnalyze = @(Get-ChangedFilesFromGit -BaseBranch $BaseBranch -FileExtensions @('*.ps1', '*.psm1', '*.psd1'))
68 }
69 else {
70 Write-Host "Analyzing all PowerShell files..." -ForegroundColor Cyan
71 $filesToAnalyze = @(Get-FilesRecursive -Path "." -Include @('*.ps1', '*.psm1', '*.psd1'))
72 }
73
74 if (@($filesToAnalyze).Count -eq 0) {
75 Write-Host "āœ… No PowerShell files to analyze" -ForegroundColor Green
76 Set-CIOutput -Name "count" -Value "0"
77 Set-CIOutput -Name "issues" -Value "0"
78 return
79 }
80
81 Write-Host "Analyzing $($filesToAnalyze.Count) PowerShell files..." -ForegroundColor Cyan
82 Set-CIOutput -Name "count" -Value $filesToAnalyze.Count
83
84 # Run PSScriptAnalyzer
85 $allResults = @()
86 $hasErrors = $false
87
88 foreach ($file in $filesToAnalyze) {
89 $filePath = if ($file -is [System.IO.FileInfo]) { $file.FullName } else { $file }
90 Write-Host "`nšŸ“„ Analyzing: $filePath" -ForegroundColor Cyan
91
92 $results = Invoke-ScriptAnalyzer -Path $filePath -Settings $ConfigPath
93
94 if ($results) {
95 $allResults += $results
96
97 foreach ($result in $results) {
98 $annotationLevel = switch ($result.Severity) {
99 'Error' { 'Error' }
100 'Warning' { 'Warning' }
101 'Information' { 'Notice' }
102 default { 'Notice' }
103 }
104
105 Write-CIAnnotation `
106 -Message "$($result.RuleName): $($result.Message)" `
107 -Level $annotationLevel `
108 -File $filePath `
109 -Line $result.Line `
110 -Column $result.Column
111
112 $icon = switch ($result.Severity) {
113 'Error' { 'āŒ'; $hasErrors = $true }
114 'Warning' { 'āš ļø' }
115 default { 'ā„¹ļø' }
116 }
117
118 Write-Host " $icon [$($result.Severity)] $($result.RuleName): $($result.Message) (Line $($result.Line))" -ForegroundColor $(
119 if ($result.Severity -eq 'Error') { 'Red' }
120 elseif ($result.Severity -eq 'Warning') { 'Yellow' }
121 else { 'Cyan' }
122 )
123 }
124 }
125 else {
126 Write-Host " āœ… No issues found" -ForegroundColor Green
127 }
128 }
129
130 # Export results
131 $summary = @{
132 TotalFiles = @($filesToAnalyze).Count
133 TotalIssues = @($allResults).Count
134 Errors = @($allResults | Where-Object Severity -eq 'Error').Count
135 Warnings = @($allResults | Where-Object Severity -eq 'Warning').Count
136 Information = @($allResults | Where-Object Severity -eq 'Information').Count
137 HasErrors = $hasErrors
138 }
139
140 # Ensure logs directory exists
141 $logsDir = Split-Path $OutputPath -Parent
142 if (-not (Test-Path $logsDir)) {
143 New-Item -ItemType Directory -Force -Path $logsDir | Out-Null
144 }
145
146 $allResults | ConvertTo-Json -Depth 5 | Out-File $OutputPath
147 $summary | ConvertTo-Json | Out-File (Join-Path $logsDir "psscriptanalyzer-summary.json")
148
149 # Set outputs
150 Set-CIOutput -Name "issues" -Value $summary.TotalIssues
151 Set-CIOutput -Name "errors" -Value $summary.Errors
152 Set-CIOutput -Name "warnings" -Value $summary.Warnings
153
154 if ($hasErrors) {
155 Set-CIEnv -Name "PSSCRIPTANALYZER_FAILED" -Value "true"
156 }
157
158 # Write summary
159 Write-CIStepSummary -Content "## PSScriptAnalyzer Results`n"
160
161 if ($summary.TotalIssues -eq 0) {
162 Write-CIStepSummary -Content "āœ… **Status**: Passed`n`nAll $($summary.TotalFiles) PowerShell files passed linting checks."
163 Write-Host "`nāœ… All PowerShell files passed PSScriptAnalyzer checks!" -ForegroundColor Green
164 return
165 }
166 else {
167 Write-CIStepSummary -Content @"
168āŒ **Status**: Failed
169
170| Metric | Count |
171|--------|-------|
172| Files Analyzed | $($summary.TotalFiles) |
173| Total Issues | $($summary.TotalIssues) |
174| Errors | $($summary.Errors) |
175| Warnings | $($summary.Warnings) |
176| Information | $($summary.Information) |
177"@
178
179 Write-Host "`nāŒ PSScriptAnalyzer found $($summary.TotalIssues) issue(s)" -ForegroundColor Red
180 throw "PSScriptAnalyzer found $($summary.TotalIssues) issue(s)"
181 }
182}
183
184#endregion Functions
185
186#region Main Execution
187
188if ($MyInvocation.InvocationName -ne '.') {
189 # Strip /mnt/* paths from PATH to avoid slow 9P cross-filesystem
190 # lookups in WSL. PSScriptAnalyzer resolves commands by scanning every
191 # PATH directory per file; Windows mount points add ~40s per file.
192 $env:PATH = ($env:PATH -split [System.IO.Path]::PathSeparator |
193 Where-Object { $_ -notlike '/mnt/*' }) -join [System.IO.Path]::PathSeparator
194
195 try {
196 Invoke-PSScriptAnalyzerCore -ChangedFilesOnly:$ChangedFilesOnly -BaseBranch $BaseBranch -ConfigPath $ConfigPath -OutputPath $OutputPath
197 exit 0
198 }
199 catch {
200 Write-Error -ErrorAction Continue "PSScriptAnalyzer failed: $($_.Exception.Message)"
201 Write-CIAnnotation -Message $_.Exception.Message -Level Error
202 exit 1
203 }
204}
205
206#endregion Main Execution
207