Skip to content

Instantly share code, notes, and snippets.

@anonhostpi
Last active October 7, 2023 11:29
Show Gist options
  • Save anonhostpi/f385e0ec2bca28cc5cc2a136627a4c69 to your computer and use it in GitHub Desktop.
Save anonhostpi/f385e0ec2bca28cc5cc2a136627a4c69 to your computer and use it in GitHub Desktop.
NuGet.Frameworks and Microsft.NETCore.Platforms wrappers for PowerShell (bootstrap code for writing an Import-Package cmdlet)
& {
Add-Type -AssemblyName System.Runtime -ErrorAction SilentlyContinue #needed by Import-Package for RID detection
$Bootstrapper = New-Object psobject
try {
Add-Type -AssemblyName System.Reflection -ErrorAction SilentlyContinue
Add-Type -AssemblyName System.Runtime.Loader -ErrorAction SilentlyContinue
$Bootstrapper | Add-Member `
-MemberType NoteProperty `
-Name Context `
-Value [System.Runtime.Loader.AssemblyLoadContext]::new( "", $true )
$Bootstrapper | Add-Member `
-MemberType ScriptMethod `
-Name Unload `
-Value {
$This.Context.Unload()
}
} catch { }
$Bootstrapper | Add-Member `
-Name GetLatest `
-MemberType ScriptMethod `
-Value {
param( $name )
$apis = Invoke-WebRequest https://api.nuget.org/v3/index.json
$apis = $this.Deserialize( $apis.ToString(), [hashtable] )
$resources = If( $apis.resources.GetType().Name -eq "JsonElement" ){
$apis.resources.EnumerateArray() | ForEach-Object {
$output = @{}
$_.EnumerateObject() | ForEach-Object {
$output[ $_.Name ] = $_.Value.ToString()
}
$output
}
} else {
$apis.resources
}
$resource = $resources | Where-Object {
($_."@type" -eq "SearchQueryService") -and
($_."comment" -like "*(primary)*")
} | Select-Object -First 1
$id = $resource."@id"
$results = Invoke-WebRequest "$id`?q=packageid:$Name&prerelease=false&take=1"
$results = $this.Deserialize( $results.ToString(), [hashtable] )
$metadata = $results.data[0]
if( $metadata.GetType().Name -eq "JsonElement" ){
($metadata.EnumerateObject() | Where-Object { $_.Name -eq "version" }).Value.ToString()
} else {
$metadata.version
}
}
$Bootstrapper | Add-Member `
-Name Deserialize `
-MemberType ScriptMethod `
-Value (& {
Try {
Add-Type -AssemblyName System.Text.Json -ErrorAction SilentlyContinue
if( [System.Text.Json.JsonSerializer]::Deserialize ){
{
param( $text, $type = [hashtable] )
[System.Text.Json.JsonSerializer]::Deserialize( $text, [hashtable] )
}
}
} Catch {
Try {
Add-Type -AssemblyName System.Web.Extensions -ErrorAction SilentlyContinue
if( [System.Web.Script.Serialization.JavaScriptSerializer] ){
{
param( $text, $type = [hashtable] )
[System.Web.Script.Serialization.JavaScriptSerializer]::new().Deserialize( $text, [hashtable] )
}
}
} Catch {
{
param( $text, $type )
if( $type = [hashtable] ){
Try {
ConvertFrom-Json $text -AsHashtable
} Catch {
ConvertFrom-Json $text
}
} else {
ConvertFrom-Json $text
}
}
}
}
})
Add-Type -assembly "system.io.compression.filesystem"
$Bootstrapper | Add-Member `
-MemberType ScriptMethod `
-Name GetContentsFromZip `
-Value {
param( $Archive, $Path )
$zip = [io.compression.zipfile]::OpenRead($Archive)
$file = $zip.Entries | where-object { $_.FullName -eq $Path }
$stream = $file.Open()
$reader = New-Object IO.StreamReader( $stream )
$content = $reader.ReadToEnd()
$content
$reader.Close() | Out-Null
$stream.Close() | Out-Null
$zip.Dispose() | Out-Null
}
$Bootstrapper | Add-Member `
-MemberType ScriptMethod `
-Name Load `
-Value {
param(
[string] $Path,
[bool] $Partial = $false
)
try {
If( $Partial ){
$This.Context.LoadFromAssemblyName( [System.Reflection.AssemblyName]::new( $Path ) ) | Out-Null
} else {
$This.Context.LoadFromAssemblyPath( $Path ) | Out-Null
}
} catch {
try {
$AddTypeParams = @{
PassThru = $false
}
if( $Partial ) {
$AddTypeParams.AssemblyName = $Path
} else {
$AddTypeParams.Path = $Path
}
Add-Type @AddTypeParams
} catch { Write-Host "Unable to load $AssemblyName" }
}
}
$Bootstrapper | Add-Member `
-MemberType ScriptMethod `
-Name ParseTypeName `
-Value {
param(
[string] $TypeName
)
try {
$Assemblies = $This.Context.Assemblies | Where-Object { $ _ }
$Assemblies.GetType( $TypeName, $false, $true )
} catch {
try {
[Type]"$TypeName"
} catch { $null }
}
}
$Bootstrapper | Add-Member `
-MemberType ScriptMethod `
-Name Init `
-Value {
If( -not( $This.ParseTypeName( "NuGet.Packaging.PackageArchiveReader" ) ) ){
$time_ordergen = Measure-Command {
$load_order = [System.Collections.ArrayList]::new()
$load_order.Add( "NuGet.Packaging" ) | Out-Null
# Loop initialization:
$index = 0
$package_name = ""
# Caching for performance:
$package_table = @{}
while( $index -lt $load_order.Count ){
$package_name = $load_order[ $index ]
If( -not( $package_table.ContainsKey( $package_name ) ) ){
$package = Get-Package $package_name -ProviderName NuGet -ErrorAction SilentlyContinue
$latest = Try {
$this.GetLatest( $package_name )
} Catch { $package.Version }
if( (-not $package) -or ($package.Version -ne $latest) ){
Try {
Install-Package $package_name `
-ProviderName NuGet `
-SkipDependencies `
-Force | Out-Null
} Catch {}
$package = Get-Package $package_name -ProviderName NuGet -ErrorAction Stop
}
$package_table[ $package_name ] = $package.Source.ToString()
# Get .NETStandard2.0 dependencies
$dependencies = (
([xml] $bootstrapper.GetContentsFromZip(
$package_table[ $package_name ],
"$package_name.nuspec"
)).package.metadata.dependencies.group |
Where-Object { $_.targetFramework -eq ".NETStandard2.0" }
).Dependency
$dependencies = $dependencies | Where-Object { $_.Id }
$dependencies |
ForEach-Object {
$load_order.Add( $_.Id ) | Out-Null
}
} else {
$oldindex = $load_order.IndexOf( $package_name )
$load_order.RemoveAt( $oldindex )
$load_order.Add( $package_name )
}
$index++
}
$this | Add-Member `
-MemberType NoteProperty `
-Name Dependencies `
-Value ($load_order | Select-Object -Unique) | Out-Null
[array]::Reverse( $this.Dependencies )
}
Write-Host "Total time:" $time_ordergen.TotalSeconds
$this.Dependencies | ForEach-Object {
$package_source = $package_table[ $_ ]
$dll = Resolve-Path "$(Split-Path $package_source -ErrorAction SilentlyContinue)\lib\netstandard2.0\$_.dll" -ErrorAction SilentlyContinue
if (-not (Test-Path $dll)) {
Write-Host "Unable to find $_"
} else {
$this.Load( $dll.ToString() )
}
}
}
}
# Return the bootstrapper, and initialize it
$Bootstrapper
$Bootstrapper.Init()
}
@anonhostpi
Copy link
Author

anonhostpi commented Oct 6, 2023

Optimization 1:

@anonhostpi
Copy link
Author

anonhostpi commented Oct 6, 2023

Improvement 2:

@anonhostpi
Copy link
Author

anonhostpi commented Oct 6, 2023

Improvement 3:

@anonhostpi
Copy link
Author

anonhostpi commented Oct 7, 2023

Improvement 4:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment