Skip to content

Commit 085a42a

Browse files
ugurkocdeclaude
andcommitted
Add PSScriptAnalyzer GitHub Action workflow
- Runs on push, PR, and manual trigger for PowerShell files - Analyzes IntuneAssignmentChecker.ps1 for code quality issues - Groups results by severity (Errors, Warnings, Information) - Generates detailed markdown reports with summary statistics - Creates GitHub job summaries for easy viewing - Uploads analysis results as artifacts - Adds PR comments with analysis summary - Configurable to fail on errors if needed Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f78c3c1 commit 085a42a

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
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

Comments
 (0)