Private/Logic/RuntimeKernel/Plan/Manifested.DependencyPlanning.ps1
|
function Get-ManifestedCommandPlanFromContext { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject]$Context, [Parameter(Mandatory = $true)] [pscustomobject]$Facts, [bool]$RefreshRequested = $false ) if ($Context.PSObject.Properties['ExecutionModel'] -and $Context.ExecutionModel -eq 'DefinitionBlocks' -and $Context.PSObject.Properties['Definition'] -and $Context.Definition) { if (Get-ManifestedDefinitionBlock -Definition $Context.Definition -SectionName 'facts' -BlockName 'npmCli') { return @(Get-ManifestedNpmCliRuntimePlan -Descriptor $Context -Facts $Facts -RefreshRequested:$RefreshRequested) } if (Get-ManifestedDefinitionBlock -Definition $Context.Definition -SectionName 'facts' -BlockName 'machinePrerequisite') { return @(Get-ManifestedMachinePrerequisiteRuntimePlan -Descriptor $Context -Facts $Facts -RefreshRequested:$RefreshRequested) } return @(Get-ManifestedPortableRuntimePlanFromFacts -Context $Context -Facts $Facts -RefreshRequested:$RefreshRequested) } return @(& $Context.PlanFunction -Descriptor $Context -Facts $Facts -RefreshRequested:$RefreshRequested) } function Test-ManifestedVersionSatisfiesMinimum { [CmdletBinding()] [OutputType([bool])] param( [string]$CurrentVersion, [string]$MinimumVersion ) if ([string]::IsNullOrWhiteSpace($MinimumVersion)) { return $true } if ([string]::IsNullOrWhiteSpace($CurrentVersion)) { return $false } $normalizedCurrent = $CurrentVersion.Trim().TrimStart('v', 'V') $normalizedMinimum = $MinimumVersion.Trim().TrimStart('v', 'V') try { return ([version]$normalizedCurrent -ge [version]$normalizedMinimum) } catch { return ($normalizedCurrent -eq $normalizedMinimum) } } function Resolve-ManifestedCommandDependencies { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject]$Definition, [hashtable]$Visited = @{}, [hashtable]$Visiting = @{}, [System.Collections.Generic.List[object]]$Resolved ) if ($null -eq $Resolved) { $Resolved = New-Object System.Collections.Generic.List[object] } if ($Visiting.ContainsKey($Definition.commandName)) { throw "Dependency cycle detected while resolving '$($Definition.commandName)'." } if ($Visited.ContainsKey($Definition.commandName)) { return $Resolved } $Visiting[$Definition.commandName] = $true foreach ($dependency in @($Definition.dependencies | Where-Object { $_.autoInstall })) { $dependencyDefinition = Get-ManifestedCommandDefinition -RuntimeName $dependency.runtimeName if (-not $dependencyDefinition) { throw "Command definition '$($Definition.commandName)' references unknown dependency runtime '$($dependency.runtimeName)'." } [void](Resolve-ManifestedCommandDependencies -Definition $dependencyDefinition -Visited $Visited -Visiting $Visiting -Resolved $Resolved) $alreadyResolved = $false foreach ($resolvedDependency in @($Resolved)) { if ($resolvedDependency.RuntimeName -eq $dependencyDefinition.runtimeName) { $alreadyResolved = $true break } } if (-not $alreadyResolved) { $Resolved.Add([pscustomobject]@{ RuntimeName = $dependencyDefinition.runtimeName Definition = $dependencyDefinition Dependency = $dependency }) | Out-Null } } $Visiting.Remove($Definition.commandName) | Out-Null $Visited[$Definition.commandName] = $true return $Resolved } function Test-ManifestedRuntimeDependencySatisfied { [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory = $true)] [pscustomobject]$Dependency, [Parameter(Mandatory = $true)] [pscustomobject]$Facts, [Parameter(Mandatory = $true)] [pscustomobject]$OwningDefinition ) if (-not $Facts -or -not $Facts.PSObject.Properties['HasUsableRuntime'] -or -not $Facts.HasUsableRuntime) { return $false } $allowedSources = switch ($Dependency.satisfactionMode) { 'managed-only' { @('Managed') } 'external-only' { @('External') } 'managed-or-external' { @('Managed', 'External') } default { if ($OwningDefinition.policies.allowExternalSatisfaction) { @('Managed', 'External') } else { @('Managed') } } } if ($allowedSources -notcontains $Facts.RuntimeSource) { return $false } return (Test-ManifestedVersionSatisfiesMinimum -CurrentVersion $Facts.CurrentVersion -MinimumVersion $Dependency.minimumVersion) } function Get-ManifestedDependencyPlanFromDefinition { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject]$Context, [Parameter(Mandatory = $true)] [pscustomobject]$Facts, [bool]$RefreshRequested = $false, [hashtable]$FactsCache = @{} ) $steps = New-Object System.Collections.Generic.List[object] if (-not $Context.PSObject.Properties['Definition'] -or -not $Context.Definition) { return $steps.ToArray() } foreach ($resolvedDependency in @(Resolve-ManifestedCommandDependencies -Definition $Context.Definition)) { $dependencyContext = Get-ManifestedRuntimeContext -RuntimeName $resolvedDependency.RuntimeName if (-not $dependencyContext) { throw "Could not resolve a runtime context for dependency '$($resolvedDependency.RuntimeName)'." } $dependencyFacts = Get-ManifestedRuntimeFactsFromContext -Context $dependencyContext -LocalRoot $Facts.LocalRoot -FactsCache $FactsCache if (Test-ManifestedRuntimeDependencySatisfied -Dependency $resolvedDependency.Dependency -Facts $dependencyFacts -OwningDefinition $Context.Definition) { continue } $steps.Add((New-ManifestedPlanStep -Name ('EnsureDependency_' + $resolvedDependency.RuntimeName) -Kind 'Dependency' -Reason $resolvedDependency.Dependency.reason -Action ('Initialize ' + $resolvedDependency.RuntimeName + ' dependency') -Target $dependencyContext.InstallTarget -HandlerFunction 'Invoke-ManifestedDependencyRuntimeStep' -HandlerArguments @{ DependencyRuntimeName = $resolvedDependency.RuntimeName DependencyCommandName = $resolvedDependency.Definition.commandName RefreshParameterName = $resolvedDependency.Definition.refreshSwitchName RefreshRequested = [bool]$RefreshRequested })) | Out-Null } return $steps.ToArray() } function Get-ManifestedPortableRuntimePlanFromFacts { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject]$Context, [Parameter(Mandatory = $true)] [pscustomobject]$Facts, [bool]$RefreshRequested = $false ) $steps = New-Object System.Collections.Generic.List[object] if (-not $Facts.PlatformSupported) { return $steps.ToArray() } foreach ($dependencyStep in @(Get-ManifestedDependencyPlanFromDefinition -Context $Context -Facts $Facts -RefreshRequested:$RefreshRequested)) { $steps.Add($dependencyStep) | Out-Null } if ($Facts.HasRepairableResidue) { $steps.Add((New-ManifestedPlanStep -Name 'RepairRuntimeArtifacts' -Kind 'Repair' -Reason 'Remove invalid or partial managed artifacts before recomputing the runtime.' -Action ('Repair ' + $Context.RuntimeName + ' artifacts') -Target $Context.RepairTarget -HandlerFunction 'Invoke-ManifestedDescriptorRepairStep')) | Out-Null } $needsInstall = $RefreshRequested -or (-not $Facts.HasUsableRuntime) if ($needsInstall) { $steps.Add((New-ManifestedPlanStep -Name 'EnsureInstallArtifact' -Kind 'EnsureArtifact' -Reason 'Acquire and validate the install artifact required for a managed install.' -Action ('Ensure ' + $Context.RuntimeName + ' install artifact') -Target $Context.ArtifactTarget -HandlerFunction 'Invoke-ManifestedDescriptorEnsureArtifactStep')) | Out-Null $steps.Add((New-ManifestedPlanStep -Name 'InstallManagedRuntime' -Kind 'InstallRuntime' -Reason 'Install the managed runtime into the sandbox tools root.' -Action ('Install ' + $Context.RuntimeName) -Target $Context.InstallTarget -HandlerFunction 'Invoke-ManifestedDescriptorInstallStep')) | Out-Null } if ($Context.PSObject.Properties['Definition'] -and $Context.Definition -and (Get-ManifestedDefinitionBlock -Definition $Context.Definition -SectionName 'hooks' -BlockName 'postInstall') -and ($needsInstall -or $Facts.RuntimeSource -eq 'Managed')) { $steps.Add((New-ManifestedPlanStep -Name 'RunPostInstallHooks' -Kind 'PostInstall' -Reason 'Apply runtime-specific post-install normalization and bootstrap hooks.' -Action ('Run ' + $Context.RuntimeName + ' post-install hooks') -Target $Context.InstallTarget -HandlerFunction 'Invoke-ManifestedDescriptorPostInstallStep')) | Out-Null } if ($Context.PSObject.Properties['SupportsEnvironmentSync'] -and $Context.SupportsEnvironmentSync) { $steps.Add((New-ManifestedPlanStep -Name 'SyncCommandEnvironment' -Kind 'SyncEnvironment' -Reason 'Align command resolution with the effective runtime facts.' -Action ('Synchronize ' + $Context.RuntimeName + ' command-line environment') -Target $Context.EnvironmentTarget -HandlerFunction 'Invoke-ManifestedDescriptorEnvironmentSyncStep')) | Out-Null } return $steps.ToArray() } function Get-ManifestedNpmCliRuntimePlan { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject]$Descriptor, [Parameter(Mandatory = $true)] [pscustomobject]$Facts, [bool]$RefreshRequested = $false ) $steps = New-Object System.Collections.Generic.List[object] if (-not $Facts.PlatformSupported) { return $steps.ToArray() } $needsInstall = $RefreshRequested -or (-not $Facts.HasUsableRuntime) if ($needsInstall) { foreach ($dependencyStep in @( if ($Descriptor.PSObject.Properties['Definition'] -and $Descriptor.Definition) { Get-ManifestedDependencyPlanFromDefinition -Context $Descriptor -Facts $Facts -RefreshRequested:$RefreshRequested } )) { $steps.Add($dependencyStep) | Out-Null } } if ($Facts.HasRepairableResidue) { $steps.Add((New-ManifestedPlanStep -Name 'RepairRuntimeArtifacts' -Kind 'Repair' -Reason 'Remove invalid or partial managed artifacts before reinstalling the CLI.' -Action ('Repair ' + $Descriptor.RuntimeName + ' artifacts') -Target $Descriptor.RepairTarget -HandlerFunction 'Invoke-ManifestedDescriptorRepairStep')) | Out-Null } if ($needsInstall) { $steps.Add((New-ManifestedPlanStep -Name 'InstallManagedRuntime' -Kind 'InstallRuntime' -Reason 'Install the managed CLI runtime into the sandbox tools root.' -Action ('Install ' + $Descriptor.RuntimeName) -Target $Descriptor.InstallTarget -HandlerFunction 'Invoke-ManifestedDescriptorInstallStep')) | Out-Null } if ($Descriptor.PSObject.Properties['SupportsEnvironmentSync'] -and $Descriptor.SupportsEnvironmentSync) { $steps.Add((New-ManifestedPlanStep -Name 'SyncCommandEnvironment' -Kind 'SyncEnvironment' -Reason 'Align command resolution with the effective runtime facts.' -Action ('Synchronize ' + $Descriptor.RuntimeName + ' command-line environment') -Target $Descriptor.EnvironmentTarget -HandlerFunction 'Invoke-ManifestedDescriptorEnvironmentSyncStep')) | Out-Null } return $steps.ToArray() } function Get-ManifestedMachinePrerequisiteRuntimePlan { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject]$Descriptor, [Parameter(Mandatory = $true)] [pscustomobject]$Facts, [bool]$RefreshRequested = $false ) $steps = New-Object System.Collections.Generic.List[object] if (-not $Facts.PlatformSupported) { return $steps.ToArray() } if ($Facts.HasRepairableResidue) { $steps.Add((New-ManifestedPlanStep -Name 'RepairInstallerArtifacts' -Kind 'Repair' -Reason 'Remove partial or corrupt prerequisite installer artifacts.' -Action ('Repair ' + $Descriptor.RuntimeName + ' artifacts') -Target $Descriptor.RepairTarget -HandlerFunction 'Invoke-ManifestedDescriptorRepairStep')) | Out-Null } $needsInstall = $RefreshRequested -or (-not $Facts.HasUsableRuntime) if ($needsInstall) { $steps.Add((New-ManifestedPlanStep -Name 'EnsureInstallArtifact' -Kind 'EnsureArtifact' -Reason 'Acquire and validate the prerequisite installer.' -Action ('Ensure ' + $Descriptor.RuntimeName + ' installer') -Target $Descriptor.ArtifactTarget -HandlerFunction 'Invoke-ManifestedDescriptorEnsureArtifactStep')) | Out-Null $steps.Add((New-ManifestedPlanStep -Name 'InstallRuntime' -Kind 'InstallRuntime' -Reason 'Install or repair the machine prerequisite.' -Action ('Install ' + $Descriptor.RuntimeName) -Target $Descriptor.InstallTarget -RequiresElevation:$Descriptor.InstallRequiresElevation -HandlerFunction 'Invoke-ManifestedDescriptorInstallStep')) | Out-Null } return $steps.ToArray() } |