1+ name : PSScriptAnalyzer
2+
3+ on :
4+ push :
5+ branches : [ main ]
6+ paths :
7+ - ' **.ps1'
8+ - ' .github/workflows/psscriptanalyzer.yml'
9+ pull_request :
10+ branches : [ main ]
11+ paths :
12+ - ' **.ps1'
13+ - ' .github/workflows/psscriptanalyzer.yml'
14+ workflow_dispatch :
15+
16+ jobs :
17+ analyze :
18+ name : PSScriptAnalyzer
19+ runs-on : ubuntu-latest
20+
21+ steps :
22+ - name : Checkout repository
23+ uses : actions/checkout@v4
24+
25+ - name : Install PSScriptAnalyzer
26+ shell : pwsh
27+ run : |
28+ Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
29+ Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser
30+ Write-Host "PSScriptAnalyzer version: $(Get-Module -Name PSScriptAnalyzer -ListAvailable | Select-Object -ExpandProperty Version)"
31+
32+ - name : Run PSScriptAnalyzer
33+ id : analysis
34+ shell : pwsh
35+ run : |
36+ # Initialize counters
37+ $errorCount = 0
38+ $warningCount = 0
39+ $infoCount = 0
40+
41+ Write-Host "============================================"
42+ Write-Host "Running PSScriptAnalyzer on IntuneAssignmentChecker.ps1"
43+ Write-Host "============================================"
44+ Write-Host ""
45+
46+ # Run the analysis
47+ $results = Invoke-ScriptAnalyzer -Path ./IntuneAssignmentChecker.ps1 -IncludeDefaultRules -RecurseCustomRulePath
48+
49+ if ($results) {
50+ # Group by severity
51+ $grouped = $results | Group-Object -Property Severity
52+
53+ # Count by severity
54+ foreach ($group in $grouped) {
55+ switch ($group.Name) {
56+ 'Error' { $errorCount = $group.Count }
57+ 'Warning' { $warningCount = $group.Count }
58+ 'Information' { $infoCount = $group.Count }
59+ }
60+ }
61+
62+ Write-Host "📊 Analysis Summary"
63+ Write-Host "==================="
64+ Write-Host "❌ Errors: $errorCount"
65+ Write-Host "⚠️ Warnings: $warningCount"
66+ Write-Host "ℹ️ Information: $infoCount"
67+ Write-Host ""
68+
69+ # Display errors first
70+ $errors = $results | Where-Object { $_.Severity -eq 'Error' }
71+ if ($errors) {
72+ Write-Host "❌ ERRORS" -ForegroundColor Red
73+ Write-Host "========" -ForegroundColor Red
74+ foreach ($error in $errors) {
75+ Write-Host "Line $($error.Line): [$($error.RuleName)] $($error.Message)" -ForegroundColor Red
76+ }
77+ Write-Host ""
78+ }
79+
80+ # Display warnings
81+ $warnings = $results | Where-Object { $_.Severity -eq 'Warning' }
82+ if ($warnings) {
83+ Write-Host "⚠️ WARNINGS" -ForegroundColor Yellow
84+ Write-Host "==========" -ForegroundColor Yellow
85+
86+ # Group warnings by rule for better readability
87+ $warningGroups = $warnings | Group-Object -Property RuleName | Sort-Object Count -Descending
88+
89+ foreach ($group in $warningGroups) {
90+ Write-Host ""
91+ Write-Host " Rule: $($group.Name) (Count: $($group.Count))" -ForegroundColor Cyan
92+
93+ # Show first 5 examples of each warning type
94+ $examples = $group.Group | Select-Object -First 5
95+ foreach ($warning in $examples) {
96+ Write-Host " Line $($warning.Line): $($warning.Message)" -ForegroundColor Yellow
97+ }
98+
99+ if ($group.Count -gt 5) {
100+ Write-Host " ... and $($group.Count - 5) more instances" -ForegroundColor DarkGray
101+ }
102+ }
103+ Write-Host ""
104+ }
105+
106+ # Display information
107+ $info = $results | Where-Object { $_.Severity -eq 'Information' }
108+ if ($info -and $env:SHOW_INFO -eq 'true') {
109+ Write-Host "ℹ️ INFORMATION" -ForegroundColor Blue
110+ Write-Host "=============" -ForegroundColor Blue
111+ foreach ($item in $info) {
112+ Write-Host "Line $($item.Line): [$($item.RuleName)] $($item.Message)" -ForegroundColor Blue
113+ }
114+ Write-Host ""
115+ }
116+
117+ # Export results for artifact
118+ $results | ConvertTo-Json -Depth 5 | Out-File -FilePath analysis-results.json
119+
120+ # Set outputs for later steps
121+ echo "error_count=$errorCount" >> $env:GITHUB_OUTPUT
122+ echo "warning_count=$warningCount" >> $env:GITHUB_OUTPUT
123+ echo "info_count=$infoCount" >> $env:GITHUB_OUTPUT
124+ echo "has_errors=$($errorCount -gt 0)" >> $env:GITHUB_OUTPUT
125+
126+ # Exit with error if errors found (configurable)
127+ if ($errorCount -gt 0 -and $env:FAIL_ON_ERROR -eq 'true') {
128+ Write-Host "❌ Analysis failed due to errors" -ForegroundColor Red
129+ exit 1
130+ }
131+ } else {
132+ Write-Host "✅ No issues found! The script passes all PSScriptAnalyzer rules." -ForegroundColor Green
133+ echo "error_count=0" >> $env:GITHUB_OUTPUT
134+ echo "warning_count=0" >> $env:GITHUB_OUTPUT
135+ echo "info_count=0" >> $env:GITHUB_OUTPUT
136+ echo "has_errors=false" >> $env:GITHUB_OUTPUT
137+ }
138+ env :
139+ FAIL_ON_ERROR : false # Set to true if you want the workflow to fail on errors
140+ SHOW_INFO : false # Set to true to show information level issues
141+
142+ - name : Generate Detailed Report
143+ if : always()
144+ shell : pwsh
145+ run : |
146+ # Generate a detailed markdown report
147+ $reportContent = @"
148+ # PSScriptAnalyzer Report
149+
150+ **Script:** IntuneAssignmentChecker.ps1
151+ **Date:** $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
152+ **Analyzer Version:** $(Get-Module -Name PSScriptAnalyzer -ListAvailable | Select-Object -ExpandProperty Version)
153+
154+ ## Summary
155+
156+ | Severity | Count |
157+ |----------|-------|
158+ | ❌ Errors | ${{ steps.analysis.outputs.error_count }} |
159+ | ⚠️ Warnings | ${{ steps.analysis.outputs.warning_count }} |
160+ | ℹ️ Information | ${{ steps.analysis.outputs.info_count }} |
161+
162+ "@
163+
164+ if (Test-Path analysis-results.json) {
165+ $results = Get-Content analysis-results.json | ConvertFrom-Json
166+
167+ if ($results) {
168+ # Add detailed findings grouped by rule
169+ $reportContent += "`n## Detailed Findings by Rule`n`n"
170+
171+ $grouped = $results | Group-Object -Property RuleName, Severity
172+
173+ foreach ($group in ($grouped | Sort-Object { $_.Group[0].Severity }, Name)) {
174+ $severity = $group.Group[0].Severity
175+ $icon = switch ($severity) {
176+ 'Error' { '❌' }
177+ 'Warning' { '⚠️' }
178+ 'Information' { 'ℹ️' }
179+ default { '❓' }
180+ }
181+
182+ $reportContent += "### $icon $($group.Name -replace ',.*') ($($group.Count) instances)`n`n"
183+
184+ # Show up to 10 examples
185+ $examples = $group.Group | Select-Object -First 10
186+ $reportContent += "| Line | Message |`n|------|---------|`n"
187+
188+ foreach ($item in $examples) {
189+ $message = $item.Message -replace '\|', '\|' -replace '\n', ' '
190+ if ($message.Length -gt 100) {
191+ $message = $message.Substring(0, 97) + "..."
192+ }
193+ $reportContent += "| $($item.Line) | $message |`n"
194+ }
195+
196+ if ($group.Count -gt 10) {
197+ $reportContent += "`n*... and $($group.Count - 10) more instances*`n"
198+ }
199+
200+ $reportContent += "`n"
201+ }
202+ } else {
203+ $reportContent += "`n## ✅ No Issues Found`n`nThe script passes all PSScriptAnalyzer rules!`n"
204+ }
205+ }
206+
207+ # Write report to file
208+ $reportContent | Out-File -FilePath analysis-report.md
209+
210+ # Also output to job summary
211+ $reportContent >> $env:GITHUB_STEP_SUMMARY
212+
213+ - name : Upload Analysis Results
214+ if : always()
215+ uses : actions/upload-artifact@v4
216+ with :
217+ name : psscriptanalyzer-results
218+ path : |
219+ analysis-results.json
220+ analysis-report.md
221+ retention-days : 30
222+
223+ - name : Comment PR (if applicable)
224+ if : github.event_name == 'pull_request' && always()
225+ uses : actions/github-script@v7
226+ with :
227+ github-token : ${{secrets.GITHUB_TOKEN}}
228+ script : |
229+ const fs = require('fs');
230+
231+ // Read the report
232+ let report = '## PSScriptAnalyzer Results\n\n';
233+
234+ const errorCount = '${{ steps.analysis.outputs.error_count }}';
235+ const warningCount = '${{ steps.analysis.outputs.warning_count }}';
236+
237+ if (errorCount === '0' && warningCount === '0') {
238+ report += '✅ **All checks passed!** No issues found.';
239+ } else {
240+ report += `Found **${errorCount}** error(s) and **${warningCount}** warning(s).\n\n`;
241+ report += 'See the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.';
242+ }
243+
244+ // Find and update or create comment
245+ const { data: comments } = await github.rest.issues.listComments({
246+ owner: context.repo.owner,
247+ repo: context.repo.repo,
248+ issue_number: context.issue.number,
249+ });
250+
251+ const botComment = comments.find(comment =>
252+ comment.user.type === 'Bot' &&
253+ comment.body.includes('PSScriptAnalyzer Results')
254+ );
255+
256+ if (botComment) {
257+ await github.rest.issues.updateComment({
258+ owner: context.repo.owner,
259+ repo: context.repo.repo,
260+ comment_id: botComment.id,
261+ body: report
262+ });
263+ } else {
264+ await github.rest.issues.createComment({
265+ owner: context.repo.owner,
266+ repo: context.repo.repo,
267+ issue_number: context.issue.number,
268+ body: report
269+ });
270+ }
271+
272+ - name : Set Status Check
273+ if : always()
274+ shell : pwsh
275+ run : |
276+ $hasErrors = "${{ steps.analysis.outputs.has_errors }}"
277+ $errorCount = "${{ steps.analysis.outputs.error_count }}"
278+ $warningCount = "${{ steps.analysis.outputs.warning_count }}"
279+
280+ if ($hasErrors -eq 'true') {
281+ Write-Host "❌ Status: Failed - Found $errorCount error(s)" -ForegroundColor Red
282+ # Uncomment the next line to fail the workflow on errors
283+ # exit 1
284+ } elseif ($warningCount -gt 0) {
285+ Write-Host "⚠️ Status: Passed with warnings - Found $warningCount warning(s)" -ForegroundColor Yellow
286+ } else {
287+ Write-Host "✅ Status: Passed - No issues found" -ForegroundColor Green
288+ }
0 commit comments