Modules/businessdev.ALbuild.RuntimePackages/Private/Get-BcRuntimeLogTail.ps1

function Get-BcRuntimeLogTail {
    <#
    .SYNOPSIS
        Reads the lines a worker has appended to its log since the last look, and keeps only the ones
        that describe a phase.
 
    .DESCRIPTION
        Until this existed, a running slice showed nothing at all for the ~20 minutes a container takes
        to build: the worker logged 'Creating container ...' into its own file, and the dispatcher only
        replayed anything once the whole platform version had finished. The run log went quiet for
        twenty minutes and then printed one line - which reads exactly like a hung agent.
 
        Tailing the whole file is the other failure, and it is the one the previous design was written
        to escape: a container's raw output is tens of thousands of lines of publish/sync progress. So
        only the worker's OWN phase lines are surfaced - the ones it writes through Write-ALbuildLog,
        which all carry a '[<platform version>]' tag. Everything else stays in the file and travels to
        the 'worker-logs' artifact.
 
        The offset is returned rather than remembered, so the caller owns the state and this stays a
        pure-enough function to test against a file on disk.
 
        A partial last line is deliberately NOT consumed: the returned offset stops at the last newline,
        so a line that is still being written is read whole on the next pass instead of being cut in two.
 
    .PARAMETER Path
        The worker's log file. A file that does not exist yet is not an error - a worker that has not
        written its first line is the normal state right after it starts.
 
    .PARAMETER Offset
        Byte offset returned by the previous call; 0 on the first.
 
    .PARAMETER PlatformVersion
        The version whose phase lines are wanted. Used to recognise the worker's own lines.
 
    .OUTPUTS
        PSCustomObject with Offset (long) and Line (string[]).
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path,
        [long] $Offset = 0,
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $PlatformVersion
    )

    $empty = [PSCustomObject]@{ Offset = $Offset; Line = @() }
    if (-not (Test-Path -LiteralPath $Path)) { return $empty }

    # FileShare.ReadWrite, because the worker holds this file open with AutoFlush on. Get-Content's
    # default share mode loses the race against a writer often enough to matter, and losing it here
    # would take down the dispatcher rather than skip a progress line.
    $stream = $null
    try {
        $stream = [System.IO.FileStream]::new($Path, [System.IO.FileMode]::Open,
            [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
        if ($stream.Length -le $Offset) { return $empty }
        [void]$stream.Seek($Offset, [System.IO.SeekOrigin]::Begin)
        # Cast: the difference is a [long], and Windows PowerShell 5.1 will not size a byte[] from one.
        $buffer = [byte[]]::new([int]($stream.Length - $Offset))
        $read = $stream.Read($buffer, 0, $buffer.Length)
        $text = [System.Text.Encoding]::UTF8.GetString($buffer, 0, $read)
    }
    catch { return $empty }
    finally { if ($stream) { $stream.Dispose() } }

    # Stop at the last complete line; the remainder is read again next time.
    $cut = $text.LastIndexOf("`n")
    if ($cut -lt 0) { return $empty }
    $complete = $text.Substring(0, $cut + 1)
    $newOffset = $Offset + [System.Text.Encoding]::UTF8.GetByteCount($complete)

    $tag = "[$PlatformVersion]"
    $lines = [System.Collections.Generic.List[string]]::new()
    foreach ($line in ($complete -split "`r?`n")) {
        # The BOM is stripped before anything is matched. Windows PowerShell 5.1 writes one with
        # '-Encoding UTF8' (PowerShell 7 does not), UTF8.GetString keeps it, and U+FEFF is not
        # whitespace - so the FIRST line of a 5.1-written worker log arrived as "##vso[..." and
        # slipped past the '##vso*' filter, replaying an Azure DevOps logging command that the
        # checkpoint owns. The serial path of the factory is exactly the 5.1 path, so this is a real
        # log and not just a test fixture.
        $t = "$line".TrimStart([char]0xFEFF).Trim()
        if ($t -eq '') { continue }
        # The worker's own phase lines, and nothing else. Azure DevOps logging commands are dropped:
        # replaying '##vso[task.logissue ...]' from here would raise the same issue twice, once now
        # and once from the checkpoint that owns it.
        if ($t -like '##vso*') { continue }
        if (-not $t.Contains($tag)) { continue }
        $lines.Add($t)
    }

    return [PSCustomObject]@{ Offset = $newOffset; Line = $lines.ToArray() }
}