Modules/businessdev.ALbuild.Containers/Public/Get-BcImage.ps1

function Get-BcImage {
    <#
    .SYNOPSIS
        Lists the cached version-specific Business Central images on this host.
 
    .DESCRIPTION
        Reports the images New-BcImage built, identified by the 'albuild.image=bc' label rather than by
        a name pattern, so an image is never mistaken for one of ours because its tag happened to match.
 
        LastUsedUtc comes from a marker file per image under the ALbuild base folder. Docker records
        when an image was created but not when it was last used, and creation time is a poor proxy for a
        cache like this one: an image is built once and then reused for months, so evicting by age would
        throw out precisely the versions that are being asked for most. A file per image - rather than
        one shared index - means concurrent workers never need to coordinate a read-modify-write.
 
    .PARAMETER ImageName
        Optional filter on the image tag.
 
    .PARAMETER DockerExecutable
        Docker executable.
 
    .OUTPUTS
        PSCustomObject[]: ImageName, ArtifactUrl, SizeBytes, CreatedUtc, LastUsedUtc, InUse.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param(
        [string] $ImageName,
        [string] $DockerExecutable = 'docker'
    )

    $listed = Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -PassThru -SuccessExitCodes @(0, 1) `
        -Arguments @('image', 'ls', '--filter', 'label=albuild.image=bc', '--format', '{{.Repository}}:{{.Tag}}')
    if ($listed.ExitCode -ne 0) { return @() }

    $names = @("$($listed.StdOut)" -split "`r?`n" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
    if ($ImageName) { $names = @($names | Where-Object { $_ -eq $ImageName }) }

    $inUse = @()
    $ps = Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -PassThru -SuccessExitCodes @(0, 1) `
        -Arguments @('ps', '-a', '--format', '{{.Image}}')
    if ($ps.ExitCode -eq 0) {
        $inUse = @("$($ps.StdOut)" -split "`r?`n" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
    }

    $result = foreach ($name in $names) {
        $inspect = Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -PassThru -SuccessExitCodes @(0, 1) `
            -Arguments @('image', 'inspect', '--format', '{{.Created}}|{{index .Config.Labels "albuild.artifacturl"}}', $name)
        $created = $null; $artifactUrl = ''
        if ($inspect.ExitCode -eq 0) {
            $parts = "$($inspect.StdOut)".Trim() -split '\|', 2
            $parsed = [datetime]::MinValue
            if ([datetime]::TryParse($parts[0], [ref]$parsed)) { $created = $parsed.ToUniversalTime() }
            if ($parts.Count -gt 1) { $artifactUrl = $parts[1] }
        }

        $marker = Get-BcImageUsageMarkerPath -ImageName $name
        $lastUsed = if (Test-Path -LiteralPath $marker) { (Get-Item -LiteralPath $marker).LastWriteTimeUtc } else { $created }

        [PSCustomObject]@{
            ImageName   = $name
            ArtifactUrl = $artifactUrl
            SizeBytes   = Get-BcImageSizeBytes -ImageName $name -DockerExecutable $DockerExecutable
            CreatedUtc  = $created
            LastUsedUtc = $lastUsed
            InUse       = $inUse -contains $name
        }
    }

    return @($result)
}