Functions/ConvertTo-WhiskeyContext.ps1


function ConvertTo-WhiskeyContext
{
    <#
    .SYNOPSIS
    Converts an `Whiskey.Context` returned by `ConvertFrom-WhiskeyContext` back into a `Whiskey.Context` object.
     
    .DESCRIPTION
    Some tasks need to run in background jobs and need access to Whiskey's context. This function converts an object returned by `ConvertFrom-WhiskeyContext` back into a `Whiskey.Context` object.
 
        $serializableContext = $TaskContext | ConvertFrom-WhiskeyContext
        $job = Start-Job {
                    Invoke-Command -ScriptBlock {
                                            $VerbosePreference = 'SilentlyContinue';
                                            # Or wherever your project keeps Whiskey relative to your task definition.
                                            Import-Module -Name (Join-Path -Path $using:PSScriptRoot -ChildPath '..\Whiskey' -Resolve -ErrorAction Stop)
                                        }
                    [Whiskey.Context]$context = $using:serializableContext | ConvertTo-WhiskeyContext
                    # Run your task
              }
 
    .EXAMPLE
    $serializedContext | ConvertTo-WhiskeyContext
 
    Demonstrates how to call `ConvertTo-WhiskeyContext`. See the description for a full example.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline)]
        [object]
        # The context to convert. You can pass an existing context via the pipeline.
        $InputObject
    )

    process 
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        try
        {
            function Sync-ObjectProperty
            {
                param(
                    [Parameter(Mandatory=$true)]
                    [object]
                    $Source,

                    [Parameter(Mandatory=$true)]
                    [object]
                    $Destination,

                    [string[]]
                    $ExcludeProperty
                )

                $destinationType = $Destination.GetType()
                $destinationType.DeclaredProperties |
                    Where-Object { $ExcludeProperty -notcontains $_.Name } |
                    Where-Object { $_.GetSetMethod($false) } |
                    Select-Object -ExpandProperty 'Name' |
                    ForEach-Object { 
                        Write-Debug ('{0} {1} -> {2}' -f $_,$Destination.$_,$Source.$_)
                                
                        $propertyType = $destinationType.GetProperty($_).PropertyType
                        if( $propertyType.IsSubclassOf([IO.FileSystemInfo]) )
                        {
                            if( $Source.$_ )
                            {
                                Write-Debug -Message ('{0} {1} = {2}' -f $propertyType.FullName,$_,$Source.$_)
                                $Destination.$_ = New-Object $propertyType.FullName -ArgumentList $Source.$_.FullName
                            }
                        }
                        else
                        {
                            $Destination.$_ = $Source.$_ 
                        }
                    }

                Write-Debug ('Source -eq $null ? {0}' -f ($Source -eq $null))
                if( $Source -ne $null )
                {
                    Write-Debug -Message 'Source'
                    Get-Member -InputObject $Source | Out-String | Write-Debug
                }

                Write-Debug ('Destination -eq $null ? {0}' -f ($Destination -eq $null))
                if( $Destination -ne $null )
                {
                    Write-Debug -Message 'Destination'
                    Get-Member -InputObject $Destination | Out-String | Write-Debug
                }

                Get-Member -InputObject $Destination -MemberType Property |
                    Where-Object { $ExcludeProperty -notcontains $_.Name } |
                    Where-Object {
                        $name = $_.Name
                        if( -not $name )
                        {
                            return
                        }

                        $value = $Destination.$name
                        if( $value -eq $null )
                        {
                            return
                        }

                        Write-Debug ('Destination.{0,-20} -eq $null ? {1}' -f $name,($value -eq $null))
                        Write-Debug (' .{0,-20} is {1}' -f $name,$value.GetType())
                        return (Get-Member -InputObject $value -Name 'Keys') -or ($value -is [Collections.IList])
                    } |
                    ForEach-Object {
                        $propertyName = $_.Name
                        Write-Debug -Message ('{0}.{1} -> {2}.{1}' -f $Source.GetType(),$propertyName,$Destination.GetType())
                        $destinationObject = $Destination.$propertyName
                        $sourceObject = $source.$propertyName
                        if( (Get-Member -InputObject $destinationObject -Name 'Keys') )
                        {
                            $keys = $sourceObject.Keys
                            foreach( $key in $keys )
                            {
                                $value = $sourceObject[$key]
                                Write-Debug (' [{0,-20}] -> {1}' -f $key,$value)
                                $destinationObject[$key] = $sourceObject[$key]
                            }
                        }
                        elseif( $destinationObject -is [Collections.IList] )
                        {
                            $idx = 0
                            foreach( $item in $sourceObject )
                            {
                                Write-Debug(' [{0}] {1}' -f $idx++,$item)
                                $destinationObject.Add($item)
                            }
                        }
                    }
            }

            $buildInfo = New-WhiskeyBuildMetadataObject
            Sync-ObjectProperty -Source $InputObject.BuildMetadata -Destination $buildInfo -Exclude @( 'BuildServer' )
            if( $InputObject.BuildMetadata.BuildServer )
            {
                $buildInfo.BuildServer = $InputObject.BuildMetadata.BuildServer
            }

            $buildVersion = New-WhiskeyVersionObject
            Sync-ObjectProperty -Source $InputObject.Version -Destination $buildVersion -ExcludeProperty @( 'SemVer1', 'SemVer2', 'SemVer2NoBuildMetadata' )
            if( $InputObject.Version )
            {
                if( $InputObject.Version.SemVer1 )
                {
                    $buildVersion.SemVer1 = $InputObject.Version.SemVer1.ToString()
                }

                if( $InputObject.Version.SemVer2 )
                {
                    $buildVersion.SemVer2 = $InputObject.Version.SemVer2.ToString()
                }

                if( $InputObject.Version.SemVer2NoBuildMetadata )
                {
                    $buildVersion.SemVer2NoBuildMetadata = $InputObject.Version.SemVer2NoBuildMetadata.ToString()
                }
            }

            [Whiskey.Context]$context = New-WhiskeyContextObject
            Sync-ObjectProperty -Source $InputObject -Destination $context -ExcludeProperty @( 'BuildMetadata', 'Configuration', 'Version', 'Credentials', 'TaskPaths', 'ApiKeys' )
            if( $context.ConfigurationPath )
            {
                $context.Configuration = Import-WhiskeyYaml -Path $context.ConfigurationPath
            }

            $context.BuildMetadata = $buildInfo
            $context.Version = $buildVersion

            foreach( $credentialID in $InputObject.Credentials.Keys )
            {
                $serializedCredential = $InputObject.Credentials[$credentialID]
                $username = $serializedCredential.UserName
                $password = ConvertTo-SecureString -String $serializedCredential.Password -Key $InputObject.CredentialKey
                [pscredential]$credential = New-Object 'pscredential' $username,$password
                Add-WhiskeyCredential -Context $context -ID $credentialID -Credential $credential
            }

            foreach( $apiKeyID in $InputObject.ApiKeys.Keys )
            {
                $serializedApiKey = $InputObject.ApiKeys[$apiKeyID]
                $apiKey = ConvertTo-SecureString -String $serializedApiKey -Key $InputObject.CredentialKey
                Add-WhiskeyApiKey -Context $context -ID $apiKeyID -Value $apiKey
            }

            foreach( $path in $InputObject.TaskPaths )
            {
                $context.TaskPaths.Add((New-Object -TypeName 'IO.FileInfo' -ArgumentList $path))
            }

            Write-Debug 'Variables'
            $context.Variables | ConvertTo-Json -Depth 50 | Write-Debug
            Write-Debug 'ApiKeys'
            $context.ApiKeys | ConvertTo-Json -Depth 50 | Write-Debug
            Write-Debug 'Credentials'
            $context.Credentials | ConvertTo-Json -Depth 50 | Write-Debug
            Write-Debug 'TaskDefaults'
            $context.TaskDefaults | ConvertTo-Json -Depth 50 | Write-Debug
            Write-Debug 'TaskPaths'
            $context.TaskPaths | ConvertTo-Json | Write-Debug

            return $context
        }
        finally
        {
            # Don't leave the decryption key lying around.
            [Array]::Clear($InputObject.CredentialKey,0,$InputObject.CredentialKey.Length)
        }
    }
}