private/Step-BuildBootCoreWinPEApp.ps1

#Requires -PSEdition Core

function Step-BuildBootCoreWinPEApp {
    <#
    .SYNOPSIS
        Runs configured and profile-local WinPE app scripts
 
    .DESCRIPTION
        Runs script paths from global BuildMedia.WinPEApp, followed by PowerShell
        scripts discovered in the build profile's build-winpeapp directory and its
        immediate subdirectories. Paths are processed in configured-first order, and
        duplicate paths are run only once. Missing paths produce warnings.
 
        Each script runs in the caller's session and can modify caches, build state,
        and the mounted image. Success-stream output from invoked scripts is passed
        through.
 
    .EXAMPLE
        PS> Step-BuildBootCoreWinPEApp
 
        Runs the configured and profile-local WinPE app scripts.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        System.Object. Passes through success-stream output produced by each invoked script.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 0.1.0
        Date: 2026-08-28
 
        Requires global BuildMedia with WinPEApp and BuildProfile properties.
        BuildProfileContentPath optionally overrides the profile-local content directory.
    #>

    [CmdletBinding()]
    param ()

    # Start with scripts explicitly configured in osdeployboot.json.
    $WinPEApp = @($global:BuildMedia.WinPEApp | Where-Object { $_ })
    $BuildProfilePath = $global:BuildMedia.BuildProfile
    $BuildProfileContentPath = $global:BuildMedia.BuildProfileContentPath
    if (-not $BuildProfileContentPath -and $BuildProfilePath) {
        $BuildProfileContentPath = Split-Path -Path $BuildProfilePath -Parent
    }
    $ProfileAppPath = if ($BuildProfileContentPath) {
        Join-Path $BuildProfileContentPath 'build-winpeapp'
    }

    # Append profile-local scripts from build-winpeapp and its immediate subdirectories.
    if ($ProfileAppPath -and (Test-Path -LiteralPath $ProfileAppPath -PathType Container)) {
        $WinPEApp += Get-ChildItem -LiteralPath $ProfileAppPath -Filter '*.ps1' -File -Recurse -Depth 1 -ErrorAction SilentlyContinue |
            Sort-Object FullName |
            Select-Object -ExpandProperty FullName
    }

    # Preserve execution order while preventing the same path from running more than once.
    $SeenScripts = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    $WinPEApp = @($WinPEApp | Where-Object { $SeenScripts.Add([System.String]$_) })

    # Run each available script and warn when a configured path no longer exists.
    foreach ($Item in $WinPEApp) {
        if (Test-Path -LiteralPath $Item -PathType Leaf) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] build-winpeapp: $Item"
            & "$Item"
        }
        else {
            Write-Warning "[$(Get-Date -Format s)] BootImage App Script not found: $Item"
        }
    }
}