Skip to content

Commit c3b9ff4

Browse files
Stop version resolution on unverifiable Gallery reads
1 parent ee65fa7 commit c3b9ff4

2 files changed

Lines changed: 121 additions & 27 deletions

File tree

‎.github/actions/Resolve-PSModuleVersion/src/Resolve-PSModuleVersion.Helpers.psm1‎

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
function Split-CommaSeparatedList {
1+
$ErrorActionPreference = 'Stop'
2+
3+
function Split-CommaSeparatedList {
24
<#
35
.SYNOPSIS
46
Splits a comma-separated string into a trimmed, non-empty array.
@@ -455,7 +457,8 @@ function Get-LatestPSGalleryVersion {
455457
456458
.DESCRIPTION
457459
Queries the PowerShell Gallery for the latest published version of the module.
458-
Retries up to five times with a ten-second delay between attempts.
460+
Only verified PackageNotFound absence provides a first-release baseline.
461+
Authentication, API, and incomplete-metadata errors stop version resolution.
459462
460463
.OUTPUTS
461464
PSSemVer representing the latest PSGallery version.
@@ -475,32 +478,22 @@ function Get-LatestPSGalleryVersion {
475478
)
476479

477480
LogGroup 'Get latest version - PSGallery' {
478-
$retryCount = 5
479-
$retryDelaySeconds = 10
480-
$latest = $null
481-
482-
for ($i = 1; $i -le $retryCount; $i++) {
483-
try {
484-
Write-Host "Finding module [$ModuleName] in the PowerShell Gallery."
485-
$latest = Find-PSResource -Name $ModuleName -Repository PSGallery -Verbose:$false
486-
Write-Host ($latest | Format-Table | Out-String)
487-
break
488-
} catch {
489-
if ($i -eq $retryCount) {
490-
Write-Warning "Failed to find the module [$ModuleName] in the PowerShell Gallery."
491-
Write-Warning $_.Exception.Message
492-
}
493-
Start-Sleep -Seconds $retryDelaySeconds
481+
Write-Host "Finding module [$ModuleName] in the PowerShell Gallery."
482+
try {
483+
$packages = @(Find-PSResource -Name $ModuleName -Repository PSGallery -ErrorAction Stop)
484+
} catch {
485+
$failure = $_
486+
if ($failure.FullyQualifiedErrorId -notlike 'PackageNotFound,*') {
487+
throw
494488
}
489+
Write-Host "The module [$ModuleName] is absent from the Gallery; using the first-release baseline [0.0.0]."
490+
return (New-PSSemVer -Version '0.0.0')
495491
}
496-
497-
$version = if ($latest.Version) {
498-
New-PSSemVer -Version ($latest.Version).ToString()
499-
} else {
500-
Write-Warning "Could not find module online. Using '0.0.0'."
501-
New-PSSemVer -Version '0.0.0'
492+
if ($packages.Count -ne 1 -or $packages[0].Name -ine $ModuleName -or
493+
[string]::IsNullOrWhiteSpace([string]$packages[0].Version)) {
494+
throw "Cannot verify the latest Gallery version metadata for [$ModuleName]."
502495
}
503-
496+
$version = New-PSSemVer -Version ([string]$packages[0].Version)
504497
Write-Host "PSGallery version: [$($version.ToString())]"
505498
$version
506499
}
@@ -600,9 +593,18 @@ function Get-NextPrereleaseNumber {
600593
Prerelease = $true
601594
Repository = 'PSGallery'
602595
Verbose = $false
603-
ErrorAction = 'SilentlyContinue'
596+
ErrorAction = 'Stop'
597+
}
598+
try {
599+
$galleryVersions = @(Find-PSResource @params)
600+
} catch {
601+
$failure = $_
602+
if ($failure.FullyQualifiedErrorId -notlike 'PackageNotFound,*') {
603+
throw
604+
}
605+
$galleryVersions = @()
604606
}
605-
$matchingPSGalleryPrereleases = Find-PSResource @params |
607+
$matchingPSGalleryPrereleases = $galleryVersions |
606608
Where-Object { "$($_.Version.Major).$($_.Version.Minor).$($_.Version.Build)" -eq $BaseVersion } |
607609
Where-Object { $_.Prerelease -like "$PrereleaseName*" }
608610

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
2+
'PSUseDeclaredVarsMoreThanAssignments', '',
3+
Justification = 'Pester setup state is used in test blocks and command replacements.'
4+
)]
5+
[CmdletBinding()]
6+
param()
7+
8+
$ErrorActionPreference = 'Stop'
9+
10+
BeforeAll {
11+
Import-Module -Name PSModule -Force
12+
Import-Module -Name ([IO.Path]::Combine(
13+
$PSScriptRoot, '..', 'src', 'Resolve-PSModuleVersion.Helpers.psm1'
14+
)) -Force
15+
$script:originalFind = (Get-Item -LiteralPath 'Function:\Find-PSResource' -ErrorAction SilentlyContinue).ScriptBlock
16+
$script:originalSleep = (Get-Item -LiteralPath 'Function:\Start-Sleep' -ErrorAction SilentlyContinue).ScriptBlock
17+
}
18+
19+
AfterAll {
20+
foreach ($name in @('Find-PSResource', 'Start-Sleep')) {
21+
Remove-Item -LiteralPath "Function:\$name" -ErrorAction SilentlyContinue
22+
}
23+
if ($null -ne $script:originalFind) {
24+
Set-Item -Path function:global:Find-PSResource -Value $script:originalFind
25+
}
26+
if ($null -ne $script:originalSleep) {
27+
Set-Item -Path function:global:Start-Sleep -Value $script:originalSleep
28+
}
29+
}
30+
31+
Describe 'Version lookup errors are not version baselines' {
32+
BeforeEach {
33+
$state = @{ Message = ''; ErrorId = ''; Package = $null }
34+
$script:state = $state
35+
Set-Item -Path function:global:Start-Sleep -Value {}
36+
Set-Item -Path function:global:Find-PSResource -Value {
37+
[CmdletBinding()]
38+
param(
39+
[string] $Name,
40+
[string] $Repository,
41+
[string] $Version,
42+
[switch] $Prerelease
43+
)
44+
if ($Name -cne 'TestModule' -or $Repository -cne 'PSGallery' -or
45+
($Version -and ($Version -cne '*' -or -not $Prerelease))) {
46+
throw 'Unexpected Gallery boundary in the version-reader fixture.'
47+
}
48+
if ($state.Message) {
49+
Write-Error -Message $state.Message -ErrorId $state.ErrorId -Category ResourceUnavailable
50+
return
51+
}
52+
$state.Package
53+
}.GetNewClosure()
54+
}
55+
56+
It 'retains verified Gallery package absence as the first-release baseline' {
57+
$script:state.Message = 'The exact module is absent.'
58+
$script:state.ErrorId = 'PackageNotFound'
59+
60+
(Get-LatestPSGalleryVersion -ModuleName TestModule).ToString() | Should -BeExactly '0.0.0'
61+
}
62+
63+
It 'surfaces Gallery authentication and API errors before resolving a publishable version' -ForEach @(
64+
@{ Message = 'Gallery authentication failed (HTTP 401).' }
65+
@{ Message = 'Gallery Service Unavailable (HTTP 503).' }
66+
) {
67+
$script:state.Message = $Message
68+
$script:state.ErrorId = 'HttpRequestCallFailure'
69+
70+
{ Get-LatestPSGalleryVersion -ModuleName TestModule } | Should -Throw
71+
}
72+
73+
It 'rejects a successful lookup with no version metadata rather than returning 0.0.0' {
74+
$script:state.Package = [pscustomobject]@{ Name = 'TestModule'; Version = $null }
75+
76+
{ Get-LatestPSGalleryVersion -ModuleName TestModule } | Should -Throw '*version*'
77+
}
78+
79+
It 'retains the actual stable version without an extra bump policy' {
80+
$script:state.Package = [pscustomobject]@{ Name = 'TestModule'; Version = [version]'1.2.3' }
81+
82+
(Get-LatestPSGalleryVersion -ModuleName TestModule).ToString() | Should -BeExactly '1.2.3'
83+
}
84+
85+
It 'does not interpret a prerelease enumeration outage as counter zero' {
86+
$script:state.Message = 'Gallery enumeration failed (HTTP 503).'
87+
$script:state.ErrorId = 'HttpRequestCallFailure'
88+
$parameters = @{ ModuleName = 'TestModule'; BaseVersion = '1.2.4'; PrereleaseName = 'preview'; Releases = @() }
89+
90+
{ Get-NextPrereleaseNumber @parameters } | Should -Throw
91+
}
92+
}

0 commit comments

Comments
 (0)