From 4f0327f64152d7e9abaa5eb44355b310cfa48bd9 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:56:41 -0300 Subject: [PATCH 1/3] Switch publish symbols jobs to Linux pool - Change pool type from windows to linux in publish-symbols-job.yml - Add LinuxContainerImage variable to onebranch-variables.yml - Add .artifactignore step to suppress empty artifact publishing - Improve comments in publish-symbols-step.yml --- .../onebranch/jobs/publish-symbols-job.yml | 15 ++++++++++++--- .../onebranch/steps/publish-symbols-step.yml | 13 ++++++++----- .../onebranch/variables/onebranch-variables.yml | 10 ++++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/eng/pipelines/onebranch/jobs/publish-symbols-job.yml b/eng/pipelines/onebranch/jobs/publish-symbols-job.yml index 21b20476cd..adcd4da41b 100644 --- a/eng/pipelines/onebranch/jobs/publish-symbols-job.yml +++ b/eng/pipelines/onebranch/jobs/publish-symbols-job.yml @@ -54,11 +54,13 @@ jobs: - job: publish_symbols_${{ parameters.packageName }} displayName: 'Publish Symbols: ${{ parameters.packageFullName }}' pool: - type: windows + type: linux variables: - # OneBranch requires ob_outputDirectory to be set, even if this job produces no artifacts. - ob_outputDirectory: $(JOB_OUTPUT) + # OneBranch requires ob_outputDirectory to be set. Pipeline Artifacts are always on and + # cannot be disabled. To prevent this job from publishing artifacts, a .artifactignore + # that excludes all files is written into ob_outputDirectory before the auto-publish step. + ob_outputDirectory: $(Build.SourcesDirectory)/no_publish # Disable SDL scanning — this job only uploads/publishes PDBs and produces no # assemblies to scan. APIScan and BinSkim are handled by the build jobs. @@ -73,6 +75,13 @@ jobs: symbolsPath: $(Pipeline.Workspace)/${{ parameters.artifactName }}/symbols steps: + # Create ob_outputDirectory with a .artifactignore that excludes everything, + # so OneBranch's auto-publish uploads an empty artifact. + - pwsh: | + New-Item -Path "$(ob_outputDirectory)" -ItemType Directory -Force + "**" | Out-File -FilePath "$(ob_outputDirectory)/.artifactignore" -Encoding ascii + displayName: 'Suppress artifact publishing' + - task: DownloadPipelineArtifact@2 displayName: 'Download ${{ parameters.packageFullName }} Artifact' inputs: diff --git a/eng/pipelines/onebranch/steps/publish-symbols-step.yml b/eng/pipelines/onebranch/steps/publish-symbols-step.yml index d06b3c73e5..1f6734fb23 100644 --- a/eng/pipelines/onebranch/steps/publish-symbols-step.yml +++ b/eng/pipelines/onebranch/steps/publish-symbols-step.yml @@ -75,16 +75,19 @@ parameters: type: string steps: - # Set variable for downstream tasks (allegedly). + # Set the ARTIFACTSERVICES_SYMBOL_ACCOUNTNAME environment variable required by the publishing + # tasks, as documented here: # - # Note: Because variables cannot be set in top-level of template, this has to be done during - # runtime. + # https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL#Option_B:_OneBranch + # + # Note that Azure Pipelines converts variable names with dots to all-caps with underscores when + # setting environment variables. # - script: 'echo ##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{ parameters.uploadAccount }}' displayName: 'Set ArtifactServices.Symbol.AccountName to ${{ parameters.uploadAccount }}' - # Log the PDB files that match the search pattern so we can verify no - # unexpected files are included in the upload. + # Log the PDB files that match the search pattern so we can verify no unexpected files are + # included in the upload. - pwsh: | $folder = '${{ parameters.symbolsFolder }}' $glob = '${{ parameters.searchPattern }}' diff --git a/eng/pipelines/onebranch/variables/onebranch-variables.yml b/eng/pipelines/onebranch/variables/onebranch-variables.yml index b3bebb0a64..d5fa784885 100644 --- a/eng/pipelines/onebranch/variables/onebranch-variables.yml +++ b/eng/pipelines/onebranch/variables/onebranch-variables.yml @@ -39,6 +39,12 @@ variables: - name: Packaging.EnableSBOMSigning value: true - # Docker image which is used to build the project https://aka.ms/obpipelines/containers + # OneBranch supplies a variety of container images we must use for our jobs. + # + # Windows jobs use this image. - name: WindowsContainerImage - value: "onebranch.azurecr.io/windows/ltsc2022/vse2022:latest" + value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest + + # Linux jobs use this image. + - name: LinuxContainerImage + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 From 2a288ce580c88e279498a65f477a3c478591d7dd Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:10:40 -0300 Subject: [PATCH 2/3] Fix symbol upload on Linux: set AccountName at job scope PublishSymbols@2 runs on the OneBranch host agent (outside the build container) due to 1ES PT credential isolation. The previous runtime ##vso[task.setvariable] ran inside the container and was not visible to the host-level task. On Linux the host defaults to the Microsoft org, causing a VssUnauthorizedException (VS30063). Move ArtifactServices.Symbol.AccountName to a job-level variable so it is visible to both container and host steps. --- .../onebranch/jobs/publish-symbols-job.yml | 10 ++++++++++ .../onebranch/steps/publish-symbols-step.yml | 13 +++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/eng/pipelines/onebranch/jobs/publish-symbols-job.yml b/eng/pipelines/onebranch/jobs/publish-symbols-job.yml index adcd4da41b..de2d7e4634 100644 --- a/eng/pipelines/onebranch/jobs/publish-symbols-job.yml +++ b/eng/pipelines/onebranch/jobs/publish-symbols-job.yml @@ -74,6 +74,16 @@ jobs: # Path to the PDB files within the downloaded artifact. symbolsPath: $(Pipeline.Workspace)/${{ parameters.artifactName }}/symbols + # PublishSymbols@2 runs on the OneBranch host agent (outside the build container) due to 1ES + # Pipeline Template credential isolation. On Linux, the host resolves to the Microsoft org by + # default. Setting this variable at job level ensures the task sees it and connects to the + # correct org's symbol store. + # + # Reference: + # https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL#Option_B:_OneBranch + # + ArtifactServices.Symbol.AccountName: ${{ parameters.symbolsUploadAccount }} + steps: # Create ob_outputDirectory with a .artifactignore that excludes everything, # so OneBranch's auto-publish uploads an empty artifact. diff --git a/eng/pipelines/onebranch/steps/publish-symbols-step.yml b/eng/pipelines/onebranch/steps/publish-symbols-step.yml index 1f6734fb23..dc794f480d 100644 --- a/eng/pipelines/onebranch/steps/publish-symbols-step.yml +++ b/eng/pipelines/onebranch/steps/publish-symbols-step.yml @@ -75,16 +75,13 @@ parameters: type: string steps: - # Set the ARTIFACTSERVICES_SYMBOL_ACCOUNTNAME environment variable required by the publishing - # tasks, as documented here: + # NOTE: ArtifactServices.Symbol.AccountName is set as a job-level variable in + # publish-symbols-job.yml. On OneBranch Linux agents, PublishSymbols@2 runs on the host (outside + # the build container) due to 1ES PT credential isolation. A ##vso[task.setvariable] inside the + # container is not visible to host-level tasks, so the variable must be declared at job scope. # + # Reference: # https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL#Option_B:_OneBranch - # - # Note that Azure Pipelines converts variable names with dots to all-caps with underscores when - # setting environment variables. - # - - script: 'echo ##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{ parameters.uploadAccount }}' - displayName: 'Set ArtifactServices.Symbol.AccountName to ${{ parameters.uploadAccount }}' # Log the PDB files that match the search pattern so we can verify no unexpected files are # included in the upload. From a9315dca8eca39a33c0554183515541271f7132a Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:53:22 -0300 Subject: [PATCH 3/3] Route official symbols to PPE when releaseToProduction is false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The official pipeline's publishSymbols stage was hardcoded to the Production symbol server regardless of the releaseToProduction flag. This meant there was no way to do a full dry-run release targeting both NuGet Test and symbol server PPE. Now the releaseToProduction parameter drives both destinations: true → NuGet Production + Production symbol server false → NuGet Test + PPE symbol server The publishSymbols flag still independently controls whether symbols are published at all. The non-official pipeline is unchanged (always PPE). --- .../onebranch-pipeline-design.instructions.md | 6 +++++- eng/pipelines/onebranch/sqlclient-official.yml | 16 +++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/instructions/onebranch-pipeline-design.instructions.md b/.github/instructions/onebranch-pipeline-design.instructions.md index 0860d5db52..f1520fa0e0 100644 --- a/.github/instructions/onebranch-pipeline-design.instructions.md +++ b/.github/instructions/onebranch-pipeline-design.instructions.md @@ -70,6 +70,8 @@ When adding a new csproj-based package: - The `publish-symbols-step.yml` accepts a `symbolsFolder` parameter to point at the downloaded PDB location - The publish step calls an extracted `publish-symbols.ps1` script with structured error handling and diagnostic logging - Symbols publishing credentials come from the `Symbols Publishing` variable group +- In the official pipeline, symbol server destination follows `releaseToProduction`: Production when true, PPE when false +- Non-official pipeline always targets the PPE symbol server ## Release Stage @@ -99,7 +101,9 @@ Release parameters (all boolean, default `false`): - `releaseSqlServerServer`, `releaseLogging`, `releaseAbstractions`, `releaseSqlClient`, `releaseAzure`, `releaseAKVProvider` Official-only parameter: -- `releaseToProduction` — push to NuGet Production feed (default `false`) +- `releaseToProduction` — controls both NuGet target feed and symbol server destination (default `false`): + - `true` → NuGet Production feed + Production symbol server + - `false` → NuGet Test feed + PPE symbol server When `isPreview` is true, pipeline resolves `effective*Version` variables to preview versions; otherwise GA versions. All versions defined in `variables/common-variables.yml`. diff --git a/eng/pipelines/onebranch/sqlclient-official.yml b/eng/pipelines/onebranch/sqlclient-official.yml index a2902f1e21..96fb0dc5f5 100644 --- a/eng/pipelines/onebranch/sqlclient-official.yml +++ b/eng/pipelines/onebranch/sqlclient-official.yml @@ -33,9 +33,10 @@ parameters: type: boolean default: false - # Push packages to NuGet Production (otherwise pushes to NuGet Test). + # When true, publish symbols and push NuGet packages to Production environments. When false, + # symbols use PPE and NuGet packages use QA/Test. - name: releaseToProduction - displayName: Release to NuGet Production + displayName: Publish Symbols and NuGet Packages to Production type: boolean default: false @@ -254,9 +255,14 @@ extends: symbolsAzureSubscription: '$(SymbolsAzureSubscription)' symbolsPublishProjectName: '$(SymbolsPublishProjectNameSqlClient)' - # Official pipelines must publish to the Production symbol server. - symbolsPublishServer: '$(SymbolsPublishServerProd)' - symbolsPublishTokenUri: '$(SymbolsPublishTokenUriProd)' + # Symbol server target follows releaseToProduction: Production for + # real releases, PPE for test/QA releases. + ${{ if eq(parameters.releaseToProduction, true) }}: + symbolsPublishServer: '$(SymbolsPublishServerProd)' + symbolsPublishTokenUri: '$(SymbolsPublishTokenUriProd)' + ${{ else }}: + symbolsPublishServer: '$(SymbolsPublishServerPPE)' + symbolsPublishTokenUri: '$(SymbolsPublishTokenUriPPE)' symbolsUploadAccount: '$(SymbolsUploadAccount)' - template: /eng/pipelines/onebranch/stages/release-stages.yml@self