Modules/businessdev.ALbuild.Pipeline/Private/Get-AdoExpressionValue.ps1
|
function Split-AdoArgument { <# .SYNOPSIS Splits a comma-separated Azure DevOps expression argument list at the top level. .DESCRIPTION Internal helper: respects nested parentheses and single-quoted literals so commas inside them do not split the list. #> [CmdletBinding()] [OutputType([string[]])] param([Parameter(Mandatory)] [AllowEmptyString()] [string] $Text) $argList = [System.Collections.Generic.List[string]]::new() $depth = 0; $inQuote = $false; $current = [System.Text.StringBuilder]::new() foreach ($ch in $Text.ToCharArray()) { switch ($ch) { "'" { $inQuote = -not $inQuote; [void]$current.Append($ch) } '(' { if (-not $inQuote) { $depth++ }; [void]$current.Append($ch) } ')' { if (-not $inQuote) { $depth-- }; [void]$current.Append($ch) } ',' { if (-not $inQuote -and $depth -eq 0) { [void]$argList.Add($current.ToString()); [void]$current.Clear() } else { [void]$current.Append($ch) } } default { [void]$current.Append($ch) } } } if ($current.Length -gt 0 -or $argList.Count -gt 0) { [void]$argList.Add($current.ToString()) } return @($argList | ForEach-Object { $_.Trim() }) } function Get-AdoExpressionValue { <# .SYNOPSIS Evaluates the supported subset of an Azure DevOps template expression to a value. .DESCRIPTION Internal, pure evaluator for the local pipeline runner. Supports the functions used by the ALbuild templates - eq, ne, not, and, or, coalesce - and operands: parameters.<name>, variables.<name>, a bare loop/parameter name, 'single-quoted literals', true / false, and numbers. The full Azure DevOps expression grammar is intentionally NOT implemented. .PARAMETER Expr The expression text (the inside of a "${{ }}" or an "if" condition). .PARAMETER Parameters Resolved template parameters and loop variables (name -> value). .OUTPUTS The evaluated value (bool, string or int). #> [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyString()] [string] $Expr, [hashtable] $Parameters = @{} ) $e = $Expr.Trim() if ($e -eq '') { return '' } # Function call: name( args ) spanning the whole expression. $fn = [regex]::Match($e, '^(?<fn>[A-Za-z][A-Za-z0-9]*)\((?<args>.*)\)$') if ($fn.Success) { $name = $fn.Groups['fn'].Value.ToLowerInvariant() $parts = @(Split-AdoArgument -Text $fn.Groups['args'].Value) $values = @($parts | ForEach-Object { Get-AdoExpressionValue -Expr $_ -Parameters $Parameters }) $toString = { param($v) if ($v -is [bool]) { if ($v) { 'true' } else { 'false' } } else { "$v" } } $toBool = { param($v) if ($v -is [bool]) { return $v } $s = "$v" return ($s -and $s -ine 'false' -and $s -ne '0') } switch ($name) { 'eq' { return ((& $toString $values[0]) -ieq (& $toString $values[1])) } 'ne' { return -not ((& $toString $values[0]) -ieq (& $toString $values[1])) } 'not' { return -not (& $toBool $values[0]) } 'and' { foreach ($v in $values) { if (-not (& $toBool $v)) { return $false } } return $true } 'or' { foreach ($v in $values) { if (& $toBool $v) { return $true } } return $false } 'coalesce' { foreach ($v in $values) { if ("$v" -ne '') { return $v } } return '' } default { throw "Unsupported expression function '$name' in '$Expr'." } } } # Operands. if ($e -ieq 'true') { return $true } if ($e -ieq 'false') { return $false } if ($e -match "^'(.*)'$") { return $matches[1] } if ($e -match '^-?\d+$') { return [int]$e } $key = $e if ($e -match '^(parameters|variables)\.(.+)$') { $key = $matches[2] } if ($Parameters.ContainsKey($key)) { return $Parameters[$key] } return $e } |