public/Add-WpcDefaultKubernetesBackendOptions.ps1

function Add-WpcDefaultKubernetesBackendOptions {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object] $Pipeline,

        # One or more images that should receive the default
        # `backend_options` + `environment` injection. Originally a single
        # string (main_build_ci_image); now accepts a list so the per-stack
        # variants (web_build_ci_image, docker_build_ci_image) get the same
        # treatment without each pipeline having to declare its own resource
        # limits / env defaults.
        [Parameter(Mandatory)]
        [string[]] $Images,

        [Parameter(Mandatory)]
        [object] $BackendOptions,

        [object] $StepEnvironment = $null
    )

    if ($Pipeline -isnot [System.Collections.IDictionary]) {
        return $Pipeline
    }

    if (-not (Test-WpcDictionaryKey -Dictionary $Pipeline -Key 'steps')) {
        return $Pipeline
    }

    $imageSet = @($Images | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
    if ($imageSet.Count -eq 0) {
        return $Pipeline
    }

    $usesCiImage = $false
    foreach ($step in @($Pipeline['steps'])) {
        if ($step -isnot [System.Collections.IDictionary]) {
            continue
        }

        if (-not (Test-WpcDictionaryKey -Dictionary $step -Key 'image')) {
            continue
        }

        if ($imageSet -notcontains [string]$step['image']) {
            continue
        }
        $usesCiImage = $true

        if (Test-WpcDictionaryKey -Dictionary $step -Key 'backend_options') {
            if ($StepEnvironment) {
                Add-WpcStepEnvironmentDefaults -Step $step -EnvironmentDefaults $StepEnvironment
            }

            continue
        }

        $step['backend_options'] = Copy-WpcNode $BackendOptions
        if ($StepEnvironment) {
            Add-WpcStepEnvironmentDefaults -Step $step -EnvironmentDefaults $StepEnvironment
        }
    }

    # Pin any pipeline that uses a CI-builder image to the kubernetes backend, so
    # a non-kubernetes agent can never pick it up. Without this, the local
    # desktop-release agents (local backend) match label-less CI workflows and
    # their backend tries to `exec` the image string as a path, failing with
    # `exec: "...ci-builder-web:0.4.0": no such file or directory`. Local-shell
    # pipelines (desktop macos/windows-local) use no CI image, so $usesCiImage
    # stays false and they keep targeting their own labelled agents. Existing
    # `labels.backend` is preserved.
    if ($usesCiImage) {
        if (-not (Test-WpcDictionaryKey -Dictionary $Pipeline -Key 'labels')) {
            $Pipeline['labels'] = [ordered]@{}
        }
        if ($Pipeline['labels'] -is [System.Collections.IDictionary] -and
            -not (Test-WpcDictionaryKey -Dictionary $Pipeline['labels'] -Key 'backend')) {
            $Pipeline['labels']['backend'] = 'kubernetes'
        }
    }

    return $Pipeline
}