Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Last active September 8, 2022 05:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jaykul/2700d288b9f541e8d45cbbaf83768c63 to your computer and use it in GitHub Desktop.
Save Jaykul/2700d288b9f541e8d45cbbaf83768c63 to your computer and use it in GitHub Desktop.
class PSEquality : System.Collections.Generic.EqualityComparer[PSObject] {
[scriptblock]$Comparer = { $_ }
[bool] Equals([PSObject]$first, [PSObject]$other) {
return ($first | ForEach-Object $this.Comparer) -eq ($other | ForEach-Object $this.Comparer)
}
[int] GetHashCode([PSObject]$module) {
return $module.GetHashCode()
}
}
function Optimize-Dependency {
<#
.SYNOPSIS
Optimize a set of objects by their dependencies
.EXAMPLE
Find-Module TerminalBlocks, PowerLine |
Optimize-Dependency -Properties Name, Version -Dependency {
$_.Dependencies.Name.ForEach{ Find-Module $_ }
}
#>
[Alias("Sort-Dependency")]
[OutputType([Array])]
[CmdletBinding(DefaultParameterSetName = "ByPropertyFromInputObject")]
param(
# The path to a RequiredModules file
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "CustomEqualityFromPath")]
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "ByPropertyFromPath")]
[string]$Path,
# The objects you want to sort
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "CustomEqualityFromInputObject")]
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "ByPropertyFromInputObject")]
[PSObject[]]$InputObject,
# A list of properties used with Compare-Object in the default equality comparer
# Since this is in RequiredModules, it defaults to "Name", "Version"
[Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "ByPropertyFromInputObject")]
[Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "ByPropertyFromPath")]
[string[]]$Properties = @("Name", "Version"),
# A custom implementation of the equality comparer for the InputObjects
# Must accept two arguments, and return $true if they are equal, $false otherwise
# InputObjects will only be added to the output if this returns $false
# The default EqualityFilter compares based on the $Properties
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "CustomEqualityFromInputObject")]
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "CustomEqualityFromPath")]
[scriptblock]$EqualityFilter = { !($args[0] | Compare-Object $args[1] -Property $Properties) },
# A ScriptBlock to calculate the dependencies of the InputObjects
# Defaults to a scriptblock that works for Find-Module and Get-Module
[Parameter(ValueFromPipelineByPropertyName)]
[ScriptBlock]$Dependency = {
if ($_ -is [psmoduleinfo]) {
$_.RequiredModules.Name.ForEach{ Get-Module $_ }
} else {
$_.Dependencies.Name.ForEach{ Find-Module $_ }
}
}
)
begin {
if ($Path) {
$null = $PSBoundParameters.Remove("Path")
ImportRequiredModulesFile $Path | FindModuleVersion | Optimize-Dependency @PSBoundParameters
return
}
if ($null -eq $Optimize_Dependency_Results) {
$Optimize_Dependency_Results = [System.Collections.Generic.HashSet[PSObject]]::new([PSEquality]::new($Properties, $EqualityFilter))
}
}
process {
$null = $PSBoundParameters.Remove("InputObject")
Write-Debug "ENTER: Optimize-Dependency $(@($Optimize_Dependency_Results).Count): $(@($InputObject | Select-Object $Properties | ForEach-Object { $_.PsObject.Properties.Value}) -join ',')"
$InputObject | ForEach-Object $Dependency | Optimize-Dependency @PSBoundParameters
$InputObject | Sort-Object $Properties | ForEach-Object {
Write-Verbose "Adding $(@($_ | Select-Object $Properties | ForEach-Object { $_.PsObject.Properties.Value}) -join ',')"
if ($Optimize_Dependency_Results.Add($_)) {
$_
}
}
}
}
# Trivial example, using already imported modules locally
Import-Module PowerShellGet -Max 2.99 -Passthru | Optimize-Dependency
# Easy example, searching the gallery
Find-Module TerminalBlocks, PowerLine | Optimize-Dependency
# The worst case that I know of:
Find-Module VMware.PowerCLI | Optimize-Dependency
@Jaykul
Copy link
Author

Jaykul commented Sep 8, 2022

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