Skip to content

Commit a5aa5a1

Browse files
committed
- Added validation stage and symbol verification job.
1 parent f5942fd commit a5aa5a1

13 files changed

Lines changed: 1002 additions & 78 deletions

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ When a new issue is created, follow these steps:
138138
- All source code is in `src/Microsoft.Data.SqlClient/src/`. Do NOT add code to legacy `netfx/src/` or `netcore/src/` directories.
139139
- Only `ref/` folders in `netcore/ref/` and `netfx/ref/` remain active for defining the public API surface.
140140
- Check for platform-specific differences using file suffixes (`.netfx.cs`, `.netcore.cs`, `.windows.cs`, `.unix.cs`) and conditional compilation (`#if NETFRAMEWORK`, `#if NET`, `#if _WINDOWS`, `#if _UNIX`).
141+
- Lines of code, comments, and other text should be a maximum of 100 characters (see `policy/coding-style.md`).
141142
- Respect API compatibility rules across .NET versions
142143
- Do not introduce breaking changes without proper justification and documentation
143144
- Use the `doc/` directory for any new documentation or updates to existing documentation

eng/pipelines/common/templates/steps/override-sni-version.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ steps:
1515
- task: PowerShell@2
1616
displayName: Add SNI Validation Feed in Nuget.config
1717
inputs:
18+
pwsh: true
1819
targetType: inline
1920
script: |
2021
Write-Host "SNI validation feed to use = ${{parameters.SNIValidationFeed}}"
2122
2223
# define file to update
2324
$NugetCfg = Join-Path -Path '.' -ChildPath 'NuGet.config'
24-
type $NugetCfg
25+
Get-Content $NugetCfg
2526
2627
# load content of xml from file defined above
2728
$xml = New-Object XML
@@ -44,10 +45,11 @@ steps:
4445
4546
# save the xml file
4647
$xml.Save($NugetCfg)
47-
type $NugetCfg
48+
Get-Content $NugetCfg
4849
- task: PowerShell@2
4950
displayName: Update SNI Version in Versions.props
5051
inputs:
52+
pwsh: true
5153
targetType: inline
5254
# TODO(https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/42204):
5355
# Package dependency versions have moved to Directory.Packages.props, so the below script no
@@ -57,7 +59,7 @@ steps:
5759
5860
# define file to update
5961
$PropsPath = Join-Path -Path '.' -ChildPath 'tools\props\Versions.props'
60-
type $PropsPath
62+
Get-Content $PropsPath
6163
6264
# load content of xml from file defined above
6365
$xml = New-Object XML
@@ -79,6 +81,6 @@ steps:
7981
8082
# save the xml file
8183
$xml.Save($PropsPath)
82-
type $PropsPath
84+
Get-Content $PropsPath
8385
- task: NuGetAuthenticate@1
8486
displayName: 'NuGet Authenticate with SNI Validation Feed'

eng/pipelines/onebranch/jobs/validate-signed-package-job.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,19 @@ parameters:
3636
- name: isOfficial
3737
type: boolean
3838

39+
# True if this build is a preview.
40+
- name: isPreview
41+
type: boolean
42+
3943
jobs:
40-
- job: validate_nuget_package
41-
displayName: "Validate NuGet package"
44+
- job: validate_signed_package
45+
displayName: Verify SqlClient NuGet Package
4246

4347
pool:
44-
type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs
48+
type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs
4549
isCustom: true
4650
name: ADO-1ES-Pool
47-
vmImage: "ADO-MMS22-SQL19"
51+
vmImage: ADO-Win25
4852

4953
variables: # More settings at https://aka.ms/obpipelines/yaml/jobs
5054

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#################################################################################
2+
# Licensed to the .NET Foundation under one or more agreements. #
3+
# The .NET Foundation licenses this file to you under the MIT license. #
4+
# See the LICENSE file in the project root for more information. #
5+
#################################################################################
6+
7+
# Validates that symbols (PDBs) for every published package DLL are available on each configured
8+
# symbol server using the symchk tool.
9+
#
10+
# All packages specified in the 'packages' parameter will be checked against all symbol servers
11+
# specified in the 'symbolServers' parameter.
12+
13+
parameters:
14+
# True to enable debug information and steps.
15+
- name: debug
16+
type: boolean
17+
18+
# The list of symbol servers to verify against. Each entry has a 'name' (friendly display name)
19+
# and 'url' (the symbol server URL that symchk can query).
20+
- name: symbolServers
21+
type: object
22+
default:
23+
- name: MSDL (Public)
24+
url: https://msdl.microsoft.com/download/symbols
25+
- name: SymWeb (Internal)
26+
url: https://symweb.azurefd.net
27+
28+
# The packages whose symbols should be verified. Each entry has:
29+
# artifactName — pipeline artifact name (from onebranch-variables.yml)
30+
# packageName — NuGet package name prefix used to locate the .nupkg file
31+
# dllPath — relative path to the DLL inside the extracted package
32+
- name: packages
33+
type: object
34+
default: []
35+
36+
jobs:
37+
- job: validate_symbols
38+
displayName: Verify symbols on symbol servers
39+
40+
pool:
41+
type: windows
42+
isCustom: true
43+
name: ADO-1ES-Pool
44+
vmImage: ADO-Win25
45+
46+
variables:
47+
extractRoot: $(Build.SourcesDirectory)\symchk_packages
48+
49+
steps:
50+
- ${{ if parameters.debug }}:
51+
- script: SET
52+
displayName: Print Environment Variables
53+
54+
# ── Download, extract, and verify symbols for each package ──────────────
55+
56+
- ${{ each pkg in parameters.packages }}:
57+
- download: current
58+
artifact: ${{ pkg.artifactName }}
59+
patterns: '**/*.nupkg'
60+
displayName: Download ${{ pkg.packageName }}
61+
62+
- ${{ each server in parameters.symbolServers }}:
63+
- task: PowerShell@2
64+
displayName: Verify ${{ pkg.packageName }} on ${{ server.name }}
65+
inputs:
66+
pwsh: true
67+
filePath: eng/pipelines/onebranch/jobs/validate-symbols.ps1
68+
arguments: >
69+
-ArtifactPath "$(Pipeline.Workspace)\${{ pkg.artifactName }}"
70+
-ExtractPath "$(extractRoot)\${{ pkg.packageName }}"
71+
-PackageName "${{ pkg.packageName }}"
72+
-DllPath "${{ pkg.dllPath }}"
73+
-SymbolServerUrl "${{ server.url }}"
74+
-SymbolServerName "${{ server.name }}"
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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+
.PARAMETER MaxRetries
37+
Maximum number of attempts when symbols are not yet available. The
38+
first attempt runs immediately; subsequent attempts wait
39+
RetryIntervalSeconds between them. Defaults to 10 (~5 minutes total
40+
with default interval).
41+
42+
.PARAMETER RetryIntervalSeconds
43+
Seconds to wait between retry attempts (default 30).
44+
45+
.EXAMPLE
46+
.\validate-symbols.ps1 `
47+
-ArtifactPath "C:\agent\_work\1\drop_SqlClient" `
48+
-ExtractPath "C:\agent\_work\1\s\symchk_packages\Microsoft.Data.SqlClient" `
49+
-PackageName "Microsoft.Data.SqlClient" `
50+
-DllPath "lib\net8.0\Microsoft.Data.SqlClient.dll" `
51+
-SymbolServerUrl "https://msdl.microsoft.com/download/symbols" `
52+
-SymbolServerName "MSDL (Public)"
53+
#>
54+
55+
[CmdletBinding()]
56+
param(
57+
[Parameter(Mandatory)]
58+
[string]$ArtifactPath,
59+
60+
[Parameter(Mandatory)]
61+
[string]$ExtractPath,
62+
63+
[Parameter(Mandatory)]
64+
[string]$PackageName,
65+
66+
[Parameter(Mandatory)]
67+
[string]$DllPath,
68+
69+
[Parameter(Mandatory)]
70+
[string]$SymbolServerUrl,
71+
72+
[Parameter(Mandatory)]
73+
[string]$SymbolServerName,
74+
75+
[int]$MaxRetries = 10,
76+
77+
[int]$RetryIntervalSeconds = 30
78+
)
79+
80+
Set-StrictMode -Version Latest
81+
$ErrorActionPreference = 'Stop'
82+
83+
# -- Extract the package (skip if already done) --------------------------------
84+
85+
$dllFullPath = Join-Path $ExtractPath $DllPath
86+
87+
if (-not (Test-Path $dllFullPath)) {
88+
Write-Host "Extracting $PackageName"
89+
90+
New-Item -ItemType Directory -Force -Path $ExtractPath | Out-Null
91+
92+
$nupkg = Get-ChildItem -Path $ArtifactPath -Filter "$PackageName.*.nupkg" `
93+
| Where-Object { $_.Name -notlike '*.snupkg' } `
94+
| Select-Object -First 1
95+
96+
if (-not $nupkg) {
97+
Write-Host "##vso[task.logissue type=error]No $PackageName nupkg found in $ArtifactPath"
98+
exit 1
99+
}
100+
101+
Write-Host "Found: $($nupkg.FullName)"
102+
103+
$zipPath = Join-Path $ExtractPath 'package.zip'
104+
Copy-Item $nupkg.FullName $zipPath
105+
Expand-Archive -Path $zipPath -DestinationPath $ExtractPath -Force
106+
Remove-Item $zipPath
107+
}
108+
109+
if (-not (Test-Path $dllFullPath)) {
110+
Write-Host "##vso[task.logissue type=error]DLL not found after extraction: $dllFullPath"
111+
exit 1
112+
}
113+
114+
# -- Locate symchk.exe ---------------------------------------------------------
115+
116+
$symchkCandidates = @(
117+
"${env:ProgramFiles(x86)}\Windows Kits\10\Debuggers\x64\symchk.exe"
118+
"${env:ProgramFiles}\Windows Kits\10\Debuggers\x64\symchk.exe"
119+
)
120+
121+
$symchkPath = $null
122+
foreach ($candidate in $symchkCandidates) {
123+
if (Test-Path $candidate) {
124+
$symchkPath = $candidate
125+
break
126+
}
127+
}
128+
129+
if (-not $symchkPath) {
130+
Write-Host "##vso[task.logissue type=error]symchk.exe not found. Ensure Debugging Tools for Windows are installed."
131+
exit 1
132+
}
133+
134+
# -- Verify symbols (with retries for publishing latency) ----------------------
135+
136+
$dllLeaf = Split-Path $dllFullPath -Leaf
137+
138+
Write-Host "Verifying symbols for $dllLeaf on $SymbolServerName ($SymbolServerUrl)"
139+
Write-Host "Using symchk: $symchkPath"
140+
Write-Host "Max attempts: $MaxRetries, interval: ${RetryIntervalSeconds}s"
141+
142+
$symchkArgs = @(
143+
$dllFullPath,
144+
"/s", "srv*$SymbolServerUrl",
145+
"/os"
146+
)
147+
148+
for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) {
149+
Write-Host "Attempt $attempt of $MaxRetries -- running: symchk $($symchkArgs -join ' ')"
150+
$output = & $symchkPath @symchkArgs 2>&1 | Out-String
151+
$symchkExit = $LASTEXITCODE
152+
153+
Write-Host $output
154+
155+
$passed = ($symchkExit -eq 0) -and
156+
($output -match "FAILED files = 0") -and
157+
($output -match "PASSED \+ IGNORED files = [1-9]")
158+
159+
if ($passed) {
160+
Write-Host "Symbols verified successfully for $dllLeaf on $SymbolServerName"
161+
exit 0
162+
}
163+
164+
if ($attempt -lt $MaxRetries) {
165+
Write-Host "Symbols not yet available. Retrying in $RetryIntervalSeconds seconds..."
166+
Start-Sleep -Seconds $RetryIntervalSeconds
167+
}
168+
}
169+
170+
Write-Host "##vso[task.logissue type=error]symchk could not verify symbols for $dllLeaf on $SymbolServerName after $MaxRetries attempts."
171+
exit 1

eng/pipelines/onebranch/sqlclient-non-official.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,22 @@ extends:
231231
symbolsPublishTokenUri: '$(SymbolsPublishTokenUriPPE)'
232232
symbolsUploadAccount: '$(SymbolsUploadAccount)'
233233

234-
- template: /eng/pipelines/onebranch/stages/release-stages.yml@self
234+
- template: /eng/pipelines/onebranch/stages/validation-stage.yml@self
235235
parameters:
236236
debug: ${{ parameters.debug }}
237+
isOfficial: false
238+
isPreview: ${{ parameters.isPreview }}
239+
publishSymbols: ${{ parameters.publishSymbols }}
240+
buildSqlServerServer: ${{ parameters.buildSqlServerServer }}
241+
buildSqlClient: ${{ parameters.buildSqlClient }}
242+
buildAKVProvider: ${{ parameters.buildAKVProvider }}
243+
244+
- template: /eng/pipelines/onebranch/stages/release-stage.yml@self
245+
parameters:
246+
debug: ${{ parameters.debug }}
247+
isOfficial: false
237248
# Non-official pipelines always push to the NuGet Test feed.
238249
releaseToProduction: false
239-
# This is _not_ an official pipeline.
240-
isOfficial: false
241250
stageNameSuffix: test
242251

243252
publishSymbols: ${{ parameters.publishSymbols }}

eng/pipelines/onebranch/sqlclient-official.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,21 @@ extends:
265265
symbolsPublishTokenUri: '$(SymbolsPublishTokenUriPPE)'
266266
symbolsUploadAccount: '$(SymbolsUploadAccount)'
267267

268-
- template: /eng/pipelines/onebranch/stages/release-stages.yml@self
268+
- template: /eng/pipelines/onebranch/stages/validation-stage.yml@self
269269
parameters:
270270
debug: ${{ parameters.debug }}
271-
releaseToProduction: ${{ parameters.releaseToProduction }}
272-
# This is an official pipeline.
273271
isOfficial: true
272+
isPreview: ${{ parameters.isPreview }}
273+
publishSymbols: ${{ parameters.publishSymbols }}
274+
buildSqlServerServer: ${{ parameters.buildSqlServerServer }}
275+
buildSqlClient: ${{ parameters.buildSqlClient }}
276+
buildAKVProvider: ${{ parameters.buildAKVProvider }}
277+
278+
- template: /eng/pipelines/onebranch/stages/release-stage.yml@self
279+
parameters:
280+
debug: ${{ parameters.debug }}
281+
isOfficial: true
282+
releaseToProduction: ${{ parameters.releaseToProduction }}
274283
stageNameSuffix: production
275284

276285
publishSymbols: ${{ parameters.publishSymbols }}

0 commit comments

Comments
 (0)