Modules/businessdev.ALbuild.RuntimePackages/Private/Get-BcRuntimeIssueClass.ps1
|
function Get-BcRuntimeIssueClass { <# .SYNOPSIS Reduces one failed (app, platform version) to the CAUSE behind it, so identical causes collapse. .DESCRIPTION A product that cannot compile against older majors fails once per platform version: ERiC produced 162 identical failures in run 27383, all of them the same line in app.json. Reporting those as 162 findings is a wall, not a report - and it is a wall the product team has to read before it can decide anything. The cause is the set of AL error codes the compiler produced, which is stable across versions and is what a developer recognises: 'AL0280+AL0282' is "we subscribe to events this Base Application does not have", 'AL0791' is "we reference namespaces it does not have". The SET, not the first code, because the order compiler diagnostics arrive in is not something to build a grouping on. Failures with no diagnostics at all - a container command that would not run, a signing tool that died - are classed by their message with the moving parts taken out: paths, guids, versions and bare numbers. Two of those collapse when they are the same problem and stay apart when they are not. .PARAMETER Message The failure message recorded for the app. NOT named -Error: '$Error' is a PowerShell automatic variable, and a parameter by that name shadows it for the whole function body. .PARAMETER Diagnostic The compiler diagnostics attributed to that app, if any. .OUTPUTS PSCustomObject: Key (stable, for grouping) and Label (for a human). #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [AllowEmptyString()] [AllowNull()] [string] $Message = '', [AllowEmptyCollection()] [string[]] $Diagnostic = @() ) # Two shapes, because the two engines phrase a compiler error differently and both must group the # same way. The container route hands over alc's own output ("… : error AL0791: …"); the source route # has no alc process, and its emit raises the codes inside the exception message # (" AL0791 <path>:4 - The namespace 'Processing' is unknown."). Only the first shape was matched, # so every source-engine failure fell through to message normalisation - where '-?\d+' turned AL0791 # into 'AL<n>' and the AL code was gone. Run 27417 lost its whole by-cause grouping to that. # # The message is searched as well as the diagnostics: two letters followed by four or more digits is # specific enough not to catch 'BC26', 'exit 1' or a container id like 'bdev-rt-8813a26f'. $codes = [System.Collections.Generic.SortedSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($text in (@($Diagnostic) + @("$Message"))) { if (-not "$text") { continue } foreach ($m in [regex]::Matches("$text", '\berror ([A-Za-z]{2}\d+):')) { [void]$codes.Add($m.Groups[1].Value) } foreach ($m in [regex]::Matches("$text", '\b([A-Za-z]{2}\d{4,})\b')) { [void]$codes.Add($m.Groups[1].Value) } } if ($codes.Count -gt 0) { $key = ($codes -join '+') return [PSCustomObject]@{ Key = $key; Label = "compiler errors $key" } } # No diagnostics: class by the message, with everything that varies per version removed. $norm = "$Message" $norm = $norm -replace '[A-Za-z]:\\[^\s'']+', '<path>' $norm = $norm -replace '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}', '<guid>' $norm = $norm -replace '\b\d+(\.\d+){2,3}\b', '<version>' $norm = $norm -replace '\b[0-9a-f]{8}\b', '<id>' $norm = $norm -replace '-?\d+', '<n>' $norm = ($norm -replace '\s+', ' ').Trim() if (-not $norm) { $norm = 'unknown failure' } return [PSCustomObject]@{ Key = $norm; Label = $norm } } |