Expand-BuildStep.ps1
function Expand-BuildStep { <# .Synopsis Expands Build Steps in a single build object .Description Component Files are .ps1 or datafiles within a directory that tells you what type they are. .Example Expand-BuildStep -StepMap @{Steps='InstallPester','RunPester'} .Link Convert-BuildStep .Link Import-BuildStep #> param( # A map of step properties to underlying data. # Each key is the name of a property the output. # Each value may contain the name of another Step or StepMap [Parameter(Mandatory)] [Collections.IDictionary] $StepMap, # The immediate parent object [PSObject] $Parent, # The absolute root object [PSObject] $Root, # If set, the component will be expanded as a singleton (single object) [switch] $Singleton, # A list of item names that automatically become singletons [string[]]$SingleItemName, # A list of item names that automatically become plurals [string[]]$PluralItemName, [ValidateSet('ADO', 'GitHubActions')] [string]$BuildSystem = 'ADO', # The name of parameters that should be supplied from build variables. # Wildcards accepted. [Parameter(ValueFromPipelineByPropertyName)] [string[]] $VariableParameter, # The name of parameters that should be supplied from the environment. # Wildcards accepted. [Parameter(ValueFromPipelineByPropertyName)] [string[]] $EnvironmentParameter, # The name of parameters that should be referred to uniquely. # For instance, if converting function foo($bar) {} and -UniqueParameter is 'bar' # The build parameter would be foo_bar. [Parameter(ValueFromPipelineByPropertyName)] [string[]] $UniqueParameter, # A collection of default parameters. [Parameter(ValueFromPipelineByPropertyName)] [Collections.IDictionary] $DefaultParameter = @{} ) begin { $convertBuildStepCmd = $ExecutionContext.SessionState.InvokeCommand.GetCommand('Convert-BuildStep','Function') } process { $theComponentMetaData = $ComponentMetaData.$BuildSystem $theComponentNames = $ComponentNames.$BuildSystem $outObject = [Ordered]@{} $splatMe = @{} + $PSBoundParameters if (-not $Root) { $Root = $outObject $splatMe.Root = $outObject } $splatMe.Remove('StepMap') :nextKey foreach ($kv in $stepMap.GetEnumerator()) { if ($kv.Key.EndsWith('s') -and -not $singleton) { # Already pluralized $thingType = $kv.Key.Substring(0,$kv.Key.Length -1) $propName = $kv.Key } elseif ($parent) { $thingType = $kv.Key $propName = $kv.Key } else { $thingType = $kv.Key $propName = if ($SingleItemName -notcontains $thingType -and $thingType -notmatch '\W$' -and $theComponentNames.Keys -contains $thingType) { $kv.Key.Substring(0,1).ToLower() + $kv.Key.Substring(1) + 's' } else { $kv.Key.Substring(0,1).ToLower() + $kv.Key.Substring(1) $singleton = $true } } $outValue = :nextValue foreach ($v in $kv.Value) { $metaData = $theComponentMetaData["$thingType.$v"] if ($propName -eq $thingType -and -not $singleton) { if ($v -is [Collections.IDictionary]) { $splatMe.StepMap = $v Expand-BuildStep @splatMe } else { $v } continue nextValue } $o = if ($metaData.Extension -eq '.psd1') { $data = Import-LocalizedData -BaseDirectory ([IO.Path]::GetDirectoryName($metaData.Path)) -FileName ([IO.PATH]::GetFileName($metaData.Path)) if (-not $data) { continue nextValue } $fileText = [IO.File]::ReadAllText($metaData.Path) $data = & ([ScriptBlock]::Create(($FileText -replace '@{', '[Ordered]@{'))) $splatMe.Parent = $stepMap if ($data -is [Collections.IDictionary]) { $splatMe.StepMap = $data try { Expand-BuildStep @splatMe } catch { Write-Debug "Could not Expand $($kv.Id): $_" } } else { $data } } elseif ($v -is [Collections.IDictionary]) { $splatMe.Parent = $stepMap $splatMe.StepMap = $v Expand-BuildStep @splatMe } else { $convertedBuildStep = if ($metaData) { $convertSplat = @{} + $PsBoundParameters foreach ($k in @($convertSplat.Keys)) { if (-not $convertBuildStepCmd.Parameters[$k]) { $convertSplat.Remove($k) } } $metaData | Convert-BuildStep @convertSplat } if ($convertedBuildStep) { if ($convertedBuildStep.parameters) { if ($BuildSystem -eq 'ADO' -and $Root) { if ($root.parameters -and $convertedBuildStep.parameters -is [Collections.IDictionary]) { foreach ($keyValue in $convertedBuildStep.parameters.GetEnumerator()) { $root.Parameters[$keyValue.Key] = $keyValue.Value } } else { $root.parameters = $convertedBuildStep.parameters } $convertedBuildStep.Remove('parameters') } } $convertedBuildStep } else { $v } } if ($metaData.Name -and $Option.$($metaData.Name) -is [Collections.IDictionary]) { $o2 = [Ordered]@{} + $o foreach ($ov in @($Option.($metaData.Name).GetEnumerator())) { $o2[$ov.Key] = $ov.Value } $o2 } else { $o } } $outObject[$propName] = $outValue if ($outObject[$propName] -isnot [Collections.IList] -and $kv.Value -is [Collections.IList] -and -not $singleton) { $outObject[$propName] = @($outObject[$propName]) } elseif ($outObject[$propName] -is [Collections.IList] -and $kv.Value -isnot [Collections.IList]) { $outObject[$propName] = $outObject[$propName][0] } if ($PluralItemName -contains $propName -and $outObject[$propName] -isnot [Collections.IList]) { $outObject[$propName] = @($outObject[$propName]) } } $outObject } } |