Private/DownloadNuGetPackage.ps1

<#
     
    .SYNOPSIS
        The DownloadNuGetPackage function download and unzips the specified NuGetPackage using the v3 NuGet API.
        It also indicated which assembly file (.dll) you should probably use for the PowerShell version (Windows or Core)
        you are using.
     
    .DESCRIPTION
        See .SYNOPSIS
 
    .PARAMETER AssemblyName
        This parameter is MANDATORY.
 
        TODO
 
    .PARAMETER NuGetPkgDownloadDirectory
        This parameter is OPTIONAL.
 
        TODO
 
    .PARAMETER AllowPreRelease
        This parameter is OPTIONAL.
 
        TODO
 
    .PARAMETER Silent
        This parameter is OPTIONAL.
 
        TODO
 
    .EXAMPLE
        # Open an elevated PowerShell Session, import the module, and -
 
        PS C:\Users\zeroadmin> DownloadNuGetPackage -AssemblyName Newtonsoft.Json -NuGetPkgDownloadDirectory "$HOME\Downloads" -Silent
     
#>

function DownloadNuGetPackage {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [string]$AssemblyName,

        [Parameter(Mandatory=$False)]
        [string]$NuGetPkgDownloadDirectory,

        [Parameter(Mandatory=$False)]
        [switch]$AllowPreRelease,

        [Parameter(Mandatory=$False)]
        [switch]$Silent
    )

    ##### BEGIN Helper Native Functions #####
    
    function Get-NativePath {
        [CmdletBinding()]
        Param( 
            [Parameter(Mandatory=$True)]
            [string[]]$PathAsStringArray
        )
    
        $PathAsStringArray = foreach ($pathPart in $PathAsStringArray) {
            $SplitAttempt = $pathPart -split [regex]::Escape([IO.Path]::DirectorySeparatorChar)
            
            if ($SplitAttempt.Count -gt 1) {
                foreach ($obj in $SplitAttempt) {
                    $obj
                }
            }
            else {
                $pathPart
            }
        }
        $PathAsStringArray = $PathAsStringArray -join [IO.Path]::DirectorySeparatorChar
    
        $PathAsStringArray
    
    }
    
    ##### END Helper Native Functions #####

    ##### BEGIN Parameter Validation #####

    if ($PSVersionTable.Platform -ne $null -and $PSVersionTable.Platform -ne "Win32NT" -and !$NuGetPkgDownloadDirectory) {
        Write-Error "On this OS Platform (i.e. $($PSVersionTable.Platform)), the -NuGetPkgDownloadDirectory parameter is required! Halting!"
        $global:FunctionResult = "1"
        return
    }

    <#
    if ($PSVersionTable.PSEdition -eq "Desktop" -and $NuGetPkgDownloadDirectory) {
        Write-Error "The -NuGetPkgDownloadPath parameter is only meant to be used with PowerShell Core! Halting!"
        $global:FunctionResult = "1"
        return
    }
    #>

    
    ##### END Parameter Validation #####


    ##### BEGIN Variable/Parameter Transforms and PreRun Prep #####

    $s = [IO.Path]::DirectorySeparatorChar

    if ($($PSVersionTable.Platform -ne $null -and $PSVersionTable.Platform -ne "Win32NT") -or $NuGetPkgDownloadDirectory) {
        #$NuGetPackageUri = "https://www.nuget.org/api/v2/package/$AssemblyName"
        #$NuGetPackageUri = "https://api.nuget.org/v3-flatcontainer/{id-lower}/{version-lower}/{id-lower}.{version-lower}.nupkg"
        if ($AllowPreRelease) {
            $SearchNuGetPackageUri = "https://api-v2v3search-0.nuget.org/query?q=$AssemblyName&prerelease=true"
        }
        else {
            $SearchNuGetPackageUri = "https://api-v2v3search-0.nuget.org/query?q=$AssemblyName&prerelease=false"
        }
        $VersionCheckPrep = $($(Invoke-RestMethod -Uri $SearchNuGetPackageUri).data | Where-Object {$_.id -eq $AssemblyName}).versions
        $LatestVersion = $VersionCheckPrep[-1].Version
        $LowercaseAssemblyName = $AssemblyName.ToLowerInvariant()
        $NuGetPackageUri = "https://api.nuget.org/v3-flatcontainer/$LowercaseAssemblyName/$LatestVersion/$LowercaseAssemblyName.$LatestVersion.nupkg"

        $OutFileBaseName = "$LowercaseAssemblyName.$LatestVersion.zip"
        $DllFileName = $OutFileBaseName -replace "zip","dll"

        if ($NuGetPkgDownloadDirectory) {
            $NuGetPkgDownloadPath = Join-Path $NuGetPkgDownloadDirectory $OutFileBaseName
            $NuGetPkgExtractionDirectory = Join-Path $NuGetPkgDownloadDirectory $AssemblyName
            if (!$(Test-Path $NuGetPkgDownloadDirectory)) {
                $null = New-Item -ItemType Directory -Path $NuGetPkgDownloadDirectory -Force
            }
            if (!$(Test-Path $NuGetPkgExtractionDirectory)) {
                $null = New-Item -ItemType Directory -Path $NuGetPkgExtractionDirectory -Force
            }
        }

        <#
        $TestPath = $NuGetPkgDownloadDirectory
        $BrokenDir = while (-not (Test-Path $TestPath)) {
            $CurrentPath = $TestPath
            $TestPath = Split-Path $TestPath
            if (Test-Path $TestPath) {$CurrentPath}
        }
 
        if ([String]::IsNullOrWhitespace([System.IO.Path]::GetExtension($NuGetPkgDownloadDirectory))) {
            # Assume it's a directory
            if ($BrokenDir) {
                if ($BrokenDir -eq $NuGetPkgDownloadDirectory) {
                    $null = New-Item -ItemType Directory -Path $BrokenDir -Force
                }
                else {
                    Write-Error "The path $TestPath was not found! Halting!"
                    $global:FunctionResult = "1"
                    return
                }
 
                $NuGetPkgDownloadPath = Get-NativePath @($BrokenDir, $OutFileBaseName)
            }
            else {
                if ($(Get-ChildItem $NuGetPkgDownloadDirectory).Count -ne 0) {
                    $NewDir = Get-NativePath @($NuGetPkgDownloadDirectory, [System.IO.Path]::GetFileNameWithoutExtension($OutFileBaseName))
                    $null = New-Item -ItemType Directory -Path $NewDir -Force
                }
                $NuGetPkgDownloadPath = Get-NativePath @($NewDir, $OutFileBaseName)
            }
        }
        else {
            # Assume it's a file
            $OutFileBaseName = $NuGetPkgDownloadDirectory | Split-Path -Leaf
            $extension = [System.IO.Path]::GetExtension($OutFileBaseName)
            if ($extension -ne ".zip") {
                $OutFileBaseName = $OutFileBaseName -replace "$extension",".zip"
            }
 
            if ($BrokenDir) {
                Write-Host "BrokenDir is $BrokenDir"
                if ($BrokenDir -eq $($NuGetPkgDownloadDirectory | Split-Path -Parent)) {
                    $null = New-Item -ItemType Directory -Path $BrokenDir -Force
                }
                else {
                    Write-Error "The path $TestPath was not found! Halting!"
                    $global:FunctionResult = "1"
                    return
                }
 
                $NuGetPkgDownloadPath = Get-NativePath @($BrokenDir, $OutFileBaseName)
            }
            else {
                if ($(Get-ChildItem $($NuGetPkgDownloadDirectory | Split-Path -Parent)).Count -ne 0) {
                    $NewDir = Get-NativePath @($($NuGetPkgDownloadDirectory | Split-Path -Parent), [System.IO.Path]::GetFileNameWithoutExtension($OutFileBaseName))
                    $null = New-Item -ItemType Directory -Path $NewDir -Force
                }
                 
                $NuGetPkgDownloadPath = Get-NativePath @($NewDir, $OutFileBaseName)
            }
        }
        #>


        #$NuGetPkgExtractionDirectory = $NuGetPkgDownloadPath | Split-Path -Parent
    }
    if ($($PSVersionTable.PSEdition -eq "Desktop" -or $PSVersionTable.Platform -eq "Win32NT") -and !$NuGetPkgDownloadDirectory) {
        $NuGetConfigContent = Get-Content $(Get-NativePath @($env:AppData, "NuGet", "nuget.config"))
        $NuGetRepoPathCheck = $NuGetConfigContent | Select-String -Pattern '<add key="repositoryPath" value=' -ErrorAction SilentlyContinue
        if ($NuGetRepoPathCheck -ne $null) {
            $NuGetPackagesPath = $($($NuGetRepoPathCheck.Line.Trim() -split 'value=')[-1] -split ' ')[0] -replace '"',''
        }
        else {
            $NuGetPackagesPath = Get-NativePath @($HOME, ".nuget", "packages")
        }

        if (!$(Test-Path $NuGetPackagesPath)) {
            $null = New-Item -ItemType Directory -Path $NuGetPackagesPath -Force
        }

        $NuGetPkgExtractionDirectory = Get-NativePath @($NuGetPackagesPath, $AssemblyName)
    }

    if ($PSVersionTable.PSEdition -eq "Core") {
        $PossibleSubDirs = @(
            [pscustomobject]@{
                Preference      = 3
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.3"))
            }
            [pscustomobject]@{
                Preference      = 3
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.6"))
            }
            [pscustomobject]@{
                Preference      = 1
                SubDirectory    = $(Get-NativePath @("lib", "netstandard2.0"))
            }
            [pscustomobject]@{
                Preference      = 2
                SubDirectory    = $(Get-NativePath @("lib", "netcoreapp2.0"))
            }
        )
    }
    else {
        $PossibleSubDirs = @(
            [pscustomobject]@{
                Preference      = 8
                SubDirectory    = $(Get-NativePath @("lib", "net40"))
            }
            [pscustomobject]@{
                Preference      = 7
                SubDirectory    = $(Get-NativePath @("lib", "net45"))
            }
            [pscustomobject]@{
                Preference      = 6
                SubDirectory    = $(Get-NativePath @("lib", "net451"))
            }
            [pscustomobject]@{
                Preference      = 5
                SubDirectory    = $(Get-NativePath @("lib", "net46"))
            }
            [pscustomobject]@{
                Preference      = 4
                SubDirectory    = $(Get-NativePath @("lib", "net461"))
            }
            [pscustomobject]@{
                Preference      = 3
                SubDirectory    = $(Get-NativePath @("lib", "net462"))
            }
            [pscustomobject]@{
                Preference      = 2
                SubDirectory    = $(Get-NativePath @("lib", "net47"))
            }
            [pscustomobject]@{
                Preference      = 1
                SubDirectory    = $(Get-NativePath @("lib", "net471"))
            }
            [pscustomobject]@{
                Preference      = 15
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.0"))
            }
            [pscustomobject]@{
                Preference      = 14
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.1"))
            }
            [pscustomobject]@{
                Preference      = 13
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.2"))
            }
            [pscustomobject]@{
                Preference      = 12
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.3"))
            }
            [pscustomobject]@{
                Preference      = 11
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.4"))
            }
            [pscustomobject]@{
                Preference      = 10
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.5"))
            }
            [pscustomobject]@{
                Preference      = 9
                SubDirectory    = $(Get-NativePath @("lib", "netstandard1.6"))
            }
            [pscustomobject]@{
                Preference      = 16
                SubDirectory    = $(Get-NativePath @("lib", "netstandard2.0"))
            }
            [pscustomobject]@{
                Preference      = 17
                SubDirectory    = $(Get-NativePath @("lib", "netcoreapp2.0"))
            }
        )
    }

    ##### END Variable/Parameter Transforms and PreRun Prep #####

    
    ##### BEGIN Main Body #####
    if ($($PSVersionTable.PSEdition -eq "Desktop" -or $PSVersionTable.Platform -eq "Win32NT") -and !$NuGetPkgDownloadDirectory) {
        #$null = Update-PackageManagement -InstallNuGetCmdLine

        if (!$(Get-Command nuget.exe -ErrorAction SilentlyContinue)) {
            $NugetPath = Join-Path $($NuGetPackagesPath | Split-Path -Parent) nuget.exe
            if(!$(Test-Path $NugetPath)) {
                Invoke-WebRequest -uri 'https://dist.nuget.org/win-x86-commandline/latest/nuget.exe' -OutFile $NugetPath
            }
            $NugetDir = $NugetPath | Split-Path -Parent

            # Update PowerShell $env:Path
            [System.Collections.Arraylist][array]$CurrentEnvPathArray = $env:Path -split ';' | Where-Object {![System.String]::IsNullOrWhiteSpace($_)} | Sort-Object | Get-Unique
            if ($CurrentEnvPathArray -notcontains $NugetDir) {
                $CurrentEnvPathArray.Insert(0,$NugetDir)
                $env:Path = $CurrentEnvPathArray -join ';'
            }
            
            # Update SYSTEM Path
            $RegistrySystemPath = 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment'
            $CurrentSystemPath = $(Get-ItemProperty -Path $RegistrySystemPath -Name PATH).Path
            [System.Collections.Arraylist][array]$CurrentSystemPathArray = $CurrentSystemPath -split ';' | Where-Object {![System.String]::IsNullOrWhiteSpace($_)} | Sort-Object | Get-Unique
            if ($CurrentSystemPathArray -notcontains $NugetDir) {
                $CurrentSystemPathArray.Insert(0,$NugetDir)
                $UpdatedSystemPath = $CurrentSystemPathArray -join ';'
                Set-ItemProperty -Path $RegistrySystemPath -Name PATH -Value $UpdatedSystemPath
            }   
        }

        try {
            $ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo
            #$ProcessInfo.WorkingDirectory = $NuGetPackagesPath
            $ProcessInfo.FileName = $(Get-Command nuget).Source
            $ProcessInfo.RedirectStandardError = $true
            $ProcessInfo.RedirectStandardOutput = $true
            $ProcessInfo.UseShellExecute = $false
            if ($AllowPreRelease) {
                $ProcessInfo.Arguments = "install $AssemblyName -PreRelease"
            }
            else {
                $ProcessInfo.Arguments = "install $AssemblyName"
            }
            $Process = New-Object System.Diagnostics.Process
            $Process.StartInfo = $ProcessInfo
            $Process.Start() | Out-Null
            $stdout = $($Process.StandardOutput.ReadToEnd()).Trim()
            $stderr = $($Process.StandardError.ReadToEnd()).Trim()
            $AllOutput = $stdout + $stderr
            $AllOutput = $AllOutput -split "`n"

            if ($stderr -match "Unable to find package") {
                throw
            }

            $NuGetPkgExtractionDirectory = $(Get-ChildItem -Path $NuGetPackagesPath -Directory | Where-Object {$_.Name -eq $AssemblyName} | Sort-Object -Property CreationTime)[-1].FullName
        }
        catch {
            Write-Error $_
            Write-Error "NuGet.exe was unable to find a package called $AssemblyName! Halting!"
            $global:FunctionResult = "1"
            return
        }
    }
    if ($($PSVersionTable.Platform -ne $null -and $PSVersionTable.Platform -ne "Win32NT") -or $NuGetPkgDownloadDirectory) {
        try {
            # Download the NuGet Package
            if (!$Silent) {
                Write-Host "Downloading $AssemblyName NuGet Package to $NuGetPkgDownloadPath ..."
            }
            Invoke-WebRequest -Uri $NuGetPackageUri -OutFile $NuGetPkgDownloadPath
            if (!$Silent) {
                Write-Host "NuGet Package has been downloaded to $NuGetPkgDownloadPath"
            }
        }
        catch {
            Write-Error "Unable to find $AssemblyName via the NuGet API! Halting!"
            $global:FunctionResult = "1"
            return
        }

        # Step through possble Zip File SubDirs and get the most highest available compatible version of the Assembly
        try {
            if (!$Silent) {
                Write-Host "Attempting to extract NuGet zip file $NuGetPkgDownloadPath to $NuGetPkgExtractionDirectory ..."
            }
            if ($(Get-ChildItem $NuGetPkgExtractionDirectory).Count -gt 1) {
                foreach ($item in $(Get-ChildItem $NuGetPkgExtractionDirectory)) {
                    if ($item.Extension -ne ".zip") {
                        $item | Remove-Item -Recurse -Force
                    }
                }
            }
            Expand-Archive -Path $NuGetPkgDownloadPath -DestinationPath $NuGetPkgExtractionDirectory
            #Unzip-File -PathToZip $NuGetPkgDownloadPath -TargetDir $NuGetPkgExtractionDirectory
            if (!$Silent) {
                Write-Host "NuGet Package is available here: $NuGetPkgExtractionDirectory"
            }
        }
        catch {
            Write-Warning "The Unzip-File function failed with the following error:"
            Write-Error $$_
            $global:FunctionResult = "1"
            return
        }
    }

    [System.Collections.ArrayList]$NuGetPackageActualSubDirs = @()
    $(Get-ChildItem -Recurse $NuGetPkgExtractionDirectory -File -Filter "*.dll").DirectoryName | foreach {
        $null = $NuGetPackageActualSubDirs.Add($_)
    }
    
    [System.Collections.ArrayList]$FoundSubDirsPSObjects = @()
    foreach ($pdir in $PossibleSubDirs) {
        foreach ($adir in $NuGetPackageActualSubDirs) {
            $IndexOfSlash = $pdir.SubDirectory.IndexOf($s)
            $pdirToRegexPattern = {
                $UpdatedString = $pdir.SubDirectory.Remove($IndexOfSlash, 1)
                $UpdatedString.Insert($IndexOfSlash, [regex]::Escape($s))
            }.Invoke()

            if ($adir -match $pdirToRegexPattern) {
                $FoundDirPSObj = [pscustomobject]@{
                    Preference   = $pdir.Preference
                    Directory    = $adir
                }
                $null = $FoundSubDirsPSObjects.Add($FoundDirPSObj)
            }
        }
    }

    $TargetDir = $($FoundSubDirsPSObjects | Sort-Object -Property Preference)[0].Directory
    $AssemblyPath = Get-NativePath @($TargetDir, $(Get-ChildItem $TargetDir -File -Filter "*.dll").Name)
    
    [pscustomobject]@{
        NuGetPackageDirectory   = $NuGetPkgExtractionDirectory
        AssemblyToLoad          = $AssemblyPath
    }
    

    <#
    $CurrentLoadedAssemblies = [System.AppDomain]::CurrentDomain.GetAssemblies()
    $CheckAssemblyIsLoaded = $CurrentLoadedAssemblies | Where-Object {$_.FullName -like "$AssemblyName*"}
    if ($CheckAssemblyIsLoaded -eq $null) {
        Add-Type -Path $AssemblyPath
    }
    else {
        Write-Warning "The Assembly $AssemblyName is already loaded!"
    }
    #>


    
    ##### END Main Body #####

}