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.
 
    .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(
        [Parameter(Mandatory)] [AllowEmptyCollection()] [string[]] $LogLine,
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $AppName,
        [ValidateRange(1, 50)] [int] $Max = 5
    )

    $start = -1
    for ($i = 0; $i -lt $LogLine.Count; $i++) {
        if ($LogLine[$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 $LogLine.Count; $i++) {
        if ($i -gt $start -and $LogLine[$i] -match 'Compilation ended') { break }
        if ($LogLine[$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(($LogLine[$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"))
}