Make the PowerShell Gallery publish step idempotent
The publish job failed on PR #19 with a 409 for version 2026.7.30.2309, but that version is live on the gallery (created 23:10:04) - the push landed and the client still surfaced an error, so the retry collided with the upload that had just succeeded. The run went red over a publish that actually worked. The step now checks the gallery before pushing and skips when the version is already there, and on a publish error it re-checks before failing. This mirrors the release job, which already looks for an existing tag and skips. Version comparison normalizes each segment the way NuGet does, since the manifest carries zero-padded segments (2026.07.30.2309) while the gallery lists the stripped form (2026.7.30.2309); comparing the raw strings would never match and the guard would never fire. Verified: both workflows still parse as YAML with the same three jobs, the normalizer reproduces the gallery form for four published versions, and a live lookup confirms the guard would have exited 0 on the run that failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -319,9 +319,65 @@ jobs:
|
||||
run: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$moduleDir = Join-Path $PWD 'Module/PSInfisicalAPI'
|
||||
Write-Host "Publishing module from: $moduleDir"
|
||||
Publish-PSResource `
|
||||
-Path $moduleDir `
|
||||
-Repository PSGallery `
|
||||
-ApiKey $env:PSGALLERY_API_KEY `
|
||||
-Verbose
|
||||
$manifest = Test-ModuleManifest -Path (Join-Path $moduleDir 'PSInfisicalAPI.psd1')
|
||||
$version = $manifest.Version.ToString()
|
||||
|
||||
# NuGet strips leading zeros from each segment, so the manifest's 2026.07.30.2309 is listed on the
|
||||
# gallery as 2026.7.30.2309. Compare on the normalized form or every lookup misses.
|
||||
function Get-NormalizedVersion {
|
||||
param([string]$Value)
|
||||
$parts = $Value -split '\.'
|
||||
$normalized = foreach ($part in $parts) {
|
||||
$number = 0
|
||||
if ([int]::TryParse($part, [ref]$number)) { $number.ToString([System.Globalization.CultureInfo]::InvariantCulture) } else { $part }
|
||||
}
|
||||
return ($normalized -join '.')
|
||||
}
|
||||
|
||||
function Test-PublishedVersion {
|
||||
param([string]$Normalized)
|
||||
$uri = "https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSInfisicalAPI'&`$select=Version"
|
||||
try {
|
||||
$feed = Invoke-RestMethod -Uri $uri -TimeoutSec 120
|
||||
} catch {
|
||||
Write-Host "==> Could not query the gallery for existing versions: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
foreach ($entry in @($feed)) {
|
||||
$candidate = $entry.properties.Version
|
||||
if ([string]::IsNullOrWhiteSpace($candidate)) { continue }
|
||||
if ((Get-NormalizedVersion -Value $candidate) -eq $Normalized) { return $true }
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
$normalizedVersion = Get-NormalizedVersion -Value $version
|
||||
Write-Host "==> Module version : $version (gallery form: $normalizedVersion)"
|
||||
Write-Host "==> Publishing from: $moduleDir"
|
||||
|
||||
if (Test-PublishedVersion -Normalized $normalizedVersion) {
|
||||
Write-Host "==> Version $normalizedVersion is already on the PowerShell Gallery; nothing to publish."
|
||||
exit 0
|
||||
}
|
||||
|
||||
try {
|
||||
Publish-PSResource `
|
||||
-Path $moduleDir `
|
||||
-Repository PSGallery `
|
||||
-ApiKey $env:PSGALLERY_API_KEY `
|
||||
-Verbose
|
||||
Write-Host "==> Published $normalizedVersion to the PowerShell Gallery."
|
||||
} catch {
|
||||
# A push can be accepted by the gallery and still surface as an error here; when that happens the
|
||||
# retry comes back as 409. Re-check before failing the run over an upload that actually landed.
|
||||
Write-Host "==> Publish-PSResource reported: $($_.Exception.Message)"
|
||||
Start-Sleep -Seconds 15
|
||||
if (Test-PublishedVersion -Normalized $normalizedVersion) {
|
||||
Write-Host "==> Version $normalizedVersion is present on the gallery; treating the publish as successful."
|
||||
exit 0
|
||||
}
|
||||
|
||||
throw
|
||||
}
|
||||
|
||||
@@ -320,9 +320,65 @@ jobs:
|
||||
run: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$moduleDir = Join-Path $PWD 'Module/PSInfisicalAPI'
|
||||
Write-Host "Publishing module from: $moduleDir"
|
||||
Publish-PSResource `
|
||||
-Path $moduleDir `
|
||||
-Repository PSGallery `
|
||||
-ApiKey $env:PSGALLERY_API_KEY `
|
||||
-Verbose
|
||||
$manifest = Test-ModuleManifest -Path (Join-Path $moduleDir 'PSInfisicalAPI.psd1')
|
||||
$version = $manifest.Version.ToString()
|
||||
|
||||
# NuGet strips leading zeros from each segment, so the manifest's 2026.07.30.2309 is listed on the
|
||||
# gallery as 2026.7.30.2309. Compare on the normalized form or every lookup misses.
|
||||
function Get-NormalizedVersion {
|
||||
param([string]$Value)
|
||||
$parts = $Value -split '\.'
|
||||
$normalized = foreach ($part in $parts) {
|
||||
$number = 0
|
||||
if ([int]::TryParse($part, [ref]$number)) { $number.ToString([System.Globalization.CultureInfo]::InvariantCulture) } else { $part }
|
||||
}
|
||||
return ($normalized -join '.')
|
||||
}
|
||||
|
||||
function Test-PublishedVersion {
|
||||
param([string]$Normalized)
|
||||
$uri = "https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSInfisicalAPI'&`$select=Version"
|
||||
try {
|
||||
$feed = Invoke-RestMethod -Uri $uri -TimeoutSec 120
|
||||
} catch {
|
||||
Write-Host "==> Could not query the gallery for existing versions: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
foreach ($entry in @($feed)) {
|
||||
$candidate = $entry.properties.Version
|
||||
if ([string]::IsNullOrWhiteSpace($candidate)) { continue }
|
||||
if ((Get-NormalizedVersion -Value $candidate) -eq $Normalized) { return $true }
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
$normalizedVersion = Get-NormalizedVersion -Value $version
|
||||
Write-Host "==> Module version : $version (gallery form: $normalizedVersion)"
|
||||
Write-Host "==> Publishing from: $moduleDir"
|
||||
|
||||
if (Test-PublishedVersion -Normalized $normalizedVersion) {
|
||||
Write-Host "==> Version $normalizedVersion is already on the PowerShell Gallery; nothing to publish."
|
||||
exit 0
|
||||
}
|
||||
|
||||
try {
|
||||
Publish-PSResource `
|
||||
-Path $moduleDir `
|
||||
-Repository PSGallery `
|
||||
-ApiKey $env:PSGALLERY_API_KEY `
|
||||
-Verbose
|
||||
Write-Host "==> Published $normalizedVersion to the PowerShell Gallery."
|
||||
} catch {
|
||||
# A push can be accepted by the gallery and still surface as an error here; when that happens the
|
||||
# retry comes back as 409. Re-check before failing the run over an upload that actually landed.
|
||||
Write-Host "==> Publish-PSResource reported: $($_.Exception.Message)"
|
||||
Start-Sleep -Seconds 15
|
||||
if (Test-PublishedVersion -Normalized $normalizedVersion) {
|
||||
Write-Host "==> Version $normalizedVersion is present on the gallery; treating the publish as successful."
|
||||
exit 0
|
||||
}
|
||||
|
||||
throw
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user