From 01ea27e73e6c96a3560fccd8ad5c89532adae229 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Mon, 3 Nov 2025 09:23:29 -0500 Subject: [PATCH 1/3] Retry installing Visual Studio Build Tools on failure We've seen occasional failures to install the Visual Studio Build Tools due to flakey responses from the server. Attempt to make this script more robust by adding a retry with exponential backoff, attempting up to 10 times before eventually giving up. This adds the same logic to both the VSB and Swift toolchain downloads. --- .../workflows/scripts/windows/install-vsb.ps1 | 26 +++++++++++++++++-- .../scripts/windows/swift/install-swift.ps1 | 26 +++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/.github/workflows/scripts/windows/install-vsb.ps1 b/.github/workflows/scripts/windows/install-vsb.ps1 index 931cbbb..3be9a24 100644 --- a/.github/workflows/scripts/windows/install-vsb.ps1 +++ b/.github/workflows/scripts/windows/install-vsb.ps1 @@ -14,8 +14,30 @@ $VSB_SHA256='C792BDB0FD46155DE19955269CAC85D52C4C63C23DB2CF43D96B9390146F9390' Set-Variable ErrorActionPreference Stop Set-Variable ProgressPreference SilentlyContinue Write-Host -NoNewLine ('Downloading {0} ... ' -f ${VSB}) -Invoke-WebRequest -Uri $VSB -OutFile $env:TEMP\vs_buildtools.exe -Write-Host 'SUCCESS' +$MaxRetries = 10 +$BaseDelay = 1 +$Attempt = 0 +$Success = $false + +while (-not $Success -and $Attempt -lt $MaxRetries) { + $Attempt++ + try { + Invoke-WebRequest -Uri $VSB -OutFile $env:TEMP\vs_buildtools.exe + $Success = $true + Write-Host 'SUCCESS' + } + catch { + if ($Attempt -eq $MaxRetries) { + Write-Host "FAILED after $MaxRetries attempts: $($_.Exception.Message)" + exit 1 + } + + # Calculate exponential backoff delay (2^attempt * base delay) + $Delay = $BaseDelay * [Math]::Pow(2, $Attempt - 1) + Write-Host "Attempt $Attempt failed, retrying in $Delay seconds..." + Start-Sleep -Seconds $Delay + } +} Write-Host -NoNewLine ('Verifying SHA256 ({0}) ... ' -f $VSB_SHA256) $Hash = Get-FileHash $env:TEMP\vs_buildtools.exe -Algorithm sha256 if ($Hash.Hash -eq $VSB_SHA256) { diff --git a/.github/workflows/scripts/windows/swift/install-swift.ps1 b/.github/workflows/scripts/windows/swift/install-swift.ps1 index 811a53d..6b658f4 100644 --- a/.github/workflows/scripts/windows/swift/install-swift.ps1 +++ b/.github/workflows/scripts/windows/swift/install-swift.ps1 @@ -17,8 +17,30 @@ function Install-Swift { Set-Variable ErrorActionPreference Stop Set-Variable ProgressPreference SilentlyContinue Write-Host -NoNewLine ('Downloading {0} ... ' -f $url) - Invoke-WebRequest -Uri $url -OutFile installer.exe - Write-Host 'SUCCESS' + $MaxRetries = 10 + $BaseDelay = 1 + $Attempt = 0 + $Success = $false + + while (-not $Success -and $Attempt -lt $MaxRetries) { + $Attempt++ + try { + Invoke-WebRequest -Uri $url -OutFile installer.exe + $Success = $true + Write-Host 'SUCCESS' + } + catch { + if ($Attempt -eq $MaxRetries) { + Write-Host "FAILED after $MaxRetries attempts: $($_.Exception.Message)" + exit 1 + } + + # Calculate exponential backoff delay (2^attempt * base delay) + $Delay = $BaseDelay * [Math]::Pow(2, $Attempt - 1) + Write-Host "Attempt $Attempt failed, retrying in $Delay seconds..." + Start-Sleep -Seconds $Delay + } + } Write-Host -NoNewLine ('Verifying SHA256 ({0}) ... ' -f $Sha256) $Hash = Get-FileHash installer.exe -Algorithm sha256 if ($Hash.Hash -eq $Sha256 -or $Sha256 -eq "") { From e64e7a8e2b6547a3a2870bdafb7ff21d7b66c4b4 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Mon, 3 Nov 2025 10:43:13 -0500 Subject: [PATCH 2/3] Move retry logic out to a utility --- .../workflows/scripts/windows/install-vsb.ps1 | 31 +++------ .../scripts/windows/swift/install-swift.ps1 | 31 +++------ .../scripts/windows/web-request-utils.psm1 | 67 +++++++++++++++++++ 3 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/scripts/windows/web-request-utils.psm1 diff --git a/.github/workflows/scripts/windows/install-vsb.ps1 b/.github/workflows/scripts/windows/install-vsb.ps1 index 3be9a24..cdd3ed0 100644 --- a/.github/workflows/scripts/windows/install-vsb.ps1 +++ b/.github/workflows/scripts/windows/install-vsb.ps1 @@ -9,34 +9,19 @@ ## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors ## ##===----------------------------------------------------------------------===## + +Import-Module $PSScriptRoot\web-request-utils.psm1 + $VSB='https://download.visualstudio.microsoft.com/download/pr/5536698c-711c-4834-876f-2817d31a2ef2/c792bdb0fd46155de19955269cac85d52c4c63c23db2cf43d96b9390146f9390/vs_BuildTools.exe' $VSB_SHA256='C792BDB0FD46155DE19955269CAC85D52C4C63C23DB2CF43D96B9390146F9390' Set-Variable ErrorActionPreference Stop Set-Variable ProgressPreference SilentlyContinue Write-Host -NoNewLine ('Downloading {0} ... ' -f ${VSB}) -$MaxRetries = 10 -$BaseDelay = 1 -$Attempt = 0 -$Success = $false - -while (-not $Success -and $Attempt -lt $MaxRetries) { - $Attempt++ - try { - Invoke-WebRequest -Uri $VSB -OutFile $env:TEMP\vs_buildtools.exe - $Success = $true - Write-Host 'SUCCESS' - } - catch { - if ($Attempt -eq $MaxRetries) { - Write-Host "FAILED after $MaxRetries attempts: $($_.Exception.Message)" - exit 1 - } - - # Calculate exponential backoff delay (2^attempt * base delay) - $Delay = $BaseDelay * [Math]::Pow(2, $Attempt - 1) - Write-Host "Attempt $Attempt failed, retrying in $Delay seconds..." - Start-Sleep -Seconds $Delay - } +try { + Invoke-WebRequestWithRetry -Uri $VSB -OutFile $env:TEMP\vs_buildtools.exe +} +catch { + exit 1 } Write-Host -NoNewLine ('Verifying SHA256 ({0}) ... ' -f $VSB_SHA256) $Hash = Get-FileHash $env:TEMP\vs_buildtools.exe -Algorithm sha256 diff --git a/.github/workflows/scripts/windows/swift/install-swift.ps1 b/.github/workflows/scripts/windows/swift/install-swift.ps1 index 6b658f4..901aa17 100644 --- a/.github/workflows/scripts/windows/swift/install-swift.ps1 +++ b/.github/workflows/scripts/windows/swift/install-swift.ps1 @@ -9,6 +9,9 @@ ## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors ## ##===----------------------------------------------------------------------===## + +Import-Module $PSScriptRoot\..\web-request-utils.psm1 + function Install-Swift { param ( [string]$Url, @@ -17,29 +20,11 @@ function Install-Swift { Set-Variable ErrorActionPreference Stop Set-Variable ProgressPreference SilentlyContinue Write-Host -NoNewLine ('Downloading {0} ... ' -f $url) - $MaxRetries = 10 - $BaseDelay = 1 - $Attempt = 0 - $Success = $false - - while (-not $Success -and $Attempt -lt $MaxRetries) { - $Attempt++ - try { - Invoke-WebRequest -Uri $url -OutFile installer.exe - $Success = $true - Write-Host 'SUCCESS' - } - catch { - if ($Attempt -eq $MaxRetries) { - Write-Host "FAILED after $MaxRetries attempts: $($_.Exception.Message)" - exit 1 - } - - # Calculate exponential backoff delay (2^attempt * base delay) - $Delay = $BaseDelay * [Math]::Pow(2, $Attempt - 1) - Write-Host "Attempt $Attempt failed, retrying in $Delay seconds..." - Start-Sleep -Seconds $Delay - } + try { + Invoke-WebRequestWithRetry -Uri $url -OutFile installer.exe + } + catch { + exit 1 } Write-Host -NoNewLine ('Verifying SHA256 ({0}) ... ' -f $Sha256) $Hash = Get-FileHash installer.exe -Algorithm sha256 diff --git a/.github/workflows/scripts/windows/web-request-utils.psm1 b/.github/workflows/scripts/windows/web-request-utils.psm1 new file mode 100644 index 0000000..4e0a1b0 --- /dev/null +++ b/.github/workflows/scripts/windows/web-request-utils.psm1 @@ -0,0 +1,67 @@ +# WebRequestUtils.psm1 +# Shared utilities for web requests with retry logic + +<# +.SYNOPSIS +Invokes a web request with retry logic and exponential backoff. + +.DESCRIPTION +Attempts to download a file from a URL with automatic retry on failure. +Uses exponential backoff to handle transient network failures. + +.PARAMETER Uri +The URL to download from. + +.PARAMETER OutFile +The destination file path for the download. + +.PARAMETER MaxRetries +Maximum number of retry attempts (default: 10). + +.PARAMETER BaseDelay +Base delay in seconds for exponential backoff (default: 1). + +.EXAMPLE +Invoke-WebRequestWithRetry -Uri "https://example.com/file.exe" -OutFile "file.exe" + +.EXAMPLE +Invoke-WebRequestWithRetry -Uri "https://example.com/file.exe" -OutFile "file.exe" -MaxRetries 5 -BaseDelay 2 +#> +function Invoke-WebRequestWithRetry { + param( + [Parameter(Mandatory=$true)] + [string]$Uri, + + [Parameter(Mandatory=$true)] + [string]$OutFile, + + [int]$MaxRetries = 10, + + [int]$BaseDelay = 1 + ) + + $Attempt = 0 + $Success = $false + + while (-not $Success -and $Attempt -lt $MaxRetries) { + $Attempt++ + try { + Invoke-WebRequest -Uri $Uri -OutFile $OutFile + $Success = $true + Write-Host 'SUCCESS' + } + catch { + if ($Attempt -eq $MaxRetries) { + Write-Host "FAILED after $MaxRetries attempts: $($_.Exception.Message)" + throw + } + + # Calculate exponential backoff delay (2^attempt * base delay) + $Delay = $BaseDelay * [Math]::Pow(2, $Attempt - 1) + Write-Host "Attempt $Attempt failed, retrying in $Delay seconds..." + Start-Sleep -Seconds $Delay + } + } +} + +Export-ModuleMember -Function Invoke-WebRequestWithRetry From 6e1cdb2f767ba4853a9855792abfd395109107ea Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Mon, 3 Nov 2025 11:47:23 -0500 Subject: [PATCH 3/3] Add licence header to web-request-utils.psm1 --- .../scripts/windows/web-request-utils.psm1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/scripts/windows/web-request-utils.psm1 b/.github/workflows/scripts/windows/web-request-utils.psm1 index 4e0a1b0..7e3f49b 100644 --- a/.github/workflows/scripts/windows/web-request-utils.psm1 +++ b/.github/workflows/scripts/windows/web-request-utils.psm1 @@ -1,4 +1,15 @@ -# WebRequestUtils.psm1 +##===----------------------------------------------------------------------===## +## +## This source file is part of the Swift.org open source project +## +## Copyright (c) 2024 Apple Inc. and the Swift project authors +## Licensed under Apache License v2.0 with Runtime Library Exception +## +## See https://swift.org/LICENSE.txt for license information +## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +## +##===----------------------------------------------------------------------===## + # Shared utilities for web requests with retry logic <# @@ -31,12 +42,9 @@ function Invoke-WebRequestWithRetry { param( [Parameter(Mandatory=$true)] [string]$Uri, - [Parameter(Mandatory=$true)] [string]$OutFile, - [int]$MaxRetries = 10, - [int]$BaseDelay = 1 )