|
| 1 | +<# |
| 2 | +.SYNOPSIS |
| 3 | + Verifies that symbols (PDBs) for a single DLL are available on a symbol server. |
| 4 | +
|
| 5 | +.DESCRIPTION |
| 6 | + This script is called by the validate-symbols-job.yml Azure Pipelines job, |
| 7 | + once per package per symbol server. It: |
| 8 | + 1. Locates the .nupkg file in the downloaded artifact directory. |
| 9 | + 2. Extracts the package contents (skipped if already extracted). |
| 10 | + 3. Runs symchk.exe to verify that matching PDBs are available on the |
| 11 | + specified symbol server. |
| 12 | +
|
| 13 | + The script exits with a non-zero exit code if verification fails. |
| 14 | +
|
| 15 | +.PARAMETER ArtifactPath |
| 16 | + The directory containing the downloaded pipeline artifact (.nupkg files). |
| 17 | +
|
| 18 | +.PARAMETER ExtractPath |
| 19 | + The directory where the package will be extracted. |
| 20 | +
|
| 21 | +.PARAMETER PackageName |
| 22 | + The NuGet package name prefix used to locate the .nupkg file |
| 23 | + (e.g. "Microsoft.Data.SqlClient"). |
| 24 | +
|
| 25 | +.PARAMETER DllPath |
| 26 | + The relative path to the DLL inside the extracted package |
| 27 | + (e.g. "lib\net8.0\Microsoft.Data.SqlClient.dll"). |
| 28 | +
|
| 29 | +.PARAMETER SymbolServerUrl |
| 30 | + The symbol server URL that symchk can query |
| 31 | + (e.g. "https://msdl.microsoft.com/download/symbols"). |
| 32 | +
|
| 33 | +.PARAMETER SymbolServerName |
| 34 | + A friendly display name for the symbol server, used in log output. |
| 35 | +
|
| 36 | +.EXAMPLE |
| 37 | + .\validate-symbols.ps1 ` |
| 38 | + -ArtifactPath "C:\agent\_work\1\drop_SqlClient" ` |
| 39 | + -ExtractPath "C:\agent\_work\1\s\symchk_packages\Microsoft.Data.SqlClient" ` |
| 40 | + -PackageName "Microsoft.Data.SqlClient" ` |
| 41 | + -DllPath "lib\net8.0\Microsoft.Data.SqlClient.dll" ` |
| 42 | + -SymbolServerUrl "https://msdl.microsoft.com/download/symbols" ` |
| 43 | + -SymbolServerName "MSDL (Public)" |
| 44 | +#> |
| 45 | + |
| 46 | +[CmdletBinding()] |
| 47 | +param( |
| 48 | + [Parameter(Mandatory)] |
| 49 | + [string]$ArtifactPath, |
| 50 | + |
| 51 | + [Parameter(Mandatory)] |
| 52 | + [string]$ExtractPath, |
| 53 | + |
| 54 | + [Parameter(Mandatory)] |
| 55 | + [string]$PackageName, |
| 56 | + |
| 57 | + [Parameter(Mandatory)] |
| 58 | + [string]$DllPath, |
| 59 | + |
| 60 | + [Parameter(Mandatory)] |
| 61 | + [string]$SymbolServerUrl, |
| 62 | + |
| 63 | + [Parameter(Mandatory)] |
| 64 | + [string]$SymbolServerName, |
| 65 | + |
| 66 | + # Maximum number of attempts when symbols are not yet available. The first |
| 67 | + # attempt runs immediately; subsequent attempts wait RetryIntervalSeconds |
| 68 | + # between them. Defaults to 10 (~5 minutes total with default interval). |
| 69 | + [int]$MaxRetries = 10, |
| 70 | + |
| 71 | + # Seconds to wait between retry attempts (default 30). |
| 72 | + [int]$RetryIntervalSeconds = 30 |
| 73 | +) |
| 74 | + |
| 75 | +Set-StrictMode -Version Latest |
| 76 | +$ErrorActionPreference = 'Stop' |
| 77 | + |
| 78 | +# ── Extract the package (skip if already done) ──────────────────────────────── |
| 79 | + |
| 80 | +$dllFullPath = Join-Path $ExtractPath $DllPath |
| 81 | + |
| 82 | +if (-not (Test-Path $dllFullPath)) { |
| 83 | + Write-Host "Extracting $PackageName" |
| 84 | + |
| 85 | + New-Item -ItemType Directory -Force -Path $ExtractPath | Out-Null |
| 86 | + |
| 87 | + $nupkg = Get-ChildItem -Path $ArtifactPath -Filter "$PackageName.*.nupkg" ` |
| 88 | + | Where-Object { $_.Name -notlike '*.snupkg' } ` |
| 89 | + | Select-Object -First 1 |
| 90 | + |
| 91 | + if (-not $nupkg) { |
| 92 | + Write-Host "##vso[task.logissue type=error]No $PackageName nupkg found in $ArtifactPath" |
| 93 | + exit 1 |
| 94 | + } |
| 95 | + |
| 96 | + Write-Host "Found: $($nupkg.FullName)" |
| 97 | + |
| 98 | + $zipPath = Join-Path $ExtractPath 'package.zip' |
| 99 | + Copy-Item $nupkg.FullName $zipPath |
| 100 | + Expand-Archive -Path $zipPath -DestinationPath $ExtractPath -Force |
| 101 | + Remove-Item $zipPath |
| 102 | +} |
| 103 | + |
| 104 | +if (-not (Test-Path $dllFullPath)) { |
| 105 | + Write-Host "##vso[task.logissue type=error]DLL not found after extraction: $dllFullPath" |
| 106 | + exit 1 |
| 107 | +} |
| 108 | + |
| 109 | +# ── Locate symchk.exe ──────────────────────────────────────────────────────── |
| 110 | + |
| 111 | +$symchkCandidates = @( |
| 112 | + "${env:ProgramFiles(x86)}\Windows Kits\10\Debuggers\x64\symchk.exe" |
| 113 | + "${env:ProgramFiles}\Windows Kits\10\Debuggers\x64\symchk.exe" |
| 114 | +) |
| 115 | + |
| 116 | +$symchkPath = $null |
| 117 | +foreach ($candidate in $symchkCandidates) { |
| 118 | + if (Test-Path $candidate) { |
| 119 | + $symchkPath = $candidate |
| 120 | + break |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +if (-not $symchkPath) { |
| 125 | + Write-Host "##vso[task.logissue type=error]symchk.exe not found. Ensure Debugging Tools for Windows are installed." |
| 126 | + exit 1 |
| 127 | +} |
| 128 | + |
| 129 | +# ── Verify symbols (with retries for publishing latency) ────────────────────── |
| 130 | + |
| 131 | +$dllLeaf = Split-Path $dllFullPath -Leaf |
| 132 | + |
| 133 | +Write-Host "Verifying symbols for $dllLeaf on $SymbolServerName ($SymbolServerUrl)" |
| 134 | +Write-Host "Using symchk: $symchkPath" |
| 135 | +Write-Host "Max attempts: $MaxRetries, interval: ${RetryIntervalSeconds}s" |
| 136 | + |
| 137 | +$symchkArgs = @( |
| 138 | + $dllFullPath, |
| 139 | + "/s", "srv*$SymbolServerUrl", |
| 140 | + "/os" |
| 141 | +) |
| 142 | + |
| 143 | +for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) { |
| 144 | + Write-Host "Attempt $attempt of $MaxRetries — running: symchk $($symchkArgs -join ' ')" |
| 145 | + $output = & $symchkPath @symchkArgs 2>&1 | Out-String |
| 146 | + $symchkExit = $LASTEXITCODE |
| 147 | + |
| 148 | + Write-Host $output |
| 149 | + |
| 150 | + $passed = ($symchkExit -eq 0) -and |
| 151 | + ($output -match "FAILED files = 0") -and |
| 152 | + ($output -match "PASSED \+ IGNORED files = [1-9]") |
| 153 | + |
| 154 | + if ($passed) { |
| 155 | + Write-Host "Symbols verified successfully for $dllLeaf on $SymbolServerName" |
| 156 | + exit 0 |
| 157 | + } |
| 158 | + |
| 159 | + if ($attempt -lt $MaxRetries) { |
| 160 | + Write-Host "Symbols not yet available. Retrying in $RetryIntervalSeconds seconds..." |
| 161 | + Start-Sleep -Seconds $RetryIntervalSeconds |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +Write-Host "##vso[task.logissue type=error]symchk could not verify symbols for $dllLeaf on $SymbolServerName after $MaxRetries attempts." |
| 166 | +exit 1 |
0 commit comments