Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 95 additions & 22 deletions .github/workflows/scripts/install-and-build-with-sdk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,91 @@ log() { printf -- "** %s\n" "$*" >&2; }
error() { printf -- "** ERROR: %s\n" "$*" >&2; }
fatal() { error "$@"; exit 1; }

# Retry configuration
CURL_MAX_RETRIES=5
CURL_RETRY_DELAY=5
CURL_TIMEOUT=300

SDK_INSTALL_MAX_RETRIES=5
SDK_INSTALL_INITIAL_RETRY_DELAY=10

curl_with_retry() {
local attempt=1
local exit_code=0

while [ $attempt -le $CURL_MAX_RETRIES ]; do
if [ $attempt -gt 1 ]; then
log "Retry attempt $attempt of $CURL_MAX_RETRIES after ${CURL_RETRY_DELAY}s delay..."
sleep $CURL_RETRY_DELAY
fi

# Run curl with connection timeout and max time limits
if curl --connect-timeout 30 --max-time $CURL_TIMEOUT --retry 3 --retry-delay 2 --retry-max-time 60 "$@"; then
return 0
else
exit_code=$?
log "curl failed with exit code $exit_code on attempt $attempt"
fi

attempt=$((attempt + 1))
done

error "curl failed after $CURL_MAX_RETRIES attempts"
return $exit_code
}

swift_sdk_install_with_retry() {
local swift_executable="$1"
local sdk_url="$2"
local checksum="$3"
local sdk_type="$4"

local attempt=1
local retry_delay=$SDK_INSTALL_INITIAL_RETRY_DELAY

# Extract SDK name from URL for checking if already installed
local sdk_filename
sdk_filename=$(basename "$sdk_url")
local sdk_name="${sdk_filename%.tar.gz}"

while [ $attempt -le $SDK_INSTALL_MAX_RETRIES ]; do
if [ $attempt -gt 1 ]; then
log "Retry attempt $attempt of $SDK_INSTALL_MAX_RETRIES for ${sdk_type} SDK installation after ${retry_delay}s delay..."

# Before retrying, check if SDK was partially installed and remove it
log "Checking for partially installed SDK..."
if "$swift_executable" sdk list 2>/dev/null | grep -q "^${sdk_name}"; then
log "Found partially installed SDK, attempting to remove it..."
if "$swift_executable" sdk remove "$sdk_name" 2>/dev/null; then
log "Successfully removed partially installed SDK"
else
log "Warning: Failed to remove partially installed SDK, continuing anyway..."
fi
fi

sleep $retry_delay
fi

log "Attempt $attempt: Installing ${sdk_type} SDK from ${sdk_url}"

if "$swift_executable" sdk install "$sdk_url" --checksum "$checksum"; then
log "✅ ${sdk_type} SDK installed successfully"
return 0
else
local exit_code=$?
log "swift sdk install failed with exit code $exit_code on attempt $attempt"

# Exponential backoff: double the delay each time
retry_delay=$((retry_delay * 2))
fi

attempt=$((attempt + 1))
done

error "${sdk_type} SDK installation failed after $SDK_INSTALL_MAX_RETRIES attempts"
return 1
}

# Parse command line options
INSTALL_ANDROID=false
INSTALL_STATIC_LINUX=false
Expand Down Expand Up @@ -125,7 +210,7 @@ find_latest_swift_version() {
log "Fetching releases from swift.org API..."

local releases_json
releases_json=$(curl -fsSL "${SWIFT_API_INSTALL_ROOT}/releases.json") || fatal "Failed to fetch Swift releases"
releases_json=$(curl_with_retry -fsSL "${SWIFT_API_INSTALL_ROOT}/releases.json") || fatal "Failed to fetch Swift releases"

# Find all releases that start with the minor version (e.g, "6.1")
# Sort them and get the latest one
Expand Down Expand Up @@ -212,7 +297,7 @@ find_latest_sdk_snapshot() {
log "Fetching development snapshots from swift.org API..."

local sdk_json
sdk_json=$(curl -fsSL "${SWIFT_API_INSTALL_ROOT}/dev/${version}/${sdk_name}-sdk.json") || fatal "Failed to fetch ${sdk_name}-sdk development snapshots"
sdk_json=$(curl_with_retry -fsSL "${SWIFT_API_INSTALL_ROOT}/dev/${version}/${sdk_name}-sdk.json") || fatal "Failed to fetch ${sdk_name}-sdk development snapshots"

# Extract the snapshot tag from the "dir" field of the first (newest) element
local snapshot_tag
Expand Down Expand Up @@ -400,16 +485,16 @@ download_and_verify() {
local temp_sig="${output_file}.sig"

log "Downloading ${url}"
curl -fsSL "$url" -o "$output_file"
curl_with_retry -fsSL "$url" -o "$output_file"

log "Downloading signature"
curl -fsSL "$sig_url" -o "$temp_sig"
curl_with_retry -fsSL "$sig_url" -o "$temp_sig"

log "Setting up GPG for verification"
local gnupghome
gnupghome="$(mktemp -d)"
export GNUPGHOME="$gnupghome"
curl -fSsL https://swift.org/keys/all-keys.asc | zcat -f | gpg --import - >/dev/null 2>&1
curl_with_retry -fSsL https://swift.org/keys/all-keys.asc | zcat -f | gpg --import - >/dev/null 2>&1

log "Verifying signature"
if gpg --batch --verify "$temp_sig" "$output_file" >/dev/null 2>&1; then
Expand Down Expand Up @@ -445,7 +530,7 @@ download_and_extract_toolchain() {

# Check if toolchain is available
local http_code
http_code=$(curl -sSL --head -w "%{http_code}" -o /dev/null "$toolchain_url")
http_code=$(curl_with_retry -sSL --head -w "%{http_code}" -o /dev/null "$toolchain_url")
if [[ "$http_code" == "404" ]]; then
log "Toolchain not found: ${toolchain_filename}"
log "Exiting workflow..."
Expand Down Expand Up @@ -556,11 +641,7 @@ install_android_sdk() {
local android_sdk_filename="${android_sdk_bundle_name}.tar.gz"
local sdk_url="${ANDROID_SDK_DOWNLOAD_ROOT}/${ANDROID_SDK_TAG}/${android_sdk_filename}"

log "Running: ${SWIFT_EXECUTABLE_FOR_ANDROID_SDK} sdk install ${sdk_url} --checksum ${ANDROID_SDK_CHECKSUM}"

if "$SWIFT_EXECUTABLE_FOR_ANDROID_SDK" sdk install "$sdk_url" --checksum "$ANDROID_SDK_CHECKSUM"; then
log "✅ Android Swift SDK installed successfully"
else
if ! swift_sdk_install_with_retry "$SWIFT_EXECUTABLE_FOR_ANDROID_SDK" "$sdk_url" "$ANDROID_SDK_CHECKSUM" "Android Swift"; then
fatal "Failed to install Android Swift SDK"
fi

Expand All @@ -579,7 +660,7 @@ install_android_sdk() {
if [[ ! -d "${ANDROID_NDK_HOME:-}" ]]; then
# permit the "--android-ndk" flag to override the default
local android_ndk_version="${ANDROID_NDK_VERSION:-r27d}"
curl -fsSL -o ndk.zip --retry 3 https://dl.google.com/android/repository/android-ndk-"${android_ndk_version}"-"$(uname -s)".zip
curl_with_retry -fsSL -o ndk.zip https://dl.google.com/android/repository/android-ndk-"${android_ndk_version}"-"$(uname -s)".zip
command -v unzip >/dev/null || install_package unzip
unzip -q ndk.zip
rm ndk.zip
Expand All @@ -602,11 +683,7 @@ install_static_linux_sdk() {
local static_linux_sdk_filename="${STATIC_LINUX_SDK_TAG}_static-linux-0.0.1.artifactbundle.tar.gz"
local sdk_url="${STATIC_LINUX_SDK_DOWNLOAD_ROOT}/${STATIC_LINUX_SDK_TAG}/${static_linux_sdk_filename}"

log "Running: ${SWIFT_EXECUTABLE_FOR_STATIC_LINUX_SDK} sdk install ${sdk_url} --checksum ${STATIC_LINUX_SDK_CHECKSUM}"

if "$SWIFT_EXECUTABLE_FOR_STATIC_LINUX_SDK" sdk install "$sdk_url" --checksum "$STATIC_LINUX_SDK_CHECKSUM"; then
log "✅ Static Linux Swift SDK installed successfully"
else
if ! swift_sdk_install_with_retry "$SWIFT_EXECUTABLE_FOR_STATIC_LINUX_SDK" "$sdk_url" "$STATIC_LINUX_SDK_CHECKSUM" "Static Linux Swift"; then
fatal "Failed to install Static Linux Swift SDK"
fi

Expand All @@ -625,11 +702,7 @@ install_wasm_sdk() {
local wasm_sdk_filename="${WASM_SDK_TAG}_wasm.artifactbundle.tar.gz"
local sdk_url="${WASM_SDK_DOWNLOAD_ROOT}/${WASM_SDK_TAG}/${wasm_sdk_filename}"

log "Running: ${SWIFT_EXECUTABLE_FOR_WASM_SDK} sdk install ${sdk_url} --checksum ${WASM_SDK_CHECKSUM}"

if "$SWIFT_EXECUTABLE_FOR_WASM_SDK" sdk install "$sdk_url" --checksum "$WASM_SDK_CHECKSUM"; then
log "✅ Swift SDK for Wasm installed successfully"
else
if ! swift_sdk_install_with_retry "$SWIFT_EXECUTABLE_FOR_WASM_SDK" "$sdk_url" "$WASM_SDK_CHECKSUM" "Swift Wasm"; then
fatal "Failed to install Swift SDK for Wasm"
fi

Expand Down
98 changes: 95 additions & 3 deletions .github/workflows/scripts/windows/swift/install-swift.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,108 @@
## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
##
##===----------------------------------------------------------------------===##

# Retry configuration
$MaxRetries = 5
$RetryDelay = 5

function Invoke-WebRequestWithRetry {
param (
[string]$Uri,
[string]$OutFile,
[int]$TimeoutSec = 300
)

$attempt = 1

while ($attempt -le $MaxRetries) {
try {
if ($attempt -gt 1) {
Write-Host "Retry attempt $attempt of $MaxRetries after ${RetryDelay}s delay..."
Start-Sleep -Seconds $RetryDelay
}

Write-Host "Attempt $attempt`: Downloading from $Uri"

# Clean up any existing partial download
if (Test-Path $OutFile) {
Remove-Item -Force $OutFile -ErrorAction SilentlyContinue
}

# Get expected file size from HTTP headers
$headRequest = Invoke-WebRequest -Uri $Uri -Method Head -UseBasicParsing -TimeoutSec 30
$expectedSize = $null
if ($headRequest.Headers.ContainsKey('Content-Length')) {
$expectedSize = [long]$headRequest.Headers['Content-Length'][0]
Write-Host "Expected file size: $($expectedSize / 1MB) MB"
}

# Download with progress tracking disabled for better performance
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($Uri, $OutFile)
$webClient.Dispose()

# Verify file exists and has content
if (-not (Test-Path $OutFile)) {
throw "Download completed but file not found at $OutFile"
}

$actualSize = (Get-Item $OutFile).Length
Write-Host "Downloaded file size: $($actualSize / 1MB) MB"

# Verify file size matches expected size
if ($expectedSize -and $actualSize -ne $expectedSize) {
throw "File size mismatch. Expected: $expectedSize bytes, Got: $actualSize bytes"
}

# Verify file is not corrupted by checking if it's a valid PE executable
$fileBytes = [System.IO.File]::ReadAllBytes($OutFile)
if ($fileBytes.Length -lt 2 -or $fileBytes[0] -ne 0x4D -or $fileBytes[1] -ne 0x5A) {
throw "Downloaded file is not a valid executable (missing MZ header)"
}

Write-Host "Download completed and verified successfully"
return $true
}
catch {
Write-Host "Download failed on attempt $attempt`: $($_.Exception.Message)"

# Clean up partial download if it exists
if (Test-Path $OutFile) {
Remove-Item -Force $OutFile -ErrorAction SilentlyContinue
}

if ($attempt -eq $MaxRetries) {
Write-Host "Download failed after $MaxRetries attempts"
throw
}
}

$attempt++
}

return $false
}

function Install-Swift {
param (
[string]$Url,
[string]$Sha256
)
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'

Write-Host "Downloading $Url ... "

try {
Invoke-WebRequestWithRetry -Uri $Url -OutFile installer.exe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also use this in .github/workflows/scripts/windows/install-vsb.ps1 to download the visual studio build tools

Write-Host 'SUCCESS'
}
catch {
Write-Host "FAILED: $($_.Exception.Message)"
exit 1
}

Write-Host -NoNewLine ('Verifying SHA256 ({0}) ... ' -f $Sha256)
$Hash = Get-FileHash installer.exe -Algorithm sha256
if ($Hash.Hash -eq $Sha256 -or $Sha256 -eq "") {
Expand Down