@@ -56,11 +56,10 @@ jobs:
5656 ProSuite-Hub
5757 SysAdmin-Tools
5858
59- # Severity gating policy:
60- # - Workflow FAILS if any "Error" findings exist
59+ # Fail gate: job fails if ANY of these severities exist
6160 FAIL_ON_SEVERITIES : " Error"
6261
63- # Optional: cap the amount of findings shown in summary
62+ # Optional: cap the amount of findings shown in summary/report
6463 SUMMARY_TOP : " 25"
6564
6665 steps :
@@ -118,7 +117,6 @@ jobs:
118117 $settingsPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.SETTINGS_FILE }}'
119118 if (-not (Test-Path $settingsPath)) { throw "Missing settings file: $settingsPath" }
120119
121- # Must be a hashtable when imported
122120 $cfg = Import-PowerShellDataFile -Path $settingsPath
123121 if (-not ($cfg -is [hashtable])) { throw "Settings file is not a hashtable: $settingsPath" }
124122
@@ -142,6 +140,39 @@ jobs:
142140 echo "ref=${GITHUB_REF}" >> "$GITHUB_OUTPUT"
143141 echo "repo=${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"
144142
143+ - name : 🗂️ Ensure report folder exists
144+ if : steps.discover.outputs.count != '0'
145+ shell : pwsh
146+ run : |
147+ $ErrorActionPreference = 'Stop'
148+ $outDir = Join-Path $env:GITHUB_WORKSPACE '${{ env.OUT_DIR }}'
149+ New-Item -ItemType Directory -Force -Path $outDir | Out-Null
150+ Write-Host "Report folder ready: $outDir"
151+
152+ # OPTION A: Always create a baseline SARIF so Code Scanning never breaks due to missing file
153+ - name : ✅ Create baseline SARIF (always)
154+ if : steps.discover.outputs.count != '0'
155+ shell : pwsh
156+ run : |
157+ $ErrorActionPreference = 'Stop'
158+ $root = $env:GITHUB_WORKSPACE
159+ $outDir = Join-Path $root '${{ env.OUT_DIR }}'
160+ $sarif = Join-Path $outDir '${{ env.OUT_SARIF }}'
161+ New-Item -ItemType Directory -Force -Path $outDir | Out-Null
162+
163+ $baseline = @{
164+ '$schema' = 'http://json.schemastore.org/sarif-2.1.0'
165+ version = '2.1.0'
166+ runs = @(
167+ @{
168+ tool = @{ driver = @{ name = 'PSScriptAnalyzer'; version = '${{ env.PSA_VERSION }}' } }
169+ results = @()
170+ }
171+ )
172+ }
173+ $baseline | ConvertTo-Json -Depth 10 | Set-Content -Path $sarif -Encoding UTF8
174+ Write-Host "Baseline SARIF created: $sarif"
175+
145176 - name : 🔎 Run PSScriptAnalyzer + Build Reports (JSON/CSV/MD/SARIF)
146177 if : steps.discover.outputs.count != '0'
147178 shell : pwsh
@@ -160,7 +191,6 @@ jobs:
160191
161192 $prune = [regex]::new('${{ env.PRUNE_DIR_REGEX }}')
162193
163- # Resolve folder roots (string paths only)
164194 $roots = @("${{ env.ANALYZE_ROOTS }}".Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)) |
165195 ForEach-Object { Join-Path $root $_ } |
166196 Where-Object { Test-Path $_ -and -not $prune.IsMatch($_) }
@@ -182,33 +212,33 @@ jobs:
182212 foreach ($x in $results) {
183213 $rel = $x.ScriptPath.Replace("$root$([IO.Path]::DirectorySeparatorChar)", '').Replace('\','/')
184214 $rows += [pscustomobject]@{
185- Severity = [string]$x.Severity
186- RuleName = [string]$x.RuleName
187- Message = [string]$x.Message
188- File = $rel
189- Line = [int]$x.Line
190- Column = [int]$x.Column
191- EndLine = [int]$x.EndLine
192- EndColumn = [int]$x.EndColumn
193- ScriptName = [string]$x.ScriptName
215+ Severity = [string]$x.Severity
216+ RuleName = [string]$x.RuleName
217+ Message = [string]$x.Message
218+ File = $rel
219+ Line = [int]$x.Line
220+ Column = [int]$x.Column
221+ EndLine = [int]$x.EndLine
222+ EndColumn = [int]$x.EndColumn
223+ ScriptName = [string]$x.ScriptName
194224 }
195225 }
196226
197- # JSON (full)
227+ # JSON
198228 $rows | ConvertTo-Json -Depth 8 | Set-Content -Path $jsonPath -Encoding UTF8
199229
200- # CSV (business-friendly)
230+ # CSV
201231 $rows | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
202232
203- # SARIF (simple + reliable )
233+ # SARIF (overwrite baseline with actual results )
204234 $psaVersion = (Get-Module -ListAvailable PSScriptAnalyzer | Sort-Object Version -Descending | Select-Object -First 1).Version.ToString()
205235 $sarifResults = @()
206236
207237 foreach ($r in $rows) {
208238 $sarifLevel = switch ($r.Severity.ToLowerInvariant()) {
209- 'error' { 'error' }
210- 'warning' { 'warning' }
211- default { 'note' }
239+ 'error' { 'error' }
240+ 'warning' { 'warning' }
241+ default { 'note' }
212242 }
213243
214244 $sarifResults += @{
@@ -241,9 +271,9 @@ jobs:
241271 }
242272 $sarif | ConvertTo-Json -Depth 12 | Set-Content -Path $sarifPath -Encoding UTF8
243273
244- # Markdown report (executive + technical)
245- $total = $rows.Count
246- $bySev = $rows | Group-Object Severity | Sort-Object Name
274+ # Markdown report
275+ $total = $rows.Count
276+ $bySev = $rows | Group-Object Severity | Sort-Object Name
247277 $byRule = $rows | Group-Object RuleName | Sort-Object Count -Descending
248278
249279 $repo = '${{ steps.meta.outputs.repo }}'
@@ -285,9 +315,10 @@ jobs:
285315 $md.Add("| $($i.Severity) | `$($i.RuleName)` | [$($i.File)]($fileLink) | $($i.Line) | $msg |")
286316 }
287317
288- $md -join "`n" | Set-Content -Path $mdPath -Encoding UTF8
318+ $mdText = ($md -join "`n")
319+ $mdText | Set-Content -Path $mdPath -Encoding UTF8
289320
290- # Job summary (short)
321+ # Job summary (short) - SAFE join
291322 $summary = New-Object System.Collections.Generic.List[string]
292323 $summary.Add("### 🧪 PowerShell Lint Summary")
293324 $summary.Add("- 🕒 **UTC:** $ts")
@@ -298,7 +329,8 @@ jobs:
298329 $summary.Add("")
299330 $summary.Add("Artifacts: JSON / CSV / MD / SARIF are in `${{ env.OUT_DIR }}`")
300331
301- $summary -join "`n" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding utf8
332+ $summaryText = ($summary -join "`n")
333+ $summaryText | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding utf8
302334
303335 # Gate: fail if any configured severities exist
304336 $failSev = @("${{ env.FAIL_ON_SEVERITIES }}".Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries))
@@ -310,15 +342,16 @@ jobs:
310342 }
311343
312344 - name : 📦 Upload Report Artifacts (Structured)
313- if : always()
345+ if : always() && hashFiles(format('{0}/{1}/*', github.workspace, env.OUT_DIR)) != ''
314346 uses : actions/upload-artifact@v4
315347 with :
316348 name : powershell-lint-reports
317349 path : ${{ github.workspace }}/${{ env.OUT_DIR }}
318350 retention-days : 30
319351
352+ # OPTION B: Only upload SARIF if it exists
320353 - name : 🛰️ Upload SARIF to GitHub Code Scanning
321- if : always()
354+ if : always() && hashFiles(format('{0}/{1}/{2}', github.workspace, env.OUT_DIR, env.OUT_SARIF)) != ''
322355 uses : github/codeql-action/upload-sarif@v4
323356 with :
324357 sarif_file : ${{ github.workspace }}/${{ env.OUT_DIR }}/${{ env.OUT_SARIF }}
0 commit comments