Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ When a new issue is created, follow these steps:
- All source code is in `src/Microsoft.Data.SqlClient/src/`. Do NOT add code to legacy `netfx/src/` or `netcore/src/` directories.
- Only `ref/` folders in `netcore/ref/` and `netfx/ref/` remain active for defining the public API surface.
- 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`).
- Lines of code, comments, and other text should be a maximum of 100 characters (see `policy/coding-style.md`).
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copilot has some built-in settings that specify 80 chars max, and it stores it locally (i.e. somewhere in my user directory). It claims that this will ensure that it generates text with our desired line length.

Comment thread
paulmedynski marked this conversation as resolved.
- Respect API compatibility rules across .NET versions
- Do not introduce breaking changes without proper justification and documentation
- Use the `doc/` directory for any new documentation or updates to existing documentation
Expand Down
10 changes: 6 additions & 4 deletions eng/pipelines/common/templates/steps/override-sni-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ steps:
- task: PowerShell@2
displayName: Add SNI Validation Feed in Nuget.config
inputs:
pwsh: true
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Tweaks to make this more cross-OS friendly.

targetType: inline
script: |
Write-Host "SNI validation feed to use = ${{parameters.SNIValidationFeed}}"

# define file to update
$NugetCfg = Join-Path -Path '.' -ChildPath 'NuGet.config'
type $NugetCfg
Get-Content $NugetCfg

# load content of xml from file defined above
$xml = New-Object XML
Expand All @@ -44,10 +45,11 @@ steps:

# save the xml file
$xml.Save($NugetCfg)
type $NugetCfg
Get-Content $NugetCfg
- task: PowerShell@2
displayName: Update SNI Version in Versions.props
inputs:
pwsh: true
targetType: inline
# TODO(https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/42204):
# Package dependency versions have moved to Directory.Packages.props, so the below script no
Expand All @@ -57,7 +59,7 @@ steps:

# define file to update
$PropsPath = Join-Path -Path '.' -ChildPath 'tools\props\Versions.props'
type $PropsPath
Get-Content $PropsPath

# load content of xml from file defined above
$xml = New-Object XML
Expand All @@ -79,6 +81,6 @@ steps:

# save the xml file
$xml.Save($PropsPath)
type $PropsPath
Get-Content $PropsPath
- task: NuGetAuthenticate@1
displayName: 'NuGet Authenticate with SNI Validation Feed'
3 changes: 3 additions & 0 deletions eng/pipelines/onebranch/jobs/build-buildproj-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ parameters:
jobs:
- job: 'build_package_${{ parameters.packageShortName }}'
displayName: 'Build: ${{ parameters.packageFullName }}'
# Pool schema docs (OneBranch): https://aka.ms/obpipelines/yaml/jobs
# Agent pool concepts (Azure Pipelines):
# https://learn.microsoft.com/azure/devops/pipelines/agents/pools-queues
pool:
type: windows

Expand Down
3 changes: 3 additions & 0 deletions eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ jobs:
artifactName: ${{ parameters.artifactName }}
targetPath: $(artifactPath)

# Pool schema docs (OneBranch): https://aka.ms/obpipelines/yaml/jobs
# Agent pool concepts (Azure Pipelines):
# https://learn.microsoft.com/azure/devops/pipelines/agents/pools-queues
pool:
type: release

Expand Down
3 changes: 3 additions & 0 deletions eng/pipelines/onebranch/jobs/publish-symbols-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ parameters:
jobs:
- job: 'publish_symbols_${{ parameters.packageShortName }}'
displayName: 'Publish Symbols: ${{ parameters.packageFullName }}'
# Pool schema docs (OneBranch): https://aka.ms/obpipelines/yaml/jobs
# Agent pool concepts (Azure Pipelines):
# https://learn.microsoft.com/azure/devops/pipelines/agents/pools-queues
pool:
type: linux

Expand Down
13 changes: 8 additions & 5 deletions eng/pipelines/onebranch/jobs/validate-signed-package-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ parameters:
type: boolean

jobs:
- job: validate_nuget_package
displayName: "Validate NuGet package"
- job: validate_signed_package
displayName: Verify SqlClient NuGet Package

# Pool schema docs (OneBranch): https://aka.ms/obpipelines/yaml/jobs
# Agent pool concepts (Azure Pipelines):
# https://learn.microsoft.com/azure/devops/pipelines/agents/pools-queues
pool:
type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs
type: windows
isCustom: true
name: ADO-1ES-Pool
vmImage: "ADO-MMS22-SQL19"
vmImage: ADO-Win25

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

# Path within the downloaded artifact where NuGet packages are located.
- name: artifactPath
Expand Down
77 changes: 77 additions & 0 deletions eng/pipelines/onebranch/jobs/validate-symbols-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#################################################################################
# Licensed to the .NET Foundation under one or more agreements. #
# The .NET Foundation licenses this file to you under the MIT license. #
# See the LICENSE file in the project root for more information. #
#################################################################################

# Validates that symbols (PDBs) for every published package DLL are available on each configured
# symbol server using the symchk tool.
#
# All packages specified in the 'packages' parameter will be checked against all symbol servers
# specified in the 'symbolServers' parameter.

parameters:
# True to enable debug information and steps.
- name: debug
type: boolean

# The list of symbol servers to verify against. Each entry has a 'name' (friendly display name)
# and 'url' (the symbol server URL that symchk can query).
- name: symbolServers
type: object
default:
- name: MSDL (Public)
url: https://msdl.microsoft.com/download/symbols
- name: SymWeb (Internal)
url: https://symweb.azurefd.net

# The packages whose symbols should be verified. Each entry has:
# artifactName — pipeline artifact name (from onebranch-variables.yml)
# packageName — NuGet package name prefix used to locate the .nupkg file
# dllPath — relative path to the DLL inside the extracted package
- name: packages
type: object
default: []

jobs:
- job: validate_symbols
displayName: Verify symbols on symbol servers

# Pool schema docs (OneBranch): https://aka.ms/obpipelines/yaml/jobs
# Agent pool concepts (Azure Pipelines):
# https://learn.microsoft.com/azure/devops/pipelines/agents/pools-queues
pool:
type: windows
isCustom: true
name: ADO-1ES-Pool
vmImage: ADO-Win25

variables:
extractRoot: $(Build.SourcesDirectory)\symchk_packages

steps:
- ${{ if parameters.debug }}:
- script: SET
displayName: Print Environment Variables

# ── Download, extract, and verify symbols for each package ──────────────

- ${{ each pkg in parameters.packages }}:
- download: current
artifact: ${{ pkg.artifactName }}
patterns: '**/*.nupkg'
displayName: Download ${{ pkg.packageName }}

Comment thread
paulmedynski marked this conversation as resolved.
- ${{ each server in parameters.symbolServers }}:
- task: PowerShell@2
displayName: Verify ${{ pkg.packageName }} on ${{ server.name }}
inputs:
pwsh: true
filePath: eng/pipelines/onebranch/scripts/validate-symbols.ps1
arguments: >
-ArtifactPath "$(Pipeline.Workspace)\${{ pkg.artifactName }}"
-ExtractPath "$(extractRoot)\${{ pkg.packageName }}"
-PackageName "${{ pkg.packageName }}"
-DllPath "${{ pkg.dllPath }}"
-SymbolServerUrl "${{ server.url }}"
-SymbolServerName "${{ server.name }}"
78 changes: 57 additions & 21 deletions eng/pipelines/onebranch/scripts/tests/README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,79 @@
# Publish-Symbols Tests
# OneBranch Script Tests

Pester tests for the `publish-symbols.ps1` script used by the symbol publishing pipeline step.
This directory contains [Pester](https://pester.dev/) tests for PowerShell
scripts used by the OneBranch pipelines.

## Prerequisites

- PowerShell 5.1+ or PowerShell 7+
- [Pester v5](https://pester.dev/) (`Install-Module Pester -MinimumVersion 5.0 -Scope CurrentUser`)
| Tool | Version | Install |
| ---- | ------- | ------- |
| PowerShell (pwsh) | 7.2+ | [Install PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) |
| Pester | 5.x | `Install-Module Pester -Force -Scope CurrentUser -SkipPublisherCheck` |

## Running the Tests
## Running tests

From this directory:
From the repository root:

```powershell
Invoke-Pester ./publish-symbols.Tests.ps1
pwsh -c "Invoke-Pester ./eng/pipelines/onebranch/scripts/tests/ -Output Detailed"
```

Or from the repository root:
Run a single test file:

```powershell
Invoke-Pester ./eng/pipelines/onebranch/scripts/tests/
pwsh -c "Invoke-Pester ./eng/pipelines/onebranch/scripts/tests/publish-symbols.Tests.ps1 -Output Detailed"
pwsh -c "Invoke-Pester ./eng/pipelines/onebranch/scripts/tests/validate-symbols.Tests.ps1 -Output Detailed"
```

For detailed output:
## Writing tests

### File naming

Test files must follow Pester naming conventions:

```text
<ScriptUnderTest>.Tests.ps1
```

### Locating the script under test

When scripts and tests are siblings under `scripts/` and `scripts/tests/`,
reference scripts relative to `$PSScriptRoot`:

```powershell
BeforeAll {
$Script:ScriptPath = Join-Path $PSScriptRoot '..' 'my-script.ps1'
}
```

### Testing scripts that use `exit`

Pipeline scripts commonly use `exit` for control flow. To validate exit codes,
run scripts as child processes with `Start-Process`:

```powershell
Invoke-Pester ./publish-symbols.Tests.ps1 -Output Detailed
$proc = Start-Process -FilePath 'pwsh' `
-ArgumentList @('-NoProfile', '-NonInteractive', '-File', $scriptPath, <args...>) `
-NoNewWindow -Wait -PassThru `
-RedirectStandardOutput $stdoutFile `
-RedirectStandardError $stderrFile

$proc.ExitCode | Should -Be 0
```

## Test Coverage
### Mocking external tools

When a script calls external tools (for example `symchk.exe`, `az`, or
`Invoke-RestMethod`), mock those calls in tests. See
`validate-symbols.Tests.ps1` and `publish-symbols.Tests.ps1`.

## Test inventory

| Area | What's tested |
| --------------------- | ---------------------------------------------------------------- |
| Parameter validation | Empty strings rejected for all mandatory parameters |
| URL construction | Base URL, register URL, request URL built from parameters |
| Request bodies | Registration body, default publish flags, flag overrides |
| Error handling | Token failure, registration failure, publish failure, status failure — all verify expanded URI in error message |
| Status validation | Detects Failed/Cancelled results, respects PublishToInternal/PublishToPublic flags, passes on Succeeded/Pending |
| Test file | Script under test | What it covers |
| --------- | ----------------- | -------------- |
| `publish-symbols.Tests.ps1` | `scripts/publish-symbols.ps1` | Parameter validation, URL construction, request bodies, status validation, error handling |
| `validate-symbols.Tests.ps1` | `scripts/validate-symbols.ps1` | Syntax validation, package discovery/extraction, symchk detection, retry logic |

## Notes

- All external calls (`az`, `Invoke-RestMethod`) are mocked — no network access or Azure credentials are required.
- Tests validate the script at `../publish-symbols.ps1` relative to this directory.
- Tests for `publish-symbols.ps1` mock all external calls (`az`, `Invoke-RestMethod`), so no network access or Azure credentials are required.
Loading
Loading