Modules/businessdev.ALbuild.RuntimePackages/Private/Get-BcRuntimeAppDiagnostic.ps1
|
function Get-BcRuntimeAppDiagnostic { <# .SYNOPSIS Picks the compiler diagnostics that belong to ONE app out of a worker log. .DESCRIPTION A worker log holds every app of one platform version, one after the other. Grepping it for 'error AL....' and printing the hits under whichever app just failed is how a log starts lying: run 27378 reported 'logo.png is being used by another process' as a failure of Sanction Screen while the path in the message named the API. The log has honest boundaries - the compile of each app is announced with "Compiling '<name>'" and closed with 'Compilation ended' - so the diagnostics are read between them, and only the LAST such block is used: a chain app may be compiled several times in one container, and the failure is in the attempt that just happened. .PARAMETER LogLine The worker log for one platform version. Optional, and empty content is allowed: a Mandatory [string[]] rejects a single empty element with "cannot bind argument ... because it is an empty string", and a worker log whose only line is blank is a perfectly ordinary thing. In run 27404 that binding error came out of the checkpoint and took both Legacy slices with it. .PARAMETER AppName The app whose diagnostics are wanted. .PARAMETER Max Upper bound; the rest are summarised as a count. .OUTPUTS System.String[] - the diagnostic lines, already trimmed of their absolute build path. #> [CmdletBinding()] [OutputType([string[]])] param( [AllowNull()] [AllowEmptyCollection()] [AllowEmptyString()] [string[]] $LogLine = @(), [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $AppName, [ValidateRange(1, 50)] [int] $Max = 5 ) # Two statements, not an 'if' expression: an if that yields a ONE-element array collapses to the # element on assignment, and on Windows PowerShell 5.1 a scalar has no .Count at all - under # Set-StrictMode that is a terminating error. Build 27406's dual-target leg caught both halves of # this; a PS7-only run sees neither, because there every object has .Count. $lines = @() if ($null -ne $LogLine) { $lines = @($LogLine) } if ($lines.Count -eq 0) { return @() } $start = -1 for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match "Compiling '$([regex]::Escape($AppName))'") { $start = $i } } if ($start -lt 0) { return @() } $found = [System.Collections.Generic.List[string]]::new() for ($i = $start; $i -lt $lines.Count; $i++) { if ($i -gt $start -and $lines[$i] -match 'Compilation ended') { break } if ($lines[$i] -notmatch '\berror [A-Za-z]{2}\d+:') { continue } # Drop the build path: it is a temp folder with a guid in it, the same on every line, and it # pushes the part that matters off the right edge. $found.Add(($lines[$i] -replace '^.*[\\/](?=[^\\/]+\.(al|json)\()', '' -replace '^\s+', '')) } if ($found.Count -le $Max) { return $found.ToArray() } $head = @($found | Select-Object -First $Max) return @($head + @("... and $($found.Count - $Max) more diagnostic(s) - the full log is in the 'worker-logs' artifact")) } |