Private/Logic/Eigenverft.Manifested.Sandbox.RuntimePack.Node.ps1

<#
    Eigenverft.Manifested.Sandbox.RuntimePack.Node
#>


function Get-ManifestedNodeRuntimeDescriptor {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$CommandName
    )

    $descriptor = Get-ManifestedRuntimeDescriptor -CommandName $CommandName
    if (-not $descriptor) {
        throw "Could not resolve runtime descriptor for '$CommandName'."
    }
    if ($descriptor.RuntimeFamily -ne 'Node') {
        throw "Runtime '$CommandName' does not belong to the Node runtime family."
    }

    return $descriptor
}

function Get-ManifestedNodePlannedActions {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject]$Descriptor,

        [Parameter(Mandatory = $true)]
        [bool]$NeedsRepair,

        [Parameter(Mandatory = $true)]
        [bool]$NeedsInstall,

        [Parameter(Mandatory = $true)]
        [bool]$NeedsAcquire,

        [pscustomobject]$RuntimeState
    )

    $plannedActions = New-Object System.Collections.Generic.List[string]

    if ($NeedsRepair) {
        $plannedActions.Add($Descriptor.RepairFunctionName) | Out-Null
    }
    if ($NeedsInstall -and $NeedsAcquire) {
        $plannedActions.Add($Descriptor.SavePackageFunctionName) | Out-Null
    }
    if ($NeedsInstall) {
        $plannedActions.Add($Descriptor.TestPackageFunctionName) | Out-Null
        $plannedActions.Add($Descriptor.InstallFunctionName) | Out-Null
    }
    if ($NeedsInstall -or ($RuntimeState -and $RuntimeState.PSObject.Properties['RuntimeSource'] -and ($RuntimeState.RuntimeSource -eq 'Managed'))) {
        $plannedActions.Add('Sync-ManifestedNpmProxyConfiguration') | Out-Null
    }

    $plannedActions.Add('Sync-ManifestedCommandLineEnvironment') | Out-Null
    return @($plannedActions)
}

function Get-ManifestedNodePersistedDetails {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject]$Descriptor,

        [pscustomobject]$FinalState
    )

    return @{
        Version        = if ($FinalState -and $FinalState.PSObject.Properties['CurrentVersion']) { $FinalState.CurrentVersion } else { $null }
        PackagePath    = if ($FinalState -and $FinalState.PSObject.Properties['PackagePath']) { $FinalState.PackagePath } else { $null }
        RuntimeHome    = if ($FinalState -and $FinalState.PSObject.Properties['RuntimeHome']) { $FinalState.RuntimeHome } else { $null }
        RuntimeSource  = if ($FinalState -and $FinalState.PSObject.Properties['RuntimeSource']) { $FinalState.RuntimeSource } else { $null }
        ExecutablePath = if ($FinalState -and $FinalState.PSObject.Properties['ExecutablePath']) { $FinalState.ExecutablePath } else { $null }
    }
}

function Get-ManifestedNodeManagedFinalizerStatus {
    [CmdletBinding()]
    param(
        [pscustomobject]$RuntimeState,

        [pscustomobject]$RuntimeTest
    )

    $emptyStatus = [pscustomobject]@{
        Applicable = $false
        ActionName = 'Sync-ManifestedNpmProxyConfiguration'
        Target     = $null
        NpmCmd     = $null
        Status     = $null
    }

    if (-not $RuntimeState -or -not $RuntimeState.PSObject.Properties['RuntimeSource'] -or $RuntimeState.RuntimeSource -ne 'Managed') {
        return $emptyStatus
    }

    $npmCmd = $null
    if ($RuntimeTest -and $RuntimeTest.PSObject.Properties['NpmCmd']) {
        $npmCmd = $RuntimeTest.NpmCmd
    }
    elseif ($RuntimeState.PSObject.Properties['Runtime'] -and $RuntimeState.Runtime -and $RuntimeState.Runtime.PSObject.Properties['NpmCmd']) {
        $npmCmd = $RuntimeState.Runtime.NpmCmd
    }
    elseif ($RuntimeState.PSObject.Properties['RuntimeHome'] -and -not [string]::IsNullOrWhiteSpace($RuntimeState.RuntimeHome)) {
        $npmCmd = Join-Path $RuntimeState.RuntimeHome 'npm.cmd'
    }

    if ([string]::IsNullOrWhiteSpace($npmCmd) -or -not (Test-Path -LiteralPath $npmCmd)) {
        return $emptyStatus
    }

    $status = Get-ManifestedNpmProxyConfigurationStatus -NpmCmd $npmCmd -LocalRoot $RuntimeState.LocalRoot
    return [pscustomobject]@{
        Applicable = $true
        ActionName = 'Sync-ManifestedNpmProxyConfiguration'
        Target     = if ($status -and $status.PSObject.Properties['GlobalConfigPath']) { $status.GlobalConfigPath } else { $null }
        NpmCmd     = $npmCmd
        Status     = $status
    }
}

function Invoke-ManifestedNodeManagedFinalization {
    [CmdletBinding()]
    param(
        [pscustomobject]$Status,

        [Parameter(Mandatory = $true)]
        [pscustomobject]$RuntimeState,

        [pscustomobject]$RuntimeTest
    )

    if (-not $Status) {
        $Status = Get-ManifestedNodeManagedFinalizerStatus -RuntimeState $RuntimeState -RuntimeTest $RuntimeTest
    }

    if (-not $Status.Applicable) {
        return if ($Status.PSObject.Properties['Status']) { $Status.Status } else { $null }
    }

    if ($Status.Status -and $Status.Status.PSObject.Properties['Action'] -and ($Status.Status.Action -eq 'NeedsManagedGlobalProxy')) {
        return (Sync-ManifestedNpmProxyConfiguration -NpmCmd $Status.NpmCmd -Status $Status.Status -LocalRoot $RuntimeState.LocalRoot)
    }

    return $Status.Status
}

function Invoke-ManifestedNodeRuntimeInitialization {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true)]
        [string]$CommandName,

        [switch]$Refresh
    )

    $descriptor = Get-ManifestedNodeRuntimeDescriptor -CommandName $CommandName
    $LocalRoot = (Get-ManifestedLayout).LocalRoot
    $selfElevationContext = Get-ManifestedSelfElevationContext

    $actionsTaken = New-Object System.Collections.Generic.List[string]
    $repairResult = $null
    $packageInfo = $null
    $packageTest = $null
    $installResult = $null
    $npmProxyConfiguration = $null
    $commandEnvironment = $null

    $initialState = & $descriptor.StateFunctionName -LocalRoot $LocalRoot
    $state = $initialState
    $elevationPlan = Get-ManifestedCommandElevationPlan -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -SkipSelfElevation:$selfElevationContext.SkipSelfElevation -WasSelfElevated:$selfElevationContext.WasSelfElevated -WhatIfMode:$WhatIfPreference

    if ($state.Status -eq 'Blocked') {
        $commandEnvironment = Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state
        $result = New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('None') -PlannedActions @() -RestartRequired:$false -AdditionalProperties ([ordered]@{
            Package               = $null
            PackageTest           = $null
            RuntimeTest           = $null
            RepairResult          = $null
            InstallResult         = $null
            NpmProxyConfiguration = $null
            CommandEnvironment    = $commandEnvironment
            Elevation             = $elevationPlan
        })

        return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -Result $result -LocalRoot $LocalRoot -Details (& $descriptor.PersistedDetailsFunctionName -Descriptor $descriptor -FinalState $state) -PersistState:(-not $WhatIfPreference))
    }

    $needsRepair = $state.Status -in @('Partial', 'NeedsRepair')
    $needsInstall = $Refresh -or -not $state.RuntimeHome
    $needsAcquire = $Refresh -or (-not $state.PackagePath)
    $plannedActions = @(Get-ManifestedNodePlannedActions -Descriptor $descriptor -NeedsRepair:$needsRepair -NeedsInstall:$needsInstall -NeedsAcquire:$needsAcquire -RuntimeState $state)
    $elevationPlan = Get-ManifestedCommandElevationPlan -CommandName $descriptor.InitializeCommandName -PlannedActions @($plannedActions) -LocalRoot $LocalRoot -SkipSelfElevation:$selfElevationContext.SkipSelfElevation -WasSelfElevated:$selfElevationContext.WasSelfElevated -WhatIfMode:$WhatIfPreference

    if ($needsRepair) {
        if (-not $PSCmdlet.ShouldProcess($state.Layout.($descriptor.ToolsRootPropertyName), ('Repair {0} runtime state' -f $descriptor.DisplayName))) {
            return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('WhatIf') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                    Package               = $null
                    PackageTest           = $null
                    RuntimeTest           = $state.Runtime
                    RepairResult          = $null
                    InstallResult         = $null
                    NpmProxyConfiguration = $null
                    CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                    Elevation             = $elevationPlan
                })
            ))
        }

        $repairResult = & $descriptor.RepairFunctionName -State $state -Flavor $state.Flavor -LocalRoot $state.LocalRoot
        if ($repairResult.Action -eq 'Repaired') {
            $actionsTaken.Add($descriptor.RepairFunctionName) | Out-Null
        }

        $state = & $descriptor.StateFunctionName -Flavor $state.Flavor -LocalRoot $state.LocalRoot
        $needsInstall = $Refresh -or -not $state.RuntimeHome
        $needsAcquire = $Refresh -or (-not $state.PackagePath)
    }

    if ($needsInstall) {
        if ($needsAcquire) {
            if (-not $PSCmdlet.ShouldProcess($state.Layout.($descriptor.CacheRootPropertyName), ('Acquire {0} runtime package' -f $descriptor.DisplayName))) {
                return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                    New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('WhatIf') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                        Package               = $null
                        PackageTest           = $null
                        RuntimeTest           = $state.Runtime
                        RepairResult          = $repairResult
                        InstallResult         = $null
                        NpmProxyConfiguration = $null
                        CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                        Elevation             = $elevationPlan
                    })
                ))
            }

            $saveParameters = @{
                LocalRoot = $state.LocalRoot
                Flavor    = $state.Flavor
            }
            $saveParameters[$descriptor.RefreshParameterName] = [bool]$Refresh
            $packageInfo = & $descriptor.SavePackageFunctionName @saveParameters
            if ($packageInfo.Action -eq 'Downloaded') {
                $actionsTaken.Add($descriptor.SavePackageFunctionName) | Out-Null
            }
        }
        else {
            $packageInfo = $state.Package
        }

        $packageTest = & $descriptor.TestPackageFunctionName -PackageInfo $packageInfo
        if ($packageTest.Status -eq 'CorruptCache') {
            if (-not $PSCmdlet.ShouldProcess($packageInfo.Path, ('Repair corrupt {0} runtime package' -f $descriptor.DisplayName))) {
                return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                    New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('WhatIf') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                        Package               = $packageInfo
                        PackageTest           = $packageTest
                        RuntimeTest           = $state.Runtime
                        RepairResult          = $repairResult
                        InstallResult         = $null
                        NpmProxyConfiguration = $null
                        CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                        Elevation             = $elevationPlan
                    })
                ))
            }

            $repairResult = & $descriptor.RepairFunctionName -State $state -CorruptPackagePaths @($packageInfo.Path) -Flavor $state.Flavor -LocalRoot $state.LocalRoot
            if ($repairResult.Action -eq 'Repaired') {
                $actionsTaken.Add($descriptor.RepairFunctionName) | Out-Null
            }

            $refreshSaveParameters = @{
                LocalRoot = $state.LocalRoot
                Flavor    = $state.Flavor
            }
            $refreshSaveParameters[$descriptor.RefreshParameterName] = $true
            $packageInfo = & $descriptor.SavePackageFunctionName @refreshSaveParameters
            if ($packageInfo.Action -eq 'Downloaded') {
                $actionsTaken.Add($descriptor.SavePackageFunctionName) | Out-Null
            }

            $packageTest = & $descriptor.TestPackageFunctionName -PackageInfo $packageInfo
        }

        if ($packageTest.Status -ne 'Ready') {
            throw ('{0} runtime package validation failed with status {1}.' -f $descriptor.DisplayName, $packageTest.Status)
        }

        $commandParameters = @{}
        if ($Refresh) {
            $commandParameters[$descriptor.RefreshParameterName] = $true
        }
        if ($PSBoundParameters.ContainsKey('WhatIf')) {
            $commandParameters['WhatIf'] = $true
        }

        $elevatedResult = Invoke-ManifestedElevatedCommand -ElevationPlan $elevationPlan -CommandName $descriptor.InitializeCommandName -CommandParameters $commandParameters
        if ($null -ne $elevatedResult) {
            return $elevatedResult
        }

        if (-not $PSCmdlet.ShouldProcess($state.Layout.($descriptor.ToolsRootPropertyName), ('Install {0} runtime' -f $descriptor.DisplayName))) {
            return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('WhatIf') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                    Package               = $packageInfo
                    PackageTest           = $packageTest
                    RuntimeTest           = $state.Runtime
                    RepairResult          = $repairResult
                    InstallResult         = $null
                    NpmProxyConfiguration = $null
                    CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                    Elevation             = $elevationPlan
                })
            ))
        }

        $installResult = & $descriptor.InstallFunctionName -PackageInfo $packageInfo -Flavor $state.Flavor -LocalRoot $state.LocalRoot
        if ($installResult.Action -eq 'Installed') {
            $actionsTaken.Add($descriptor.InstallFunctionName) | Out-Null
        }
    }

    $finalState = & $descriptor.StateFunctionName -Flavor $state.Flavor -LocalRoot $state.LocalRoot
    $runtimeTest = if ($finalState.RuntimeHome) {
        $runtimeTestParameters = if ($descriptor.PSObject.Properties['RuntimeTestParameterResolver'] -and $descriptor.RuntimeTestParameterResolver) {
            & $descriptor.RuntimeTestParameterResolver $finalState
        }
        else {
            @{}
        }

        & $descriptor.RuntimeTestFunctionName @runtimeTestParameters
    }
    else {
        [pscustomobject]@{
            Status   = 'Missing'
            IsReady  = $false
            NodeHome = $null
            NodeExe  = $null
            NpmCmd   = $null
        }
    }

    $nodeManagedFinalizerStatus = Get-ManifestedNodeManagedFinalizerStatus -RuntimeState $finalState -RuntimeTest $runtimeTest
    $npmProxyConfiguration = if ($nodeManagedFinalizerStatus.Status) { $nodeManagedFinalizerStatus.Status } else { $null }

    if ($nodeManagedFinalizerStatus.Applicable -and $nodeManagedFinalizerStatus.Status -and $nodeManagedFinalizerStatus.Status.PSObject.Properties['Action'] -and ($nodeManagedFinalizerStatus.Status.Action -eq 'NeedsManagedGlobalProxy')) {
        if (-not $PSCmdlet.ShouldProcess($nodeManagedFinalizerStatus.Target, 'Configure managed npm proxy settings')) {
            return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                New-ManifestedRuntimeResult -LocalRoot $finalState.LocalRoot -Layout $finalState.Layout -InitialState $initialState -FinalState $finalState -ActionTaken @('WhatIf') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                    Package               = $packageInfo
                    PackageTest           = $packageTest
                    RuntimeTest           = $runtimeTest
                    RepairResult          = $repairResult
                    InstallResult         = $installResult
                    NpmProxyConfiguration = $npmProxyConfiguration
                    CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $finalState)
                    Elevation             = $elevationPlan
                })
            ))
        }

        $npmProxyConfiguration = & $descriptor.ManagedFinalizerFunctionName -Status $nodeManagedFinalizerStatus -RuntimeState $finalState -RuntimeTest $runtimeTest
        if ($npmProxyConfiguration -and $npmProxyConfiguration.PSObject.Properties['Action'] -and ($npmProxyConfiguration.Action -eq 'ConfiguredManagedGlobalProxy')) {
            $actionsTaken.Add('Sync-ManifestedNpmProxyConfiguration') | Out-Null
        }
    }

    $commandEnvironmentSync = Invoke-ManifestedRuntimeCommandEnvironmentSync -Cmdlet $PSCmdlet -CommandName $descriptor.InitializeCommandName -DisplayName $descriptor.DisplayName -RuntimeState $finalState -ActionsTaken $actionsTaken -UseShouldProcess:$true
    $commandEnvironment = $commandEnvironmentSync.CommandEnvironment
    if ($commandEnvironmentSync.StopProcessing) {
        return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
            New-ManifestedRuntimeResult -LocalRoot $finalState.LocalRoot -Layout $finalState.Layout -InitialState $initialState -FinalState $finalState -ActionTaken @('WhatIf') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                Package               = $packageInfo
                PackageTest           = $packageTest
                RuntimeTest           = $runtimeTest
                RepairResult          = $repairResult
                InstallResult         = $installResult
                NpmProxyConfiguration = $npmProxyConfiguration
                CommandEnvironment    = $commandEnvironment
                Elevation             = $elevationPlan
            })
        ))
    }

    $actionTaken = if ($actionsTaken.Count -gt 0) { @($actionsTaken) } else { @('None') }
    $result = New-ManifestedRuntimeResult -LocalRoot $finalState.LocalRoot -Layout $finalState.Layout -InitialState $initialState -FinalState $finalState -ActionTaken $actionTaken -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
        Package               = $packageInfo
        PackageTest           = $packageTest
        RuntimeTest           = $runtimeTest
        RepairResult          = $repairResult
        InstallResult         = $installResult
        NpmProxyConfiguration = $npmProxyConfiguration
        CommandEnvironment    = $commandEnvironment
        Elevation             = $elevationPlan
    })

    return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -Result $result -LocalRoot $LocalRoot -Details (& $descriptor.PersistedDetailsFunctionName -Descriptor $descriptor -FinalState $finalState) -PersistState:(-not $WhatIfPreference))
}