Skip to content

CHORE: Add OneBranch release pipelines for mssql-python-odbc#664

Open
jahnvi480 wants to merge 33 commits into
mainfrom
jahnvi/odbc-release-pipelines
Open

CHORE: Add OneBranch release pipelines for mssql-python-odbc#664
jahnvi480 wants to merge 33 commits into
mainfrom
jahnvi/odbc-release-pipelines

Conversation

@jahnvi480

@jahnvi480 jahnvi480 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

AB#46218


Summary

Adds the ADO / OneBranch release pipelines for the standalone mssql-python-odbc package. This is the CI/CD half of the ODBC driver split; the package code and build script are in companion PR #663.

Files

  • OneBranchPipelines/build-release-odbc-pipeline.yml - build orchestration. Fans out to 7 platform build stages plus a consolidate stage, with path-filtered triggers scoped to setup_odbc.py, mssql_python_odbc/, mssql_python/libs/, and these pipeline files. SDL keeps credscan / policheck / sbom / tsa and disables binskim / apiscan / armory / codeql (data-only package, no first-party compiled binaries) with justifications.
  • OneBranchPipelines/stages/build-odbc-windows-stage.yml - Windows amd64 / arm64.
  • OneBranchPipelines/stages/build-odbc-macos-stage.yml - macOS universal2.
  • OneBranchPipelines/stages/build-odbc-linux-stage.yml - manylinux_2_28 and musllinux_1_2 (x86_64 / aarch64) built in tdslibrs ACR PyPA containers. No compilation happens here; the musllinux tag is auto-detected in setup_odbc.py via platform.libc_ver(), so the hardcoded MANYLINUX_TAG only affects glibc builds.
  • OneBranchPipelines/jobs/consolidate-odbc-artifacts-job.yml - gathers all wheels and verifies the count is exactly 7.
  • OneBranchPipelines/official-release-odbc-pipeline.yml - ESRP-based PyPI release, gated by releaseToPyPI. The EsrpRelease@9 step is identical to the existing mssql-python release pipeline. A preflight step fails with a clear message if odbcBuildDefinitionId is still the 0 placeholder, so the artifact download can't fail cryptically.

Wheels produced (7, py3-none-)

win_amd64, win_arm64, macosx_15_0_universal2, manylinux_2_28_x86_64, manylinux_2_28_aarch64, musllinux_1_2_x86_64, musllinux_1_2_aarch64.

Infra follow-ups (marked "Infra setup:" in the YAML)

  • Onboard the mssql-python-odbc package name to ESRP for PyPI release.
  • Register the build pipeline in ADO and set odbcBuildDefinitionId in official-release-odbc-pipeline.yml (currently placeholder 0; the release pipeline now preflight-checks this and errors clearly if left at 0), and confirm pipelines.source matches the registered definition name.
  • Grant the new pipeline(s) access to the ESRP Federated Creds (AME) variable group.

Notes

These pipelines are inert until they are registered as ADO build definitions; merging the YAML alone runs nothing. Pairs with #663.

Adds the build and release pipelines for the standalone mssql-python-odbc package: per-platform build stages (Windows x64/arm64, macOS universal2, Linux manylinux_2_28 + musllinux_1_2 for x86_64/aarch64), a consolidate job that gathers all 7 py3-none wheels, the build orchestration pipeline, and the ESRP-based official release pipeline.

The build stages run no compilation (data-only package); Linux builds run in tdslibrs ACR PyPA containers and the musllinux tag is auto-detected via libc_ver. Infra follow-ups (ESRP onboarding of the mssql-python-odbc name, registering the ADO build definition to set odbcBuildDefinitionId, and sharing the ESRP variable group) are marked with TODO(infra) comments.
Copilot AI review requested due to automatic review settings July 7, 2026 15:09
@github-actions github-actions Bot added the pr-size: large Substantial code update label Jul 7, 2026
Comment thread OneBranchPipelines/official-release-odbc-pipeline.yml Fixed
Comment thread OneBranchPipelines/official-release-odbc-pipeline.yml Fixed
Comment thread OneBranchPipelines/official-release-odbc-pipeline.yml Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds Azure DevOps OneBranch build + official release pipeline YAMLs to produce and publish the standalone mssql-python-odbc “data-only” wheel set (7 platform-specific py3-none-<platform> wheels), aligning the CI/CD half of the ODBC-driver split with the existing mssql-python OneBranch approach.

Changes:

  • Introduces a new OneBranch build pipeline that fans out into Windows/macOS/Linux wheel build stages and then consolidates artifacts.
  • Adds per-OS stage templates for building the ODBC wheels (Windows x64/arm64, macOS universal2, Linux manylinux/musllinux x86_64/aarch64).
  • Adds an official ESRP-based release pipeline to publish the consolidated wheels to PyPI (gated by releaseToPyPI).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
OneBranchPipelines/build-release-odbc-pipeline.yml Orchestrates multi-stage build for 7 ODBC wheels + consolidation; configures global SDL settings for this package.
OneBranchPipelines/stages/build-odbc-windows-stage.yml Builds/stages Windows ODBC wheels per-arch and publishes per-stage artifacts.
OneBranchPipelines/stages/build-odbc-macos-stage.yml Builds/stages macOS universal2 ODBC wheel and publishes per-stage artifacts.
OneBranchPipelines/stages/build-odbc-linux-stage.yml Builds/stages Linux ODBC wheels via PyPA containers (manylinux/musllinux) and publishes per-stage artifacts.
OneBranchPipelines/jobs/consolidate-odbc-artifacts-job.yml Downloads all stage artifacts, consolidates wheels into a single dist/, and publishes the consolidated artifact.
OneBranchPipelines/official-release-odbc-pipeline.yml Downloads consolidated artifact and (optionally) releases to PyPI via ESRP.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread OneBranchPipelines/official-release-odbc-pipeline.yml Outdated
Comment thread OneBranchPipelines/build-release-odbc-pipeline.yml Outdated
Comment thread OneBranchPipelines/jobs/consolidate-odbc-artifacts-job.yml
Comment thread OneBranchPipelines/stages/build-odbc-linux-stage.yml
Comment thread OneBranchPipelines/stages/build-odbc-linux-stage.yml
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

100%


🎯 Overall Coverage

80%


📈 Total Lines Covered: 6743 out of 8343
📁 Project: mssql-python


Diff Coverage

Diff: main...HEAD, staged and unstaged changes

No lines with coverage information in this diff.


📋 Files Needing Attention

📉 Files with overall lowest coverage (click to expand)
mssql_python.pybind.logger_bridge.cpp: 59.2%
mssql_python.pybind.ddbc_bindings.h: 59.9%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.connection.connection.cpp: 76.2%
mssql_python.pybind.ddbc_bindings.cpp: 76.2%
mssql_python.__init__.py: 77.3%
mssql_python.row.py: 77.6%
mssql_python.ddbc_bindings.py: 79.6%
mssql_python.connection.py: 83.6%
mssql_python.logging.py: 85.5%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

jahnvi480 and others added 17 commits July 7, 2026 21:43
…nux twine check + container cleanup, reword infra TODOs
…; drop unused targetArch var on Windows stage
Per review feedback, build and release the standalone mssql-python-odbc package from the existing Build-Release-Package-Pipeline (def 2199) and official release pipeline instead of separate ADO definitions.

Build pipeline: add odbcWindows/Macos/LinuxConfigs params (2+1+4=7 data-only wheels), reference the 3 odbc stage templates alongside the mssql-python stages, and add a separate ConsolidateOdbc stage publishing drop_ConsolidateOdbc_ConsolidateArtifacts (distinct from mssql-python's drop_Consolidate_ConsolidateArtifacts).

Release pipeline: add releasePackage parameter (mssql-python | mssql-python-odbc); select the consolidated artifact accordingly and skip symbol publishing + mssql-py-core version validation for odbc.

Delete the redundant standalone orchestrators (build-release-odbc-pipeline.yml, official-release-odbc-pipeline.yml). This removes the need to register a new ADO build definition and the odbcBuildDefinitionId placeholder. Both packages now build from def 2199; the odbc stages consume setup_odbc.py from PR #663, so #663 must merge before these stages can build.
Def 2199 now builds mssql-python and mssql-python-odbc in one run. Both consolidate jobs used DownloadPipelineArtifact@2 with buildType: current, which downloads every artifact in the run and copies all *.whl into dist/. In the merged run this caused: (1) the main Consolidate to sweep the 7 odbc wheels into the mssql-python release (count is warning-only, so silent), and (2) ConsolidateOdbc to see 34 wheels while expecting 7 and hard-fail. Add itemPattern to scope each download to its own package's build-stage artifacts (main: drop_Win_*/drop_MacOS_*/drop_Linux_*; odbc: drop_ODBC_*), giving clean partitioning with no wheel leakage.
…-> 34)

The matrix is 9 Windows + 5 macOS + 4 Linux stages x 5 Python versions (cp310-cp314) = 34 wheels across 18 stages. The diagnostic said 27 (7+4+16), stale from an older 3.10-3.13 / 7-Windows matrix. Count check remains warning-only (no behavior change).
official-release-pipeline.yml: gate 'Published symbols' dry-run/summary text to mssql-python so odbc runs no longer claim symbol publishing that never happens; add a mssql-python-only step asserting the wheel metadata pins mssql-python-odbc==18.6.0 (fails by design if released from a pre-pin build). build-odbc-{linux,macos,windows}-stage.yml: assert each built wheel actually contains the ODBC driver binary (msodbcsql*), guarding against the .gitignore '*.so' trap / silent sync_libs() skip shipping an empty data wheel that still passes twine check.
Flip the 34-wheel count check from WARNING-only to a hard exit 1, symmetric with the odbc consolidate job's count guard. A mismatch means a build stage silently produced a partial wheel set (e.g. a missing Python version) or the matrix changed without updating the expected count -- previously this shipped silently.
Mirrors the official-release-pipeline odbc changes: releasePackage parameter, consolidatedArtifactName selection for both artifact downloads, and gating the mssql-py-core version validation + symbol publishing to mssql-python only. Stays on Maven ContentType so it never publishes to PyPI.
Adds the mssql-python-odbc pin guard (gated to mssql-python, matching official-release-pipeline) and gates the 'symbols published' summary messaging on releasePackage==mssql-python so the dummy pipeline exercises the exact same mssql-python validation/messaging as the official pipeline for both packages.
Gate mssql-python and mssql-python-odbc stage groups behind a buildPackage parameter (mssql-python default) so only the selected package builds, halving run time and enabling concurrent per-package builds. Mirrors the releasePackage selector pattern in the release pipelines.
Add effectiveBuildPackage variable that forces 'both' on scheduled (Build.Reason=Schedule) runs while manual/PR runs honour the buildPackage parameter. Mirrors the effectiveOneBranchType pattern so the nightly keeps refreshing every artifact.
### Work Item / Issue Reference

> GitHub Issue: #626

-------------------------------------------------------------------
### Summary

cursor.bulkcopy() opens a separate connection through mssql-py-core,
which defaulted to a hardcoded 15s connect timeout with no way to
override it from Python. I forward the cursor's query timeout (set via
connect(timeout=X)) into pycore's connect_timeout when it's set, so the
bulk copy connection honors the same limit. timeout=0 stays a
no-override, leaving pycore on its 15s default. added tests covering the
positive forward, the zero case, and that the cursor snapshot (not a
later live connection change) is what's used.

---------

Co-authored-by: Gaurav Sharma <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Saurabh Singh <saurabh500@gmail.com>
… before tests (#658)

### Work Item / Issue Reference

> GitHub Issue: #656

-------------------------------------------------------------------
### Summary

on macOS the test job never removed system ODBC before running, so
mssql-python could load a Homebrew unixODBC driver manager / msodbcsql18
from the default dyld path and pass even if the bundled copy was broken.
every Linux job already uninstalls system ODBC before tests to force the
bundled driver. this brings macOS in line and adds a static check of the
bundle itself.

- new step in the macOS test job uninstalls Homebrew
msodbcsql18/mssql-tools18/unixodbc, deletes leftover libodbc/libodbcinst
dylibs and odbcinst config from both Homebrew prefixes, then otool -L's
the bundled driver to confirm it's intact. guarded so it's a no-op when
nothing is installed.
- new test (test_000_dependencies.py) walks the bundled driver's load
chain with otool and asserts every inter-library reference is
relocatable (@loader_path, not an absolute /opt/homebrew path), which is
what breaks on machines without Homebrew.
- brew trust on the mssql-release tap so the benchmark step can install
msodbcsql18 for the pyodbc comparison (newer Homebrew refuses untrusted
taps).

---------

Co-authored-by: Gaurav Sharma <sharmag@microsoft.com>
Co-authored-by: Gaurav Sharma <223556219+Copilot@users.noreply.github.com>
…edian, warmup) (#680)

### Work Item / Issue Reference

> ADO Work Item: Fixed
[AB#46419](https://sqlclientdrivers.visualstudio.com/c6d89619-62de-46a0-8b46-70b92a84d85e/_workitems/edit/46419)

-------------------------------------------------------------------
### Summary

the CI perf benchmark flags false regressions on perf-neutral PRs.
across 11 main runs of identical code, the same scenario's raw median
swings 1.42x-1.73x (CV 11-18%) purely from shared-runner jitter, so the
old 5% gate fired on ~34% of scenario comparisons (and ~30% false
"improvements").

this reworks `benchmarks/perf-benchmarking.py` measurement and
reporting. no pipeline changes, the step stays continueOnError:

- metric is now the median, first sample discarded as warmup (no extra
DB passes, so macOS timing is unchanged).
- vs-main uses a normalized score instead of raw time: mssql-python's
median relative to pyodbc measured on the same runner. pyodbc is pinned,
so dividing by it cancels runner-to-runner speed. on the 11 historical
runs this roughly halves variance on the I/O-bound scenarios (CV
18%->8%, 11%->5%) and trims the rest.
- regression/highlight threshold raised to 20% (real perf changes move
30%+), gates only with >= 3 samples, and falls back to raw median for
baselines that predate the normalized score.

validated with deterministic unit checks, including the key case where
absolute time doubles but the normalized score stays flat so nothing is
flagged.

---------

Co-authored-by: Gaurav Sharma <223556219+Copilot@users.noreply.github.com>
### Work Item / Issue Reference

> GitHub Issue: #247

-------------------------------------------------------------------
### Summary

adds `.github/copilot-instructions.md`: a from-scratch, repo-wide guide
for coding agents working on mssql-python. it covers the architecture
(python -> pybind `ddbc_bindings` -> odbc, with py-core/tds for bulk
copy), the validated build/test/validation-gate commands, the C++
shutdown/init/arch hazards, the credential-scanning localhost rule, PR
conventions, and a trust-and-validate close. it references the existing
`.github/prompts/` files instead of duplicating them, and every path
plus the CI-enforced title prefixes were checked against the current
tree.

---------

Co-authored-by: Gaurav Sharma <sharmag@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# Builds the platform-specific `mssql-python-odbc` wheel for ONE libc/arch combo
# (manylinux_2_28 or musllinux_1_2 x x86_64 or aarch64).
#
# Data-only package: no native compilation, no per-Python matrix, no pytest.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the odbc wheels ship the driver but nothing runs pytest against them as of now
the pr-validation & daily build-release pipelines resolve the driver from bundled mssql_python/libs, so GetOdbcLibsBaseDir never gets hit in any case actually

GetOdbcLibsBaseDir runs on every connect, but CI only ever hits its fallback branch (bundled libs) since nothing installs the odbc wheel. the external-resolution branch never executes, so a break there ships green.

I think we need a separate pytest step here and have a nightly build routine for mssql-python-odbc as well
we should test installing the built package in a clean env with latest mssql-python build & run the same pytest suite
no new tests or smoketest neded, just the existing suite pointed at the built package

sumitmsft
sumitmsft previously approved these changes Jul 16, 2026
Adds the build and release pipelines for the standalone mssql-python-odbc package: per-platform build stages (Windows x64/arm64, macOS universal2, Linux manylinux_2_28 + musllinux_1_2 for x86_64/aarch64), a consolidate job that gathers all 7 py3-none wheels, the build orchestration pipeline, and the ESRP-based official release pipeline.

The build stages run no compilation (data-only package); Linux builds run in tdslibrs ACR PyPA containers and the musllinux tag is auto-detected via libc_ver. Infra follow-ups (ESRP onboarding of the mssql-python-odbc name, registering the ADO build definition to set odbcBuildDefinitionId, and sharing the ESRP variable group) are marked with TODO(infra) comments.
…nux twine check + container cleanup, reword infra TODOs
…; drop unused targetArch var on Windows stage
jahnvi480 added 12 commits July 17, 2026 15:29
Per review feedback, build and release the standalone mssql-python-odbc package from the existing Build-Release-Package-Pipeline (def 2199) and official release pipeline instead of separate ADO definitions.

Build pipeline: add odbcWindows/Macos/LinuxConfigs params (2+1+4=7 data-only wheels), reference the 3 odbc stage templates alongside the mssql-python stages, and add a separate ConsolidateOdbc stage publishing drop_ConsolidateOdbc_ConsolidateArtifacts (distinct from mssql-python's drop_Consolidate_ConsolidateArtifacts).

Release pipeline: add releasePackage parameter (mssql-python | mssql-python-odbc); select the consolidated artifact accordingly and skip symbol publishing + mssql-py-core version validation for odbc.

Delete the redundant standalone orchestrators (build-release-odbc-pipeline.yml, official-release-odbc-pipeline.yml). This removes the need to register a new ADO build definition and the odbcBuildDefinitionId placeholder. Both packages now build from def 2199; the odbc stages consume setup_odbc.py from PR #663, so #663 must merge before these stages can build.
Def 2199 now builds mssql-python and mssql-python-odbc in one run. Both consolidate jobs used DownloadPipelineArtifact@2 with buildType: current, which downloads every artifact in the run and copies all *.whl into dist/. In the merged run this caused: (1) the main Consolidate to sweep the 7 odbc wheels into the mssql-python release (count is warning-only, so silent), and (2) ConsolidateOdbc to see 34 wheels while expecting 7 and hard-fail. Add itemPattern to scope each download to its own package's build-stage artifacts (main: drop_Win_*/drop_MacOS_*/drop_Linux_*; odbc: drop_ODBC_*), giving clean partitioning with no wheel leakage.
…-> 34)

The matrix is 9 Windows + 5 macOS + 4 Linux stages x 5 Python versions (cp310-cp314) = 34 wheels across 18 stages. The diagnostic said 27 (7+4+16), stale from an older 3.10-3.13 / 7-Windows matrix. Count check remains warning-only (no behavior change).
official-release-pipeline.yml: gate 'Published symbols' dry-run/summary text to mssql-python so odbc runs no longer claim symbol publishing that never happens; add a mssql-python-only step asserting the wheel metadata pins mssql-python-odbc==18.6.0 (fails by design if released from a pre-pin build). build-odbc-{linux,macos,windows}-stage.yml: assert each built wheel actually contains the ODBC driver binary (msodbcsql*), guarding against the .gitignore '*.so' trap / silent sync_libs() skip shipping an empty data wheel that still passes twine check.
Flip the 34-wheel count check from WARNING-only to a hard exit 1, symmetric with the odbc consolidate job's count guard. A mismatch means a build stage silently produced a partial wheel set (e.g. a missing Python version) or the matrix changed without updating the expected count -- previously this shipped silently.
Mirrors the official-release-pipeline odbc changes: releasePackage parameter, consolidatedArtifactName selection for both artifact downloads, and gating the mssql-py-core version validation + symbol publishing to mssql-python only. Stays on Maven ContentType so it never publishes to PyPI.
Adds the mssql-python-odbc pin guard (gated to mssql-python, matching official-release-pipeline) and gates the 'symbols published' summary messaging on releasePackage==mssql-python so the dummy pipeline exercises the exact same mssql-python validation/messaging as the official pipeline for both packages.
Gate mssql-python and mssql-python-odbc stage groups behind a buildPackage parameter (mssql-python default) so only the selected package builds, halving run time and enabling concurrent per-package builds. Mirrors the releasePackage selector pattern in the release pipelines.
Add effectiveBuildPackage variable that forces 'both' on scheduled (Build.Reason=Schedule) runs while manual/PR runs honour the buildPackage parameter. Mirrors the effectiveOneBranchType pattern so the nightly keeps refreshing every artifact.
… ADO design

Add hardened wheel-installation-test-stage.yml (positive+negative driver proof, source-tree shadowing guards, TESTED==0 fails stage) and wire two 'both'-gated TestBothWheels_* stages (manylinux_2_28 + musllinux x86_64) after ConsolidateOdbc, replacing the old single 3.12 invocation.
…crosoft/mssql-python into jahnvi/odbc-release-pipelines

# Conflicts:
#	OneBranchPipelines/build-release-package-pipeline.yml
echo "Waiting for SQL Server to be ready..."
for i in {1..30}; do
if docker exec sqlserver-$(LINUX_TAG)-$(ARCH) /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U SA -P "$(DB_PASSWORD)" -C -Q "SELECT 1" >/dev/null 2>&1; then
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: large Substantial code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants