Modules/businessdev.ALbuild.Core/Public/Get-ALbuildProjectConfig.ps1

function Get-ALbuildProjectConfig {
    <#
    .SYNOPSIS
        Loads the effective ALbuild project configuration for an app folder.
 
    .DESCRIPTION
        Builds an [ALbuildProjectConfig] by merging, in increasing precedence:
          1. Built-in defaults (the ALbuildProjectConfig constructor)
          2. The workspace-root config file (when -WorkspaceRoot is given and differs from the app
             folder) - the shared settings for the whole repository
          3. The app-folder config file - per-app overrides (e.g. a different country or test
             runner for a test app in a multi-root workspace)
 
        Each config file is 'albuild.json' (canonical) or, as a deprecated fallback, the V1
        'pipeline.config' (parsed with a deprecation warning). This is the single project config
        surface: one file per workspace and, optionally, one per app folder.
 
    .PARAMETER AppFolder
        The app folder whose effective configuration is requested.
 
    .PARAMETER WorkspaceRoot
        The workspace root that holds the shared config. When omitted (or equal to -AppFolder),
        only the app-folder config is applied.
 
    .EXAMPLE
        Get-ALbuildProjectConfig -AppFolder 'C:\repo\test' -WorkspaceRoot 'C:\repo'
 
    .OUTPUTS
        ALbuildProjectConfig.
    #>

    [CmdletBinding()]
    [OutputType('ALbuildProjectConfig')]
    param(
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $AppFolder,
        [string] $WorkspaceRoot
    )

    if (-not (Test-Path -LiteralPath $AppFolder)) {
        throw "App folder '$AppFolder' does not exist."
    }

    $config = [ALbuildProjectConfig]::new()

    # Build the ordered list of folders to merge (lowest precedence first), de-duplicated by their
    # resolved path so a single-root workspace does not read the same file twice.
    $folders = [System.Collections.Generic.List[string]]::new()
    $seen = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)

    if ($WorkspaceRoot -and (Test-Path -LiteralPath $WorkspaceRoot)) {
        $resolvedRoot = (Resolve-Path -LiteralPath $WorkspaceRoot).Path
        if ($seen.Add($resolvedRoot)) { $folders.Add($resolvedRoot) }
    }
    $resolvedApp = (Resolve-Path -LiteralPath $AppFolder).Path
    if ($seen.Add($resolvedApp)) { $folders.Add($resolvedApp) }

    foreach ($folder in $folders) {
        $settings = Read-ALbuildProjectConfigFile -Folder $folder
        if ($settings) { $config.Apply($settings, $true) }
    }

    return $config
}