Skip to content
Open
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b845621
Add OneBranch release pipelines for mssql-python-odbc
jahnvi480 Jul 7, 2026
c19a261
Address PR review: fail-fast on wheel count, armory justification, Li…
jahnvi480 Jul 7, 2026
62910b4
Address review: preflight guard for odbcBuildDefinitionId placeholder…
jahnvi480 Jul 7, 2026
38b9ff9
Fold mssql-python-odbc build+release into existing pipelines
jahnvi480 Jul 8, 2026
d85fd92
Scope consolidate downloads by package to prevent cross-contamination
jahnvi480 Jul 8, 2026
e3cf2a9
Correct stale mssql-python wheel count in consolidate diagnostic (27 …
jahnvi480 Jul 8, 2026
a4b4e59
Harden odbc release path: pin guard + wheel-content assertions
jahnvi480 Jul 8, 2026
b88e169
Hard-fail mssql-python consolidate on wheel-count mismatch
jahnvi480 Jul 8, 2026
0346212
Merge branch 'main' into jahnvi/odbc-release-pipelines
jahnvi480 Jul 10, 2026
d1b3d46
odbc: add mssql-python-odbc selector to dummy release pipeline
jahnvi480 Jul 10, 2026
9f93c36
odbc: align dummy release pipeline mssql-python handling with official
jahnvi480 Jul 10, 2026
2d93fe0
odbc: bump mssql-python-odbc pin to 18.6.2 in release pipelines
jahnvi480 Jul 10, 2026
a4e3968
build: add buildPackage selector to build pipeline
jahnvi480 Jul 10, 2026
9d3097c
build: nightly build always builds both packages
jahnvi480 Jul 10, 2026
fdb02cb
Add OneBranch release pipelines for mssql-python-odbc
jahnvi480 Jul 7, 2026
79d60ba
Address PR review: fail-fast on wheel count, armory justification, Li…
jahnvi480 Jul 7, 2026
d4b82a6
Address review: preflight guard for odbcBuildDefinitionId placeholder…
jahnvi480 Jul 7, 2026
68b7ba0
Fold mssql-python-odbc build+release into existing pipelines
jahnvi480 Jul 8, 2026
7a1d351
Scope consolidate downloads by package to prevent cross-contamination
jahnvi480 Jul 8, 2026
7e5947e
Correct stale mssql-python wheel count in consolidate diagnostic (27 …
jahnvi480 Jul 8, 2026
68cba53
Harden odbc release path: pin guard + wheel-content assertions
jahnvi480 Jul 8, 2026
0772cc0
Hard-fail mssql-python consolidate on wheel-count mismatch
jahnvi480 Jul 8, 2026
52dbd7b
odbc: add mssql-python-odbc selector to dummy release pipeline
jahnvi480 Jul 10, 2026
19f1db1
odbc: align dummy release pipeline mssql-python handling with official
jahnvi480 Jul 10, 2026
2038ca6
odbc: bump mssql-python-odbc pin to 18.6.2 in release pipelines
jahnvi480 Jul 10, 2026
926ba53
build: add buildPackage selector to build pipeline
jahnvi480 Jul 10, 2026
72c3c88
build: nightly build always builds both packages
jahnvi480 Jul 10, 2026
b15c8ad
CHORE: reconcile GH pipeline Option A both-wheels test with validated…
jahnvi480 Jul 17, 2026
3caadc7
Merge branch 'jahnvi/odbc-release-pipelines' of https://github.com/mi…
jahnvi480 Jul 17, 2026
9580488
Merge branch 'main' into jahnvi/odbc-release-pipelines
jahnvi480 Jul 20, 2026
564ee03
Merge branch 'main' of https://github.com/microsoft/mssql-python into…
jahnvi480 Jul 20, 2026
9de3f4d
Merge branch 'jahnvi/odbc-release-pipelines' of https://github.com/mi…
jahnvi480 Jul 20, 2026
7795a2d
FIX: resolve DevSkim localhost finding and harden both-wheels test stage
jahnvi480 Jul 20, 2026
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
272 changes: 212 additions & 60 deletions OneBranchPipelines/build-release-package-pipeline.yml

Large diffs are not rendered by default.

97 changes: 81 additions & 16 deletions OneBranchPipelines/dummy-release-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ pr: none

# Parameters for DUMMY release pipeline
parameters:
# Which package to test-release. Both are produced by the same build pipeline
# (definition 2199); this switches the consolidated artifact and messaging.
- name: releasePackage
displayName: '[TEST] Package to Release'
type: string
values:
- 'mssql-python'
- 'mssql-python-odbc'
default: 'mssql-python'

- name: publishSymbols
displayName: '[TEST] Publish Symbols to Symbol Servers'
type: boolean
Expand All @@ -32,6 +42,16 @@ variables:
- group: 'ESRP Federated Creds (AME)' # Contains ESRP signing credentials
- group: 'Symbols Publishing' # Contains SymbolServer, SymbolTokenUri variables

# Select which consolidated artifact to download based on the target package.
# Both are produced by the same build pipeline (definition 2199):
# mssql-python -> drop_Consolidate_ConsolidateArtifacts
# mssql-python-odbc -> drop_ConsolidateOdbc_ConsolidateArtifacts
- name: consolidatedArtifactName
${{ if eq(parameters.releasePackage, 'mssql-python-odbc') }}:
value: 'drop_ConsolidateOdbc_ConsolidateArtifacts'
${{ else }}:
value: 'drop_Consolidate_ConsolidateArtifacts'

# OneBranch resources
resources:
repositories:
Expand Down Expand Up @@ -114,7 +134,7 @@ extends:
definition: 2199 # Build-Release-Package-Pipeline definition ID
buildVersionToDownload: 'specific'
buildId: $(resources.pipeline.buildPipeline.runID) # Use the build run selected in UI
artifactName: 'drop_Consolidate_ConsolidateArtifacts' # Consolidated artifact with dist/ and symbols/
artifactName: '${{ variables.consolidatedArtifactName }}' # mssql-python or mssql-python-odbc consolidated wheels
targetPath: '$(Build.SourcesDirectory)/artifacts'

# Step 2: List downloaded artifacts for verification
Expand Down Expand Up @@ -175,13 +195,58 @@ extends:
Write-Host "Symbols: $(if ($symbols) { $symbols.Count } else { 0 }) files"
Write-Host "====================================="

# Step 3: Validate mssql-py-core is a stable version (no dev/alpha/beta/rc)
- task: PowerShell@2
displayName: '[TEST] Validate mssql-py-core is a stable version'
inputs:
targetType: 'filePath'
filePath: '$(Build.SourcesDirectory)\OneBranchPipelines\scripts\validate-release-versions.ps1'
arguments: '-VersionFile "$(Build.SourcesDirectory)\artifacts\dist\mssql-py-core.version"'
# Step 3: Validate mssql-py-core is a stable version (mssql-python only;
# mssql-python-odbc ships no mssql-py-core.version file)
- ${{ if eq(parameters.releasePackage, 'mssql-python') }}:
- task: PowerShell@2
displayName: '[TEST] Validate mssql-py-core is a stable version'
inputs:
targetType: 'filePath'
filePath: '$(Build.SourcesDirectory)\OneBranchPipelines\scripts\validate-release-versions.ps1'
arguments: '-VersionFile "$(Build.SourcesDirectory)\artifacts\dist\mssql-py-core.version"'

# Guard: the released mssql-python wheel MUST depend on the pinned
# mssql-python-odbc package. Catches releasing from a PRE-pin build
# (e.g. the wrong build run), which would ship a driver-less package
# that breaks 'pip install mssql-python'. By design this FAILS on a
# pre-pin build. Update $expectedVersion when the odbc pin bumps.
- task: PowerShell@2
displayName: '[TEST] Validate mssql-python-odbc pin in wheel metadata'
inputs:
targetType: 'inline'
script: |
$ErrorActionPreference = 'Stop'
$expectedVersion = '18.6.2'
$wheels = Get-ChildItem -Path "$(Build.SourcesDirectory)/artifacts/dist" -Filter "mssql_python-*.whl"
if ($wheels.Count -eq 0) {
Write-Error "No mssql_python-*.whl found in artifacts/dist to validate the odbc pin."
exit 1
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
$wheel = $wheels[0].FullName
Write-Host "Inspecting wheel metadata: $wheel"
$zip = [System.IO.Compression.ZipFile]::OpenRead($wheel)
$metaEntry = $zip.Entries | Where-Object { $_.FullName -match '\.dist-info/METADATA$' } | Select-Object -First 1
if (-not $metaEntry) {
$zip.Dispose()
Write-Error "METADATA not found inside $wheel"
exit 1
}
$reader = New-Object System.IO.StreamReader($metaEntry.Open())
$metadata = $reader.ReadToEnd()
$reader.Dispose()
$zip.Dispose()
$odbcLines = ($metadata -split "`n") | Where-Object { $_ -match 'Requires-Dist:\s*mssql[-_]python[-_]odbc' }
if ($odbcLines) {
Write-Host "odbc dependency in metadata:"
$odbcLines | ForEach-Object { Write-Host " $($_.Trim())" }
}
$pinRegex = 'Requires-Dist:\s*mssql[-_]python[-_]odbc\s*==\s*' + [regex]::Escape($expectedVersion)
if ($metadata -notmatch $pinRegex) {
Write-Error "mssql-python wheel does not pin mssql-python-odbc==$expectedVersion. Are you releasing from the POST-pin build? mssql-python must depend on the pinned odbc package before release."
exit 1
}
Write-Host "OK: wheel depends on mssql-python-odbc==$expectedVersion"

# Step 4: Verify wheel integrity
- task: PowerShell@2
Expand Down Expand Up @@ -217,8 +282,8 @@ extends:

Write-Host "`nAll wheels verified successfully!"

# Step 5: Publish Symbols (if enabled and symbols exist)
- ${{ if eq(parameters.publishSymbols, true) }}:
# Step 5: Publish Symbols (if enabled and symbols exist; mssql-python only)
- ${{ if and(eq(parameters.publishSymbols, true), eq(parameters.releasePackage, 'mssql-python')) }}:
- template: /OneBranchPipelines/steps/symbol-publishing-step.yml@self
parameters:
SymbolsFolder: '$(Build.SourcesDirectory)/symbols'
Expand All @@ -233,13 +298,13 @@ extends:
Write-Host "===================================="
Write-Host "⚠️ TEST PIPELINE - DRY RUN MODE ⚠️"
Write-Host "===================================="
Write-Host "Package: mssql-python (TEST)"
Write-Host "Package: ${{ parameters.releasePackage }} (TEST)"
Write-Host ""
Write-Host "Actions performed:"
Write-Host "✓ Downloaded wheels from build pipeline"
Write-Host "✓ Verified wheel integrity"
Write-Host "✓ Downloaded symbols from build pipeline"
if ("${{ parameters.publishSymbols }}" -eq "True") {
if ("${{ parameters.publishSymbols }}" -eq "True" -and "${{ parameters.releasePackage }}" -eq "mssql-python") {
Write-Host "✓ Published symbols to SqlClientDrivers org"
}
Write-Host "✗ ESRP dummy release NOT performed (parameter disabled)"
Expand Down Expand Up @@ -280,7 +345,7 @@ extends:
definition: 2199
buildVersionToDownload: 'specific'
buildId: $(resources.pipeline.buildPipeline.runID)
artifactName: 'drop_Consolidate_ConsolidateArtifacts'
artifactName: '${{ variables.consolidatedArtifactName }}'
targetPath: '$(Build.SourcesDirectory)/artifacts'

# ⚠️ IMPORTANT: Uses Maven ContentType for testing - NOT PyPI!
Expand Down Expand Up @@ -313,11 +378,11 @@ extends:
Write-Host "===================================="
Write-Host "⚠️ TEST PIPELINE - DUMMY RELEASE COMPLETED ⚠️"
Write-Host "===================================="
Write-Host "Package: mssql-python (TEST)"
Write-Host "Package: ${{ parameters.releasePackage }} (TEST)"
Write-Host "ContentType: Maven (NOT PyPI - Safe for Testing)"
Write-Host "Owners: $(owner)"
Write-Host "Approvers: $(approver)"
Write-Host "Symbols Published: ${{ parameters.publishSymbols }}"
Write-Host "Symbols Published: $(if ('${{ parameters.publishSymbols }}' -eq 'True' -and '${{ parameters.releasePackage }}' -eq 'mssql-python') { 'True' } else { 'False' })"
Write-Host "====================================="
Write-Host ""
Write-Host "⚠️ IMPORTANT: This was a DUMMY release using Maven ContentType"
Expand All @@ -326,7 +391,7 @@ extends:
Write-Host "What was tested:"
Write-Host "✓ Artifact download from build pipeline"
Write-Host "✓ Wheel integrity verification"
if ("${{ parameters.publishSymbols }}" -eq "True") {
if ("${{ parameters.publishSymbols }}" -eq "True" -and "${{ parameters.releasePackage }}" -eq "mssql-python") {
Write-Host "✓ Symbol publishing to SqlClientDrivers org"
}
Write-Host "✓ ESRP release workflow (Maven ContentType)"
Expand Down
30 changes: 21 additions & 9 deletions OneBranchPipelines/jobs/consolidate-artifacts-job.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Consolidate Artifacts Job Template
# Downloads artifacts from all platform build stages and consolidates into single dist/ folder
# Works with individual stage artifacts (15 stages total: 7 Windows + 4 macOS + 4 Linux)
# Each Linux stage builds 4 Python versions, resulting in 27 total wheels
# Works with individual stage artifacts (18 stages total: 9 Windows + 5 macOS + 4 Linux)
# Each Linux stage builds 5 Python versions (3.10-3.14), resulting in 34 total wheels
parameters:
- name: oneBranchType
type: string
Expand Down Expand Up @@ -29,13 +29,20 @@ jobs:
- checkout: self
fetchDepth: 1

# Download ALL artifacts from current build
# Matrix jobs publish as: Windows_<JobId>, macOS_<JobId>, Linux_<JobId>
# This downloads all of them automatically (27 total artifacts)
# Download only the mssql-python platform-stage artifacts from the current build.
# The build definition now ALSO produces mssql-python-odbc artifacts (drop_ODBC_*)
# in the SAME run, so we scope this download with itemPattern to the mssql-python
# stages (drop_Win_*, drop_MacOS_*, drop_Linux_*). Without this, the odbc wheels
# would be swept into this package's dist/ and shipped to the mssql-python PyPI
# project. The odbc wheels are consolidated separately (ConsolidateOdbc stage).
- task: DownloadPipelineArtifact@2
displayName: 'Download All Platform Artifacts'
inputs:
buildType: 'current'
itemPattern: |
drop_Win_*/**
drop_MacOS_*/**
drop_Linux_*/**
targetPath: '$(Pipeline.Workspace)/all-artifacts'

# Consolidate all wheels into single dist/ directory
Expand Down Expand Up @@ -68,12 +75,17 @@ jobs:
echo ""
WHEEL_COUNT=$(ls -1 $(ob_outputDirectory)/dist/*.whl 2>/dev/null | wc -l)
echo "Total wheel count: $WHEEL_COUNT"
echo "Expected: 27 wheels (7 Windows + 4 macOS + 16 Linux)"
echo "Expected: 34 wheels (9 Windows + 5 macOS + 20 Linux)"

if [ "$WHEEL_COUNT" -ne 27 ]; then
echo "WARNING: Expected 27 wheels but found $WHEEL_COUNT"
# Hard-fail on a wheel-count mismatch (symmetric with the odbc consolidate job).
# A mismatch means a build stage silently produced a partial set (e.g. a missing
# Python version) or the build matrix changed without updating this count.
# Update the expected 34 whenever the Windows/macOS/Linux build matrix changes.
if [ "$WHEEL_COUNT" -ne 34 ]; then
echo "ERROR: Expected 34 wheels but found $WHEEL_COUNT" >&2
exit 1
else
echo "SUCCESS: All 27 wheels consolidated!"
echo "SUCCESS: All 34 wheels consolidated!"
fi
displayName: 'Consolidate wheels from all platforms'

Expand Down
75 changes: 75 additions & 0 deletions OneBranchPipelines/jobs/consolidate-odbc-artifacts-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Consolidate ODBC Artifacts Job Template
# Downloads the per-platform `mssql-python-odbc` wheels from all build stages and
# consolidates them into a single dist/ folder for the release pipeline to publish.
# Expected: 7 wheels (2 Windows + 1 macOS universal2 + 4 Linux).
parameters:
- name: oneBranchType
type: string
default: 'Official'
- name: expectedWheelCount
type: number
default: 7

jobs:
- job: ConsolidateArtifacts
displayName: 'Consolidate All ODBC Platform Artifacts'
condition: succeeded()

pool:
type: linux
isCustom: true
name: Azure Pipelines
vmImage: 'ubuntu-latest'

variables:
# Consolidation only moves files; no binaries to scan.
- name: ob_sdl_binskim_enabled
value: false
- name: ob_outputDirectory
value: '$(Build.ArtifactStagingDirectory)'

steps:
- checkout: self
fetchDepth: 1

# Download only the mssql-python-odbc platform-stage artifacts (drop_ODBC_*) from
# the current build. The same run also builds the mssql-python wheels (drop_Win_*,
# drop_MacOS_*, drop_Linux_*); itemPattern scopes this download to the odbc stages
# so ONLY the 7 odbc wheels are consolidated here (not the mssql-python wheels).
- task: DownloadPipelineArtifact@2
displayName: 'Download All ODBC Platform Artifacts'
inputs:
buildType: 'current'
itemPattern: 'drop_ODBC_*/**'
targetPath: '$(Pipeline.Workspace)/all-artifacts'

- bash: |
set -e
mkdir -p $(ob_outputDirectory)/dist

echo "Finding all .whl files..."
find $(Pipeline.Workspace)/all-artifacts -name "*.whl" -exec ls -lh {} \;

echo "Copying all wheels to consolidated dist/..."
find $(Pipeline.Workspace)/all-artifacts -name "*.whl" -exec cp -v {} $(ob_outputDirectory)/dist/ \;

echo "Consolidated wheels:"
ls -lh $(ob_outputDirectory)/dist/
WHEEL_COUNT=$(ls -1 $(ob_outputDirectory)/dist/*.whl 2>/dev/null | wc -l)
echo "Total wheel count: $WHEEL_COUNT (expected ${{ parameters.expectedWheelCount }})"
if [ "$WHEEL_COUNT" -ne "${{ parameters.expectedWheelCount }}" ]; then
echo "ERROR: expected ${{ parameters.expectedWheelCount }} wheels but found $WHEEL_COUNT" >&2
exit 1
fi
Comment thread
Copilot marked this conversation as resolved.
echo "SUCCESS: all ${{ parameters.expectedWheelCount }} ODBC wheels consolidated."
displayName: 'Consolidate ODBC wheels'

- task: PublishPipelineArtifact@1
displayName: 'Publish Consolidated ODBC Artifacts'
inputs:
targetPath: '$(ob_outputDirectory)'
# Distinct name so it does not collide with the mssql-python consolidate
# artifact (drop_Consolidate_ConsolidateArtifacts) in the same build run.
# Matches the OneBranch auto-name for a stage named `ConsolidateOdbc`.
artifact: 'drop_ConsolidateOdbc_ConsolidateArtifacts'
publishLocation: 'pipeline'
Loading
Loading