Skip to content

Commit eacf112

Browse files
authored
Move symbol publishing into a dedicated stage and refactor build artifacts (dotnet#4175)
1 parent e0176e1 commit eacf112

22 files changed

Lines changed: 1270 additions & 350 deletions

.github/instructions/onebranch-pipeline-design.instructions.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Defined in `stages/build-stages.yml`. Four build stages plus validation, ordered
3636
- **`build_addons`** (Stage 4) — AKV Provider; `dependsOn: build_dependent`; downloads SqlClient + Abstractions + Logging artifacts
3737
- **`mds_package_validation`** — Validates signed SqlClient package; `dependsOn: build_dependent`; runs in parallel with Stage 4
3838

39+
Each build job copies PDB files into `$(JOB_OUTPUT)/symbols/` so they are included in the auto-published pipeline artifact alongside the NuGet packages in `$(JOB_OUTPUT)/packages/`.
40+
3941
Stage conditional rules:
4042
- Wrap stages/jobs in `${{ if }}` compile-time conditionals based on build parameters
4143
- `buildSqlClient` controls Stages 2, 3, validation, and Logging (when AKV is disabled)
@@ -45,17 +47,30 @@ Stage conditional rules:
4547

4648
## Job Templates
4749

48-
- **`build-signed-csproj-package-job.yml`** — Generic job for csproj-based packages (Logging, SqlServer.Server, Abstractions, Azure, AKV Provider). Flow: Build DLLs → ESRP DLL signing → NuGet pack (`NoBuild=true`) → ESRP NuGet signing
49-
- **`build-signed-sqlclient-package-job.yml`** — SqlClient-specific job (nuspec-based). Flow: Build all configurations → ESRP DLL signing (main + resource DLLs) → NuGet pack via nuspec → ESRP NuGet signing
50+
- **`build-signed-csproj-package-job.yml`** — Generic job for csproj-based packages (Logging, SqlServer.Server, Abstractions, Azure, AKV Provider). Flow: Build DLLs → ESRP DLL signing → NuGet pack (`NoBuild=true`) → ESRP NuGet signing → Copy PDBs to artifact
51+
- **`build-signed-sqlclient-package-job.yml`** — SqlClient-specific job (nuspec-based). Flow: Build all configurations → ESRP DLL signing (main + resource DLLs) → NuGet pack via nuspec → ESRP NuGet signing → Copy PDBs to artifact
5052
- **`validate-signed-package-job.yml`** — Validates signed MDS package (signature, strong names, folder structure, target frameworks)
5153
- **`publish-nuget-package-job.yml`** — Reusable release job using OneBranch `templateContext.type: releaseJob` with `inputs` for artifact download; pushes via `NuGetCommand@2`
54+
- **`publish-symbols-job.yml`** — Reusable symbols job: downloads a build artifact, locates PDBs under `symbols/`, and invokes `publish-symbols-step.yml`
5255

5356
When adding a new csproj-based package:
5457
- Use `build-signed-csproj-package-job.yml` with appropriate `packageName`, `packageFullName`, `versionProperties`, and `downloadArtifacts`
5558
- Add build and pack targets to `build.proj`
5659
- Add version variables to `variables/common-variables.yml`
5760
- Add artifact name variable to `variables/onebranch-variables.yml`
5861

62+
## Symbols Publishing Stage
63+
64+
- Defined in `stages/publish-symbols-stage.yml`; produces stage `publish_symbols`
65+
- Entire stage excluded at compile time when `publishSymbols` is false
66+
- `dependsOn` is conditional based on which `build*` parameters are set, mirroring the build stage dependency graph
67+
- One job per package (`publish-symbols-job.yml`), each downloading its build artifact and publishing PDBs from `symbols/`
68+
- Each package's PDBs are published separately with unique artifact names and version information
69+
- Build jobs copy PDBs into `$(JOB_OUTPUT)/symbols/` so they are included in the auto-published artifact
70+
- The `publish-symbols-step.yml` accepts a `symbolsFolder` parameter to point at the downloaded PDB location
71+
- The publish step calls an extracted `publish-symbols.ps1` script with structured error handling and diagnostic logging
72+
- Symbols publishing credentials come from the `Symbols Publishing` variable group
73+
5974
## Release Stage
6075

6176
- Defined in `stages/release-stages.yml`; produces stage `release_production` (official) or `release_test` (non-official) via `stageNameSuffix` parameter
@@ -98,8 +113,7 @@ When `isPreview` is true, pipeline resolves `effective*Version` variables to pre
98113
- When adding a new package, add GA version, preview version, and assembly file version entries
99114

100115
Variable groups:
101-
- `Release Variables` — release configuration (in `common-variables.yml`)
102-
- `Symbols publishing` — symbol publishing credentials (in `common-variables.yml`)
116+
- `Symbols Publishing` — symbol publishing credentials (in `onebranch-variables.yml`)
103117
- `ESRP Federated Creds (AME)` — ESRP signing credentials (in `common-variables.yml`)
104118

105119
## Code Signing (ESRP)
@@ -115,15 +129,19 @@ Variable groups:
115129

116130
- TSA: enabled only in official pipeline; disabled in non-official to avoid spurious alerts
117131
- ApiScan: enabled in both; currently `break: false` pending package registration
118-
- Each build job sets `ob_sdl_apiscan_*` variables pointing to `$(Build.SourcesDirectory)/apiScan/<PackageName>/`
132+
- Each build job sets `ob_sdl_apiscan_softwareFolder` to `$(JOB_OUTPUT)/assemblies` and `ob_sdl_apiscan_symbolsFolder` to `$(JOB_OUTPUT)/symbols`
119133
- CodeQL, SBOM, Policheck (`break: true`): enabled in both pipelines
120134
- asyncSdl `enabled: false` in both; individual sub-tools (CredScan, BinSkim, Armory, Roslyn) configured underneath
121135
- Policheck exclusions: `$(REPO_ROOT)\.config\PolicheckExclusions.xml`
122136
- CredScan suppressions: `$(REPO_ROOT)/.config/CredScanSuppressions.json`
123137

124138
## Artifact Conventions
125139

126-
- `ob_outputDirectory` set to `$(PACK_OUTPUT)` (= `$(REPO_ROOT)/output`) — OneBranch auto-publishes this directory
140+
- `ob_outputDirectory` set to `$(JOB_OUTPUT)` (= `$(REPO_ROOT)/output`) — OneBranch auto-publishes this directory
141+
- Each published artifact uses subdirectories to separate file types:
142+
- `assemblies/` — DLL assemblies for APIScan (preserving TFM folder structure)
143+
- `packages/` — NuGet packages (`.nupkg`, `.snupkg`)
144+
- `symbols/` — PDB symbol files (preserving TFM folder structure, shared by APIScan and symbol publishing)
127145
- Artifact names follow `drop_<stageName>_<jobName>` — defined in `variables/onebranch-variables.yml`
128146
- Downstream jobs download artifacts via `DownloadPipelineArtifact@2` into `$(Build.SourcesDirectory)/packages`
129147
- Downloaded packages serve as a local NuGet source for `dotnet restore`

eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml

Lines changed: 38 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,6 @@ parameters:
3636
- name: signingEsrpConnectedServiceName
3737
type: string
3838

39-
# Symbols Publishing Parameters ------------------------------------------
40-
41-
- name: symbolsAzureSubscription
42-
type: string
43-
default: 'Symbols publishing Workload Identity federation service-ADO.Net'
44-
45-
- name: symbolsPublishProjectName
46-
type: string
47-
default: 'Microsoft.Data.SqlClient.SNI'
48-
49-
- name: symbolsPublishServer
50-
type: string
51-
52-
- name: symbolsPublishTokenUri
53-
type: string
54-
55-
- name: symbolsUploadAccount
56-
type: string
57-
default: 'SqlClientDrivers'
58-
5939
# OTHERS +=====================================
6040

6141
# Short package name used in the job name, display strings, filesystem paths, and as a suffix for
@@ -74,11 +54,6 @@ parameters:
7454
- name: packageFullName
7555
type: string
7656

77-
# The version of the package. This is used for symbol publishing. It is not used for the DLL or
78-
# NuGet package versions since those are supplied via the versionProperties parameter.
79-
- name: packageVersion
80-
type: string
81-
8257
# The MSBuild build target in build.proj (e.g. BuildLogging). If not specified, defaults to
8358
# Build<packageName>.
8459
- name: buildTarget
@@ -106,10 +81,6 @@ parameters:
10681
- name: assemblyFileVersion
10782
type: string
10883

109-
# True to publish symbols to private and public servers.
110-
- name: publishSymbols
111-
type: boolean
112-
11384
# Optional list of pipeline artifacts to download before building. Each entry is an object
11485
# with 'artifactName' (the pipeline artifact name) and 'displayName' (used in the task label).
11586
# This replaces hard-coded packageName conditionals so callers declare their own dependencies.
@@ -125,12 +96,12 @@ jobs:
12596

12697
variables:
12798
# Inform OneBranch that files put in this directory should be uploaded as artifacts.
128-
ob_outputDirectory: $(PACK_OUTPUT)
99+
ob_outputDirectory: $(JOB_OUTPUT)
129100

130101
# APIScan configuration for this Extension package
131102
ob_sdl_apiscan_enabled: true
132-
ob_sdl_apiscan_softwareFolder: $(REPO_ROOT)/apiScan/${{ parameters.packageName }}/dlls
133-
ob_sdl_apiscan_symbolsFolder: $(REPO_ROOT)/apiScan/${{ parameters.packageName }}/pdbs
103+
ob_sdl_apiscan_softwareFolder: $(JOB_OUTPUT)/assemblies
104+
ob_sdl_apiscan_symbolsFolder: $(JOB_OUTPUT)/symbols
134105
ob_sdl_apiscan_softwarename: ${{ parameters.packageFullName }}
135106
ob_sdl_apiscan_versionNumber: ${{ parameters.assemblyFileVersion }}
136107

@@ -149,7 +120,7 @@ jobs:
149120
displayName: Download ${{ artifact.displayName }}
150121
inputs:
151122
artifactName: ${{ artifact.artifactName }}
152-
targetPath: $(REPO_ROOT)/packages
123+
targetPath: $(JOB_INPUT)
153124

154125
# Install the .NET SDK.
155126
- template: /eng/pipelines/steps/install-dotnet.yml@self
@@ -179,26 +150,42 @@ jobs:
179150
authSignCertName: '${{ parameters.signingAuthSignCertName }}'
180151
esrpClientId: '${{ parameters.signingEsrpClientId }}'
181152
esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}'
182-
pattern: '${{ parameters.packageFullName }}.dll'
183-
184-
# Copy signed/unsigned DLLs and PDBs to APIScan folders.
153+
# Minimatch pattern (multi-line, one pattern per line).
154+
# **/ matches any nested directory (e.g. net8.0/, netstandard2.0/fr/).
155+
#
156+
# Matches: net8.0/<packageFullName>.dll
157+
# netstandard2.0/fr/<packageFullName>.resources.dll
158+
pattern: |
159+
**/${{ parameters.packageFullName }}.dll
160+
**/${{ parameters.packageFullName }}.resources.dll
161+
162+
# Copy DLLs to the assemblies/ subdirectory for APIScan.
185163
- task: CopyFiles@2
186164
displayName: Copy DLLs for APIScan
187165
inputs:
188166
SourceFolder: $(BUILD_OUTPUT)/Package/bin
189-
Contents: "**/${{ parameters.packageFullName }}.dll"
190-
TargetFolder: ${{ variables.ob_sdl_apiscan_softwareFolder }}
167+
# Matches: <packageFullName>.dll (main assembly) across all TFM subdirs
168+
# <packageFullName>.resources.dll (localized satellite assemblies in locale subdirs, if any)
169+
Contents: |
170+
**/${{ parameters.packageFullName }}.dll
171+
**/${{ parameters.packageFullName }}.resources.dll
172+
TargetFolder: $(JOB_OUTPUT)/assemblies
191173
# We must preserve the folder structure since our C# projects may produce multiple
192174
# identically named DLLs for different target frameworks (e.g. netstandard2.0, net5.0,
193175
# etc.), and we need to keep those separate for APIScan to work correctly.
194176
flattenFolders: false
195177

178+
# Copy PDBs into the output directory so they are included in the published pipeline
179+
# artifact. The symbols publishing stage will download this artifact and publish PDBs
180+
# for this package using the files under symbols/.
196181
- task: CopyFiles@2
197-
displayName: Copy PDBs for APIScan
182+
displayName: Copy PDBs for APIScan and Symbols Publishing
198183
inputs:
199184
SourceFolder: $(BUILD_OUTPUT)/Package/bin
200-
Contents: "**/${{ parameters.packageFullName }}.pdb"
201-
TargetFolder: ${{ variables.ob_sdl_apiscan_symbolsFolder }}
185+
# Matches: <packageFullName>.pdb across all TFM subdirs.
186+
# Note: Resource DLLs are resource-only satellite assemblies and do not produce PDBs.
187+
Contents: '**/${{ parameters.packageFullName }}.pdb'
188+
TargetFolder: $(JOB_OUTPUT)/symbols
202189
flattenFolders: false
203190

204191
# Pack the signed DLLs into NuGet package (NoBuild=true).
@@ -217,22 +204,13 @@ jobs:
217204
authSignCertName: '${{ parameters.signingAuthSignCertName }}'
218205
esrpClientId: '${{ parameters.signingEsrpClientId }}'
219206
esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}'
220-
searchPath: $(PACK_OUTPUT)
221-
searchPattern: '${{ parameters.packageFullName}}.*nupkg'
222-
223-
# Publish symbols to servers
224-
# @TODO: Get these parameters from variables/libraries
225-
- ${{ if eq(parameters.publishSymbols, true) }}:
226-
- template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self
227-
parameters:
228-
artifactName: '${{ parameters.packageFullName }}_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.packageVersion }}_$(System.TimelineId)'
229-
azureSubscription: '${{ parameters.symbolsAzureSubscription }}'
230-
packageName: '${{ parameters.packageFullName }}'
231-
publishProjectName: '${{ parameters.symbolsPublishProjectName }}'
232-
publishServer: '${{ parameters.symbolsPublishServer }}'
233-
publishToInternal: 'true'
234-
publishToPublic: 'true'
235-
publishTokenUri: '${{ parameters.symbolsPublishTokenUri }}'
236-
searchPattern: '**/${{ parameters.packageFullName }}*.pdb'
237-
uploadAccount: '${{ parameters.symbolsUploadAccount }}'
238-
version: '${{ parameters.packageVersion }}'
207+
searchPath: $(JOB_OUTPUT)/packages
208+
# Minimatch pattern with extglob.
209+
# [0-9] matches a single digit, anchoring to the version segment so
210+
# similarly-prefixed package names are not matched.
211+
# ?(s) is an extglob that optionally matches 's'.
212+
#
213+
# Matches: <packageFullName>.1.0.0.nupkg
214+
# <packageFullName>.1.0.0-preview.1.snupkg
215+
# Excludes: <packageFullName>.SomeOther.1.0.0.nupkg
216+
searchPattern: '${{ parameters.packageFullName }}.[0-9]*.?(s)nupkg'

0 commit comments

Comments
 (0)