Support/Package/Execution/Eigenverft.Manifested.Sandbox.Package.VersionSelection.ps1
|
<#
Eigenverft.Manifested.Sandbox.Package.VersionSelection Version ordering policy for authored package releases: - normalVersion: full [version] values such as 1.15.7, 24.15.0, 1.4.8.1. - plainInteger: single integer build versions such as 9094. - dateHash: date-like dotted numeric prefix with a suffix, such as 2026.05.09-0afadcc. - numericPrefix: fallback for other values that contain a numeric dotted prefix. Selection is intentionally local to authored, compatible release candidates; no strategy in this file performs a network "latest" lookup. #> function ConvertTo-PackageVersion { <# .SYNOPSIS Converts package version text to a comparable Version object. .DESCRIPTION Keeps existing semantic-like version comparisons while also supporting single integer package versions as Major.0 and suffix-bearing versions by their first numeric dotted prefix. #> [CmdletBinding()] param( [AllowNull()] [string]$VersionText ) if ([string]::IsNullOrWhiteSpace($VersionText)) { return [version]'0.0.0' } $text = ([string]$VersionText).Trim() if ($text -match '^\d+$') { return [version]('{0}.0' -f $text) } try { return [version]$text } catch { $match = [regex]::Match($text, '\d+(?:\.\d+){0,3}') if ($match.Success) { try { $numericText = $match.Value if ($numericText -match '^\d+$') { $numericText = '{0}.0' -f $numericText } return [version]$numericText } catch { } } } return [version]'0.0.0' } function Get-PackageVersionOrderingInfo { [CmdletBinding()] param( [AllowNull()] [string]$VersionText, [int]$AuthorIndex = 0, [int]$CandidateIndex = 0 ) $text = if ([string]::IsNullOrWhiteSpace($VersionText)) { '' } else { ([string]$VersionText).Trim() } $orderingKind = 'numericPrefix' if ([string]::IsNullOrWhiteSpace($text)) { $orderingKind = 'empty' } elseif ($text -match '^\d+$') { $orderingKind = 'plainInteger' } else { try { $null = [version]$text $orderingKind = 'normalVersion' } catch { if ($text -match '^\d{4}\.\d{1,2}\.\d{1,2}(?:\.\d+)?[-+_].+$') { $orderingKind = 'dateHash' } } } return [pscustomobject]@{ VersionText = $text OrderingKind = $orderingKind SortVersion = ConvertTo-PackageVersion -VersionText $text AuthorIndex = $AuthorIndex CandidateIndex = $CandidateIndex } } function Sort-PackageVersionCandidates { [CmdletBinding()] param( [AllowNull()] [object[]]$Candidates ) return @($Candidates) | Sort-Object ` @{ Expression = { $_.VersionOrdering.SortVersion }; Descending = $true }, @{ Expression = { $_.VersionOrdering.AuthorIndex }; Descending = $false }, @{ Expression = { $_.VersionOrdering.CandidateIndex }; Descending = $false } } function Resolve-PackageVersionCandidateSelection { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [object[]]$Candidates, [AllowNull()] [string]$CommandSelector = $null, [Parameter(Mandatory = $true)] [string]$DefinitionId, [Parameter(Mandatory = $true)] [string]$Platform, [Parameter(Mandatory = $true)] [string]$Architecture, [Parameter(Mandatory = $true)] [string]$ReleaseTrack, [AllowNull()] [object[]]$AllVersionEntries ) if (@($Candidates).Count -eq 0) { throw "No Package target/release entry matched platform '$Platform', architecture '$Architecture', and releaseTrack '$ReleaseTrack'." } $selector = if ([string]::IsNullOrWhiteSpace($CommandSelector)) { $null } else { ([string]$CommandSelector).Trim() } $selectionSource = if ([string]::IsNullOrWhiteSpace($selector)) { 'definition' } else { 'command' } if ([string]::IsNullOrWhiteSpace($selector)) { $firstTarget = @($Candidates)[0].ArtifactTarget $selector = if ($firstTarget -and $firstTarget.PSObject.Properties['versionSelection'] -and $firstTarget.versionSelection -and $firstTarget.versionSelection.PSObject.Properties['strategy'] -and -not [string]::IsNullOrWhiteSpace([string]$firstTarget.versionSelection.strategy)) { [string]$firstTarget.versionSelection.strategy } else { 'latestByVersion' } } $orderedCandidates = @(Sort-PackageVersionCandidates -Candidates $Candidates) $selected = $null $requestedVersion = $null $selectionKind = $selector if ([string]::Equals($selector, 'latestByVersion', [System.StringComparison]::OrdinalIgnoreCase)) { $selected = $orderedCandidates | Select-Object -First 1 $selectionKind = 'latestByVersion' } elseif ([string]::Equals($selector, 'previousByVersion', [System.StringComparison]::OrdinalIgnoreCase)) { $selected = if ($orderedCandidates.Count -gt 1) { $orderedCandidates[1] } else { $orderedCandidates[0] } $selectionKind = 'previousByVersion' } else { $requestedVersion = $selector $matches = @( foreach ($candidate in @($Candidates)) { if ([string]::Equals([string]$candidate.VersionEntry.version, $requestedVersion, [System.StringComparison]::OrdinalIgnoreCase)) { $candidate } } ) if ($matches.Count -eq 0) { $versionIsAuthored = $false foreach ($versionEntry in @($AllVersionEntries)) { if ([string]::Equals([string]$versionEntry.version, $requestedVersion, [System.StringComparison]::OrdinalIgnoreCase)) { $versionIsAuthored = $true break } } if ($versionIsAuthored) { throw "Package version '$requestedVersion' is not available for definition '$DefinitionId' on platform '$Platform', architecture '$Architecture', and releaseTrack '$ReleaseTrack'." } throw "Package version '$requestedVersion' is not authored for definition '$DefinitionId'." } $selected = @(Sort-PackageVersionCandidates -Candidates $matches) | Select-Object -First 1 $selectionKind = 'exact' } return [pscustomobject]@{ Candidate = $selected Selector = $selector Source = $selectionSource SelectionKind = $selectionKind OrderingKind = [string]$selected.VersionOrdering.OrderingKind RequestedVersion = $requestedVersion } } |