Skip to content

Commit 38ac1f1

Browse files
Marius StorhaugCopilot
authored andcommitted
Narrow the Gallery existence probe to PackageNotFound
Addresses review feedback on #528: -ErrorAction SilentlyContinue swallowed every Find-PSResource failure, not just 'version not found'. During a Gallery outage the probe returned null, publish was attempted anyway, and an already-published version failed the upload with a conflict - losing the resume behaviour the probe exists to provide. The probe now runs with -ErrorAction Stop and only treats the PackageNotFound error ID as 'not yet published'. Any other error stays fatal. Also fixed the test harness, which could not observe whether publish ran: - The $script:publishInvoked flag was never set, because the publish script runs in its own scope. Both existing assertions were vacuous. Replaced with a hashtable captured by GetNewClosure(), shared by reference. - The marker-file variant depended on $env:GITHUB_WORKSPACE, which is process-wide and races between parallel Pester runspaces. - The not-found shim used ThrowTerminatingError, which ignores -ErrorAction and so could not distinguish the fix from the bug. It now uses Write-Error with the real PackageNotFound error ID. Added a case asserting a non-PackageNotFound lookup failure stays fatal and does not publish. Verified each test fails against the defect it guards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 7019cd9 commit 38ac1f1

2 files changed

Lines changed: 45 additions & 11 deletions

File tree

.github/actions/Publish-PSModule/src/publish.ps1

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,18 @@ LogGroup 'Publish to PSGallery' {
145145
if ($whatIf) {
146146
Write-Host "Publish-PSResource -Path $modulePath -Repository PSGallery -ApiKey ***"
147147
} else {
148-
$publishedPackage = Find-PSResource -Name $name -Version $publishPSVersion -Repository PSGallery -ErrorAction SilentlyContinue
148+
# A version that is not on the Gallery is the expected state for a new release, but PSResourceGet
149+
# reports it as a PackageNotFound error, which -ErrorAction Stop turns into a throw. Only that error
150+
# may be treated as 'not published'; any other failure (for example a Gallery outage) must stay fatal,
151+
# otherwise a version that already exists would be re-published and fail the upload with a conflict.
152+
$publishedPackage = $null
153+
try {
154+
$publishedPackage = Find-PSResource -Name $name -Version $publishPSVersion -Repository PSGallery -ErrorAction Stop
155+
} catch {
156+
if ($_.FullyQualifiedErrorId -notlike 'PackageNotFound,*') { throw }
157+
Write-Host "$name $publishPSVersion is not on the PowerShell Gallery yet."
158+
}
159+
149160
if ($publishedPackage) {
150161
Write-Host (
151162
"::notice title=♻️ Resuming Gallery-only publication::$name $publishPSVersion is already " +

.github/actions/Publish-PSModule/tests/Publish-PSModule.Recovery.Tests.ps1

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,25 +70,28 @@ Describe 'Publish-PSModule recovery' {
7070
$env:PSMODULE_PUBLISH_PSMODULE_INPUT_PSGALLERY_API_KEY = 'test-key'
7171
$env:PSMODULE_PUBLISH_PSMODULE_INPUT_PullRequest = ''
7272
$env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf = 'false'
73-
$script:publishInvoked = $false
73+
# The publish script runs in its own scope, so a $script: flag set inside a shim never reaches the
74+
# test. A hashtable captured by GetNewClosure() is shared by reference and records the call reliably.
75+
# The closure captures local variables only, so $calls must be local here, not $script:-qualified.
76+
$calls = @{ PublishInvoked = $false }
77+
$script:calls = $calls
7478

7579
Set-Item -Path function:global:Resolve-PSModuleDependency -Value {}
7680
Set-Item -Path function:global:Find-PSResource -Value {
7781
[PSCustomObject]@{ Name = 'TestModule'; Version = '1.2.4' }
7882
}
7983
Set-Item -Path function:global:Publish-PSResource -Value {
80-
$script:publishInvoked = $true
81-
}
84+
$calls.PublishInvoked = $true
85+
}.GetNewClosure()
8286
}
8387

8488
It 'skips Gallery publication when the resolved version already exists' {
8589
{ & $script:publishScriptPath } | Should -Not -Throw
8690

87-
$script:publishInvoked | Should -BeFalse
91+
$script:calls.PublishInvoked | Should -BeFalse
8892
}
8993

9094
It 'publishes when the resolved version is not in the Gallery' {
91-
$publishMarkerPath = Join-Path -Path $script:workspacePath -ChildPath 'publish-invoked'
9295
Set-Item -Path function:global:Find-PSResource -Value {
9396
[CmdletBinding()]
9497
param(
@@ -97,14 +100,34 @@ Describe 'Publish-PSModule recovery' {
97100
[string] $Repository
98101
)
99102

100-
Write-Error "Package with name '$Name', version '$Version' could not be found in repository '$Repository'."
101-
}
102-
Set-Item -Path function:global:Publish-PSResource -Value {
103-
$null = New-Item -Path (Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath 'publish-invoked') -ItemType File -Force
103+
# Mirrors how PSResourceGet reports an absent version: an error with the PackageNotFound error ID
104+
# that honours -ErrorAction, so the shim reacts to -ErrorAction the same way the real cmdlet does.
105+
Write-Error -Message "Package with name '$Name', version '$Version' could not be found in repository '$Repository'." `
106+
-ErrorId 'PackageNotFound' -Category ObjectNotFound -TargetObject $Name
104107
}
105108

106109
{ & $script:publishScriptPath } | Should -Not -Throw
107110

108-
(Test-Path -Path $publishMarkerPath) | Should -BeTrue
111+
$script:calls.PublishInvoked | Should -BeTrue
112+
}
113+
114+
It 'fails without publishing when the Gallery lookup errors for another reason' {
115+
Set-Item -Path function:global:Find-PSResource -Value {
116+
[CmdletBinding()]
117+
param(
118+
[string] $Name,
119+
[string] $Version,
120+
[string] $Repository
121+
)
122+
123+
# A transient Gallery failure carries a different error ID and must not be mistaken for
124+
# 'version not published', otherwise an already-published version would be re-uploaded.
125+
Write-Error -Message "Error occured while trying to find '$Name' '$Version' in repository '$Repository': Service Unavailable" `
126+
-ErrorId 'HttpRequestCallFailure' -Category ResourceUnavailable -TargetObject $Name
127+
}
128+
129+
{ & $script:publishScriptPath } | Should -Throw
130+
131+
$script:calls.PublishInvoked | Should -BeFalse
109132
}
110133
}

0 commit comments

Comments
 (0)