src/functions/qbuild.ps1
|
function Invoke-QBuild { [CmdletBinding()] param( [ArgumentCompleter({ param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) try { $mapPath = if ($fakeBoundParameters.map) { $fakeBoundParameters.map } else { "./.build.map.ps1" } $localMapExists = Test-Path $mapPath $userMap = try { Import-ConfigMap -Map $fakeBoundParameters.map -Fallback "./.build.map.ps1" -ErrorAction Stop } catch { if ($_.Exception.Message -match '^map file .* not found$|^No map provided and fallback .* not found$|^map is null and defaultMapFile is not provided$') { $null } else { throw } } if ($userMap) { $userMap = $userMap | Assert-ConfigMap } $completions = [System.Collections.Generic.List[string]]::new() if ($userMap) { foreach ($c in @(Get-EntryCompletion $userMap -language "build" @PSBoundParameters)) { if ($c) { $completions.Add([string]$c) | Out-Null } } } $qbuildModule = (Get-Command Invoke-QBuild).Module $internalCommands = & $qbuildModule { Get-ConfigMapInternalCommands -Language build } foreach ($item in $internalCommands.GetEnumerator()) { if (-not ([string]$item.Key).StartsWith($wordToComplete)) { continue } $includeCompletion = & $qbuildModule { param($commandEntry, $mapExists) Test-ConfigMapCommandCompletion -Entry $commandEntry -LocalMapExists $mapExists } $item.Value $localMapExists if (-not $includeCompletion) { continue } if (-not $completions.Contains([string]$item.Key)) { $completions.Add([string]$item.Key) | Out-Null } } try { $resolvedMapPath = Resolve-MapPath -Map $userMap if ($resolvedMapPath) { foreach ($procName in @(Get-ConfigMapProcessNamesInMap -Map $userMap)) { $procWords = Get-ConfigMapProcessCompleteWord ` -ProcessName $procName ` -MapPath $resolvedMapPath ` -Word $wordToComplete if ($null -eq $procWords) { continue } foreach ($w in @($procWords)) { if ($w -and -not $completions.Contains([string]$w)) { $completions.Add([string]$w) | Out-Null } } } } } catch { # No file path / process discovery failure → keep map-only completions } return @($completions) } catch { return "ERROR [-entry]: $($_.Exception.Message) $($_.ScriptStackTrace)" } })] [Parameter(Position = 0)] $entry = $null, [Parameter(DontShow)] $command = "exec", [Parameter(DontShow)] $map = "./.build.map.ps1", [Parameter(ValueFromRemainingArguments = $true, DontShow = $true)] [string[]]$RemainingArguments ) dynamicparam { if (-not $entry) { return } try { $dynUserMap = try { . $ImportConfigMap -Map $map -Fallback "./.build.map.ps1" | Assert-ConfigMap } catch { if ($_.Exception.Message -match '^map file .* not found$|^No map provided and fallback .* not found$|^map is null and defaultMapFile is not provided$') { $null } else { throw } } $dynMap = Merge-ConfigMapCommands -Map $dynUserMap -Language build if (Test-ConfigMapInternalCommand -Name $entry -Language build) { return Get-EntryDynamicParam $dynMap $entry $command -skip 0 -bound $PSBoundParameters } if (!$dynUserMap) { return } $settings = Get-ConfigMapSettingsForPath -Map $dynMap -Path $entry if ($settings.Process -ne 'qbuild') { $mapPath = Resolve-MapPath -Map $dynMap $followHint = [bool]$PSBoundParameters['Follow'] if (-not $mapPath) { return Get-ProcessDynamicParam -Map $dynMap -ProcessKey $entry -Follow:$followHint } return New-ConfigMapProcessDynamicParam ` -ProcessName $settings.Process ` -MapPath $mapPath ` -Map $dynMap ` -Entry $entry ` -Follow:$followHint } $result = Get-EntryDynamicParam $dynMap $entry $command -skip 0 -bound $PSBoundParameters Write-Debug "Dynamic parameters for entry '$entry': $($result.Keys -join ', ')" return $result } catch { return "ERROR [dynamic]: $($_.Exception.Message) $($_.ScriptStackTrace)" } } process { $mapPath = $map $userMap = try { . $ImportConfigMap -Map $map -Fallback "./.build.map.ps1" | Assert-ConfigMap } catch { if ($_.Exception.Message -match '^map file .* not found$|^No map provided and fallback .* not found$|^map is null and defaultMapFile is not provided$') { $null } else { throw } } $map = Merge-ConfigMapCommands -Map $userMap -Language build if (-not $entry) { $entry = 'list' } $isInternalCommand = Test-ConfigMapInternalCommand -Name $entry -Language build if (!$userMap -and !$isInternalCommand) { Write-Help -invocation $MyInvocation -mapPath "./.build.map.ps1" return } $operationContext = New-ConfigMapOperationContext if (!$isInternalCommand) { $processSettings = Get-ConfigMapSettingsForPath -Map $map -Path $entry -OperationContext $operationContext if ($processSettings.Process -ne 'qbuild') { $resolvedMapPath = Resolve-MapPath -Map $map if (-not $resolvedMapPath) { throw "Cannot delegate process '$($processSettings.Process)' for entry '$entry': the map has no _sourceFile." } $splat = ConvertTo-ConfigMapProcessSplat ` -MapPath $resolvedMapPath ` -Entry $entry ` -BoundParameters $PSBoundParameters ` -RemainingArguments $RemainingArguments Invoke-ConfigMapProcessCli -ProcessName $processSettings.Process -Splat $splat | Out-Null return } } $targets = Get-MapEntries $map $entry -language "build" -OperationContext $operationContext # Validate that the entry exists if (!$targets -or $targets.Count -eq 0) { throw "Entry '$entry' not found. Run 'qbuild list' to see all available commands." } Write-Verbose "running targets: $($targets.Key)" $bound = @{} foreach ($item in $PSBoundParameters.GetEnumerator()) { if ($item.Key -notin @('entry', 'RemainingArguments')) { $bound[$item.Key] = $item.Value } } $passthrough = @($RemainingArguments) | Where-Object { $null -ne $_ } if ($isInternalCommand) { $bound._context = @{ Map = $map UserMap = $userMap MapArgument = $mapPath Invocation = $MyInvocation RemainingArguments = @($passthrough) } } elseif ($passthrough.Count -gt 0) { $bound._context = @{} $bound._context['additionalArgs'] = @($passthrough) } $hookContext = @{ MainCommand = 'qbuild' Entry = $entry Targets = @($targets) Map = $map MapPath = $mapPath Command = $command Bound = $bound RemainingArguments = $passthrough } $hookEntryKey = if ($entry -match '^(.*)\.all$') { $Matches[1] } else { [string]$entry } $hookEntry = $map if ($hookEntryKey) { $resolvedHookEntry = Get-MapEntry $map $hookEntryKey -language "build" -OperationContext $operationContext if ($null -ne $resolvedHookEntry) { $hookEntry = $resolvedHookEntry } } $hookResult = Invoke-WithEntrySettings -Map $map -EntryKey $hookEntryKey -Entry $hookEntry -OperationContext $operationContext -ScriptBlock { Invoke-ConfigMapPluginHooks -HookName 'InvokeQBuildTargets' -Context $hookContext } if ($hookResult.Handled) { return $hookResult.Result } foreach ($_ in $targets) { $targetKey = $_.key $targetEntry = $_.value Write-Verbose "running entry '$targetKey'" if ($command -eq "exec" ` -and $targetEntry -is [System.Collections.IDictionary] ` -and -not (Get-EntryHasExec $targetEntry) ` -and (Test-IsParentEntry $targetEntry)) { Write-ChooseSubcommand -parentKey $targetKey -parentEntry $targetEntry -invocation $MyInvocation -language "build" return } Invoke-WithEntrySettings -Map $map -EntryKey $targetKey -Entry $targetEntry -OperationContext $operationContext -ScriptBlock { Invoke-EntryWrapper -MainCommand 'qbuild' -TargetKey $targetKey -TargetEntry $targetEntry -Command $command -Bound $bound -RemainingArguments $passthrough } } } } Set-Alias -Name "qbuild" -Value "Invoke-QBuild" -Force |