Commands/Package/Eigenverft.Manifested.Package.Cmd.SearchPackage.ps1
|
<#
Public package search command surface. #> function Get-PackageSearchSettings { [CmdletBinding()] param() $globalDocumentInfo = Read-PackageJsonDocument -Path (Get-PackageConfigPath) Assert-PackageConfigSchema -PackageConfigDocumentInfo $globalDocumentInfo $packageGlobalConfig = $globalDocumentInfo.Document.package $applicationRootDirectory = Resolve-PackageApplicationRootDirectory -PackageConfiguration $packageGlobalConfig $catalogTrustPolicy = 'strict' $catalogTrustUnknownSignedKeyPolicy = 'prompt' $catalogTrustAllowUnsignedPublisherIds = @() $catalogTrustBlockedPublisherIds = @() if ($packageGlobalConfig.PSObject.Properties['catalogTrust'] -and $packageGlobalConfig.catalogTrust) { if ($packageGlobalConfig.catalogTrust.PSObject.Properties['policy'] -and -not [string]::IsNullOrWhiteSpace([string]$packageGlobalConfig.catalogTrust.policy)) { $catalogTrustPolicy = [string]$packageGlobalConfig.catalogTrust.policy } if ($packageGlobalConfig.catalogTrust.PSObject.Properties['unknownSignedKeyPolicy'] -and -not [string]::IsNullOrWhiteSpace([string]$packageGlobalConfig.catalogTrust.unknownSignedKeyPolicy)) { $catalogTrustUnknownSignedKeyPolicy = [string]$packageGlobalConfig.catalogTrust.unknownSignedKeyPolicy } if ($packageGlobalConfig.catalogTrust.PSObject.Properties['allowUnsignedPublisherIds'] -and $null -ne $packageGlobalConfig.catalogTrust.allowUnsignedPublisherIds) { $catalogTrustAllowUnsignedPublisherIds = @( foreach ($configuredPublisherId in @($packageGlobalConfig.catalogTrust.allowUnsignedPublisherIds)) { $normalizedPublisherId = ([string]$configuredPublisherId).Trim() if (-not [string]::IsNullOrWhiteSpace($normalizedPublisherId)) { $normalizedPublisherId } } ) } if ($packageGlobalConfig.catalogTrust.PSObject.Properties['blockedPublisherIds'] -and $null -ne $packageGlobalConfig.catalogTrust.blockedPublisherIds) { $catalogTrustBlockedPublisherIds = @( foreach ($configuredPublisherId in @($packageGlobalConfig.catalogTrust.blockedPublisherIds)) { $normalizedPublisherId = ([string]$configuredPublisherId).Trim() if (-not [string]::IsNullOrWhiteSpace($normalizedPublisherId)) { $normalizedPublisherId } } ) } } $releaseTrack = 'none' if ($packageGlobalConfig.PSObject.Properties['selectionDefaults'] -and $packageGlobalConfig.selectionDefaults -and $packageGlobalConfig.selectionDefaults.PSObject.Properties['releaseTrack'] -and -not [string]::IsNullOrWhiteSpace([string]$packageGlobalConfig.selectionDefaults.releaseTrack)) { $releaseTrack = [string]$packageGlobalConfig.selectionDefaults.releaseTrack } return [pscustomobject]@{ ApplicationRootDirectory = $applicationRootDirectory ReleaseTrack = $releaseTrack CatalogTrustPolicy = $catalogTrustPolicy CatalogTrustAllowUnsignedPublisherIds = @($catalogTrustAllowUnsignedPublisherIds) CatalogTrustBlockedPublisherIds = @($catalogTrustBlockedPublisherIds) CatalogTrustUnknownSignedKeyPolicy = $catalogTrustUnknownSignedKeyPolicy } } function Get-PackageSearchCandidateRows { [CmdletBinding()] param( [AllowNull()] [string]$EndpointName = $null, [Parameter(Mandatory = $true)] [psobject]$Settings, [Parameter(Mandatory = $true)] [psobject]$TrustInventoryDocument ) $endpointInventoryInfo = Get-PackageEndpointInventoryInfo $sourceRows = @(Get-PackageEnabledEndpointSources -EndpointInventoryDocument $endpointInventoryInfo.Document) if (-not [string]::IsNullOrWhiteSpace($EndpointName)) { $sourceRows = @($sourceRows | Where-Object { [string]::Equals([string]$_.EndpointName, [string]$EndpointName, [System.StringComparison]::OrdinalIgnoreCase) }) } $candidateRows = New-Object System.Collections.Generic.List[object] foreach ($sourceRow in @($sourceRows)) { try { $scanRootPath = Resolve-PackageEndpointRootPath -EndpointName ([string]$sourceRow.EndpointName) -Source $sourceRow.Source -ApplicationRootDirectory ([string]$Settings.ApplicationRootDirectory) foreach ($candidate in @(Select-PackageDefinitionCandidatesFromEndpointScanRoot -EndpointName ([string]$sourceRow.EndpointName) -EndpointSource $sourceRow.Source -ScanRootPath $scanRootPath -TrustInventoryDocument $TrustInventoryDocument)) { $candidate | Add-Member -MemberType NoteProperty -Name EndpointSearchOrder -Value ([int]$sourceRow.SearchOrder) -Force $candidateRows.Add($candidate) | Out-Null } } catch { Write-Warning ("Skipped package endpoint '{0}' during search: {1}" -f [string]$sourceRow.EndpointName, $_.Exception.Message) } } return @($candidateRows.ToArray()) } function Get-PackageSearchTextFields { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Definition ) $fields = New-Object System.Collections.Generic.List[string] if ($Definition.PSObject.Properties['definitionPublication'] -and $Definition.definitionPublication) { foreach ($name in @('definitionId', 'publisherId', 'publisherName')) { if ($Definition.definitionPublication.PSObject.Properties[$name] -and -not [string]::IsNullOrWhiteSpace([string]$Definition.definitionPublication.$name)) { $fields.Add([string]$Definition.definitionPublication.$name) | Out-Null } } } if ($Definition.PSObject.Properties['display'] -and $Definition.display.PSObject.Properties['default'] -and $Definition.display.default) { foreach ($name in @('name', 'publisher', 'corporation', 'summary')) { if ($Definition.display.default.PSObject.Properties[$name] -and -not [string]::IsNullOrWhiteSpace([string]$Definition.display.default.$name)) { $fields.Add([string]$Definition.display.default.$name) | Out-Null } } } foreach ($tag in @(Get-PackageDefinitionClassificationTags_1_9 -Definition $Definition)) { if (-not [string]::IsNullOrWhiteSpace([string]$tag)) { $fields.Add([string]$tag) | Out-Null } } if ($Definition.PSObject.Properties['discovery'] -and $Definition.discovery.PSObject.Properties['presence'] -and $Definition.discovery.presence) { $presence = $Definition.discovery.presence foreach ($command in @($presence.commands)) { if ($null -eq $command) { continue } if ($command.PSObject.Properties['name'] -and -not [string]::IsNullOrWhiteSpace([string]$command.name)) { $fields.Add([string]$command.name) | Out-Null } if ($command.PSObject.Properties['relativePath'] -and -not [string]::IsNullOrWhiteSpace([string]$command.relativePath)) { $fields.Add([string]$command.relativePath) | Out-Null } } foreach ($app in @($presence.apps)) { if ($null -eq $app) { continue } if ($app.PSObject.Properties['name'] -and -not [string]::IsNullOrWhiteSpace([string]$app.name)) { $fields.Add([string]$app.name) | Out-Null } } foreach ($module in @($presence.powerShellModules)) { if ($null -eq $module) { continue } if ($module.PSObject.Properties['name'] -and -not [string]::IsNullOrWhiteSpace([string]$module.name)) { $fields.Add([string]$module.name) | Out-Null } } foreach ($file in @($presence.files)) { if (-not [string]::IsNullOrWhiteSpace([string]$file)) { $fields.Add([string]$file) | Out-Null } } } return @($fields.ToArray() | Select-Object -Unique) } function Test-PackageSearchQueryMatch { [CmdletBinding()] param( [AllowNull()] [string]$Query = $null, [AllowNull()] [string[]]$Fields = @() ) if ([string]::IsNullOrWhiteSpace($Query)) { return $true } foreach ($term in @(([string]$Query).Split([char[]]@(' ', "`t", "`r", "`n"), [System.StringSplitOptions]::RemoveEmptyEntries))) { $termMatched = $false foreach ($field in @($Fields)) { if ($null -ne $field -and ([string]$field).IndexOf($term, [System.StringComparison]::OrdinalIgnoreCase) -ge 0) { $termMatched = $true break } } if (-not $termMatched) { return $false } } return $true } function Test-PackageSearchTagMatch { [CmdletBinding()] [OutputType([bool])] param( [AllowNull()] [string[]]$RequiredTags = @(), [AllowNull()] [string[]]$DefinitionTags = @() ) if (@($RequiredTags).Count -eq 0) { return $true } $availableTags = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase) foreach ($tag in @($DefinitionTags)) { if (-not [string]::IsNullOrWhiteSpace([string]$tag)) { $availableTags.Add(([string]$tag).Trim()) | Out-Null } } foreach ($requiredTag in @($RequiredTags)) { if (-not $availableTags.Contains(([string]$requiredTag).Trim())) { return $false } } return $true } function Resolve-PackageSearchRequiredTags { [CmdletBinding()] [OutputType([string[]])] param( [AllowNull()] [string[]]$Tag = @() ) $requiredTags = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase) foreach ($rawTag in @($Tag)) { $normalizedTag = ([string]$rawTag).Trim() if ([string]::IsNullOrWhiteSpace($normalizedTag)) { throw 'Search-Package -Tag cannot contain an empty or whitespace-only tag.' } $requiredTags.Add($normalizedTag) | Out-Null } return @( foreach ($requiredTag in $requiredTags) { $requiredTag } ) } function Get-PackageSearchEntryPointNames { [CmdletBinding()] param( [AllowNull()] [psobject]$Definition ) if (-not $Definition -or -not $Definition.PSObject.Properties['discovery'] -or -not $Definition.discovery.PSObject.Properties['presence'] -or -not $Definition.discovery.presence) { return [pscustomobject]@{ Commands = @(); Apps = @(); PowerShellModules = @() } } $presence = $Definition.discovery.presence return [pscustomobject]@{ Commands = @($presence.commands | ForEach-Object { if ($null -ne $_ -and $_.PSObject.Properties['name'] -and -not [string]::IsNullOrWhiteSpace([string]$_.name)) { [string]$_.name } } | Select-Object -Unique) Apps = @($presence.apps | ForEach-Object { if ($null -ne $_ -and $_.PSObject.Properties['name'] -and -not [string]::IsNullOrWhiteSpace([string]$_.name)) { [string]$_.name } } | Select-Object -Unique) PowerShellModules = @($presence.powerShellModules | ForEach-Object { if ($null -ne $_ -and $_.PSObject.Properties['name'] -and -not [string]::IsNullOrWhiteSpace([string]$_.name)) { [string]$_.name } } | Select-Object -Unique) } } function Get-PackageSearchTargetSummaries { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Definition ) return @( foreach ($target in @($Definition.artifacts.targets)) { $constraints = if ($target.PSObject.Properties['constraints']) { $target.constraints } else { [pscustomobject]@{} } $os = if ($constraints.PSObject.Properties['os']) { @($constraints.os) } else { @('*') } $cpu = if ($constraints.PSObject.Properties['cpu']) { @($constraints.cpu) } else { @('*') } [pscustomobject]@{ TargetId = [string]$target.id ReleaseTrack = [string]$target.releaseTrack ArtifactDistributionVariant = [string]$target.artifactDistributionVariant Os = @($os) Cpu = @($cpu) } } ) } function Resolve-PackageSearchSelectedVersion { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Definition, [Parameter(Mandatory = $true)] [string]$DefinitionId, [Parameter(Mandatory = $true)] [string]$Platform, [Parameter(Mandatory = $true)] [string]$Architecture, [Parameter(Mandatory = $true)] [string]$ReleaseTrack ) try { $packageConfig = [pscustomobject]@{ Definition = $Definition DefinitionId = $DefinitionId Platform = $Platform Architecture = $Architecture ReleaseTrack = $ReleaseTrack } $package = Resolve-PackageEffectivePackage_1_9 -PackageConfig $packageConfig return [pscustomobject]@{ Available = $true Version = [string]$package.version TargetId = [string]$package.artifactTargetId ReleaseTrack = [string]$package.releaseTrack ArtifactDistributionVariant = [string]$package.artifactDistributionVariant Error = $null } } catch { return [pscustomobject]@{ Available = $false Version = $null TargetId = $null ReleaseTrack = $ReleaseTrack ArtifactDistributionVariant = $null Error = $_.Exception.Message } } } function Search-Package { <# .SYNOPSIS Searches enabled package definition endpoints. .DESCRIPTION Scans enabled moduleLocal and filesystem endpoints, applies catalog trust eligibility, and returns package definition rows that match the query. .PARAMETER Tag Filters results to definitions that carry every requested classification tag. Tag matches are exact and case-insensitive. #> [CmdletBinding()] param( [Parameter(Position = 0)] [AllowNull()] [string]$Query = $null, [AllowNull()] [string[]]$Tag = @(), [AllowNull()] [string]$PublisherId = $null, [AllowNull()] [string]$EndpointName = $null, [AllowNull()] [string]$Platform = $null, [AllowNull()] [string]$Architecture = $null, [AllowNull()] [string]$ReleaseTrack = $null, [switch]$CurrentPlatformOnly, [switch]$IncludeIneligible ) $settings = Get-PackageSearchSettings $runtimeContext = Get-PackageRuntimeContext $effectivePlatform = if ([string]::IsNullOrWhiteSpace($Platform)) { [string]$runtimeContext.Platform } else { ([string]$Platform).Trim() } $effectiveArchitecture = if ([string]::IsNullOrWhiteSpace($Architecture)) { [string]$runtimeContext.Architecture } else { ([string]$Architecture).Trim() } $effectiveReleaseTrack = if ([string]::IsNullOrWhiteSpace($ReleaseTrack)) { [string]$settings.ReleaseTrack } else { ([string]$ReleaseTrack).Trim() } $requiredTags = @(Resolve-PackageSearchRequiredTags -Tag $Tag) $trustInventoryInfo = Get-PackageTrustInventoryInfo $rows = New-Object System.Collections.Generic.List[object] foreach ($candidate in @(Get-PackageSearchCandidateRows -EndpointName $EndpointName -Settings $settings -TrustInventoryDocument $trustInventoryInfo.Document)) { if (-not [string]::IsNullOrWhiteSpace($PublisherId) -and -not [string]::Equals([string]$candidate.PublisherId, [string]$PublisherId, [System.StringComparison]::OrdinalIgnoreCase)) { continue } $trust = Resolve-PackageDefinitionCandidateTrustEligibility -Candidate $candidate -CatalogTrustPolicy ([string]$settings.CatalogTrustPolicy) -CatalogTrustAllowUnsignedPublisherIds @($settings.CatalogTrustAllowUnsignedPublisherIds) -CatalogTrustBlockedPublisherIds @($settings.CatalogTrustBlockedPublisherIds) -UnknownSignedKeyPolicy ([string]$settings.CatalogTrustUnknownSignedKeyPolicy) if (-not $trust.Eligible -and -not $IncludeIneligible.IsPresent) { continue } try { $definitionInfo = Read-PackageJsonDocument -Path ([string]$candidate.DefinitionPath) Assert-PackageDefinitionSchema -DefinitionDocumentInfo $definitionInfo -DefinitionId ([string]$candidate.DefinitionId) -PublisherId ([string]$candidate.PublisherId) } catch { Write-Warning ("Skipped package definition '{0}' during search: {1}" -f [string]$candidate.DefinitionPath, $_.Exception.Message) continue } $definition = $definitionInfo.Document $definitionTags = @(Get-PackageDefinitionClassificationTags_1_9 -Definition $definition) $fields = @(Get-PackageSearchTextFields -Definition $definition) if (-not (Test-PackageSearchQueryMatch -Query $Query -Fields $fields)) { continue } if (-not (Test-PackageSearchTagMatch -RequiredTags $requiredTags -DefinitionTags $definitionTags)) { continue } $selection = Resolve-PackageSearchSelectedVersion -Definition $definition -DefinitionId ([string]$candidate.DefinitionId) -Platform $effectivePlatform -Architecture $effectiveArchitecture -ReleaseTrack $effectiveReleaseTrack if ($CurrentPlatformOnly.IsPresent -and -not [bool]$selection.Available) { continue } $display = $definition.display.default $entryPoints = Get-PackageSearchEntryPointNames -Definition $definition $targetSummaries = @(Get-PackageSearchTargetSummaries -Definition $definition) $invokeCommand = if ([string]::IsNullOrWhiteSpace([string]$candidate.PublisherId)) { "Invoke-Package -DefinitionId '$($candidate.DefinitionId)'" } else { "Invoke-Package -DefinitionId '$($candidate.DefinitionId)' -PublisherId '$($candidate.PublisherId)'" } $rows.Add([pscustomobject]@{ DefinitionId = [string]$candidate.DefinitionId PublisherId = [string]$candidate.PublisherId PublisherName = [string]$candidate.PublisherName Name = [string]$display.name DisplayPublisher = [string]$display.publisher Corporation = [string]$display.corporation Summary = [string]$display.summary Tags = @($definitionTags) Version = [string]$selection.Version PlatformAvailable = [bool]$selection.Available Platform = $effectivePlatform Architecture = $effectiveArchitecture ReleaseTrack = $effectiveReleaseTrack SelectedTargetId = [string]$selection.TargetId SelectedArtifactDistributionVariant = [string]$selection.ArtifactDistributionVariant SelectionError = [string]$selection.Error Commands = @($entryPoints.Commands) Apps = @($entryPoints.Apps) PowerShellModules = @($entryPoints.PowerShellModules) Targets = @($targetSummaries) EndpointName = [string]$candidate.EndpointName EndpointSearchOrder = [int]$candidate.EndpointSearchOrder EndpointSourceKind = [string]$candidate.EndpointSourceKind DefinitionRevision = [int]$candidate.DefinitionRevision PublishedAtUtc = [string]$candidate.PublishedAtUtc CatalogTrustStatus = [string]$trust.TrustStatus CatalogTrustReason = [string]$trust.TrustReason CatalogTrustEligible = [bool]$trust.Eligible SignatureStatus = [string]$candidate.SignatureStatus SignatureValid = [bool]$candidate.SignatureValid SignatureTrusted = [bool]$candidate.SignatureTrusted SignatureKeyThumbprint = if ([string]::IsNullOrWhiteSpace([string]$candidate.SignatureKeyThumbprint)) { $null } else { [string]$candidate.SignatureKeyThumbprint } DefinitionPath = [string]$candidate.DefinitionPath InvokeCommand = $invokeCommand }) | Out-Null } return @($rows.ToArray() | Sort-Object -Property DefinitionId, PublisherId, EndpointSearchOrder, EndpointName, DefinitionRevision) } |