Skip to content

Commit 3acf7bb

Browse files
authored
ci: add retry and fail-fast to install-wdk NuGet QFE resolution (#624)
When `install-wdk` receives a 3-part WDK version like `10.0.26100` with NuGet source, it must resolve a QFE suffix because NuGet WDK packages only exist as 4-part versions (e.g. `10.0.26100.1882`). The previous fallback paths silently used the bare 3-part version on API timeout or when no versions matched, guaranteeing a downstream "package not found" failure. This was hit in PR #593 where a 30s NuGet API timeout caused the action to try installing the non-existent `Microsoft.Windows.WDK.x64 10.0.26100`. Replaces the silent fallbacks with a retry loop (3 attempts, exponential backoff) that only retries transient errors (timeouts, network failures, HTTP 429/5xx) and fails fast on non-transient errors like no matching versions for the base version. Includes the last exception message in the final error for easier CI diagnosis.
1 parent 73b7ba9 commit 3acf7bb

1 file changed

Lines changed: 54 additions & 19 deletions

File tree

.github/actions/install-wdk/action.yaml

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -109,28 +109,63 @@ runs:
109109
$sdkVersion = $versionParts[0..2] -join '.'
110110
111111
if ($versionParts.Length -eq 3) {
112-
# No QFE specified, find the latest QFE for this base version
112+
# No QFE specified, find the latest QFE for this base version.
113+
# NuGet WDK packages only exist with 4-part versions (e.g. 10.0.26100.1882),
114+
# so we must resolve a QFE — the bare 3-part version will never exist.
113115
Write-Host "No QFE specified, searching for latest QFE for version $inputVersion..."
114-
try {
115-
Write-Host "Trying NuGet API..."
116-
$nugetApiUrl = "https://api.nuget.org/v3-flatcontainer/microsoft.windows.wdk.x64/index.json"
117-
$response = Invoke-RestMethod -Uri $nugetApiUrl -TimeoutSec 30
118-
$availableVersions = @($response.versions | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' })
119-
Write-Host "Found $(@($availableVersions).Count) versions using NuGet API"
120-
121-
# Filter versions that match the base version and find the latest QFE
122-
$matchingVersions = @($availableVersions | Where-Object { $_.StartsWith("$inputVersion.") })
123-
if (@($matchingVersions).Count -eq 0) {
124-
Write-Warning "No QFE versions found for base version $inputVersion, using base version..."
125-
$version = $inputVersion
126-
} else {
116+
$version = $null
117+
$maxAttempts = 3
118+
$retryDelaySec = 5
119+
$lastError = $null
120+
121+
for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
122+
try {
123+
Write-Host "Querying NuGet API (attempt $attempt of $maxAttempts)..."
124+
$nugetApiUrl = "https://api.nuget.org/v3-flatcontainer/microsoft.windows.wdk.x64/index.json"
125+
$response = Invoke-RestMethod -Uri $nugetApiUrl -TimeoutSec 30
126+
$availableVersions = @($response.versions | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' })
127+
Write-Host "Found $(@($availableVersions).Count) total versions on NuGet"
128+
129+
# API call succeeded — filter for matching QFE versions
130+
$matchingVersions = @($availableVersions | Where-Object { $_.StartsWith("$inputVersion.") })
131+
if (@($matchingVersions).Count -eq 0) {
132+
# Not transient — the API responded but this base version has no packages. Fail immediately.
133+
throw "No QFE versions found on NuGet for base version $inputVersion. NuGet WDK packages require a 4-part version (e.g. $inputVersion.1882)."
134+
}
135+
127136
$version = $matchingVersions | Sort-Object { [System.Version]$_ } | Select-Object -Last 1
128-
Write-Host "Found latest QFE version: $version"
137+
Write-Host "Resolved latest QFE version: $version"
138+
break
139+
} catch {
140+
$lastError = $_
141+
# Only retry transient errors (timeouts, network failures, server errors).
142+
# If the API responded successfully but had no matching versions, fail fast.
143+
$ex = $_.Exception
144+
$statusCode = if ($ex -is [Microsoft.PowerShell.Commands.HttpResponseException] -and $ex.Response) {
145+
try { [int]$ex.Response.StatusCode } catch { $null }
146+
}
147+
$isTransient = $ex -is [System.Net.Http.HttpRequestException] -or
148+
$ex -is [System.Threading.Tasks.TaskCanceledException] -or
149+
$ex -is [System.Net.WebException] -or
150+
$ex -is [System.TimeoutException] -or
151+
$ex.Message -match 'Timeout|timed out' -or
152+
$statusCode -eq 429 -or
153+
($statusCode -ge 500 -and $statusCode -lt 600)
154+
if (-not $isTransient) {
155+
throw
156+
}
157+
Write-Warning "Attempt $attempt failed (transient): $($ex.Message)"
158+
if ($attempt -lt $maxAttempts) {
159+
Write-Host "Retrying in $retryDelaySec seconds..."
160+
Start-Sleep -Seconds $retryDelaySec
161+
$retryDelaySec *= 2
162+
}
129163
}
130-
} catch {
131-
Write-Warning "Failed to query NuGet for latest QFE version: $_"
132-
Write-Host "Using input version $inputVersion without QFE lookup"
133-
$version = $inputVersion
164+
}
165+
166+
if (-not $version) {
167+
$lastErrorMsg = if ($lastError -and $lastError.Exception) { $lastError.Exception.Message } else { $lastError }
168+
throw "Failed to resolve QFE version for $inputVersion after $maxAttempts attempts. NuGet WDK packages only exist with 4-part versions (e.g. $inputVersion.1882). Specify a full version or ensure the NuGet API (https://api.nuget.org) is reachable. Last error: $lastErrorMsg"
134169
}
135170
} else {
136171
$version = $inputVersion

0 commit comments

Comments
 (0)