Skip to content

FEAT: Add conda packaging recipes and publish tooling for mssql-python - #720

Draft
Jahnvi Thakkar (jahnvi480) wants to merge 122 commits into
mainfrom
jahnvi/conda-onboarding
Draft

FEAT: Add conda packaging recipes and publish tooling for mssql-python#720
Jahnvi Thakkar (jahnvi480) wants to merge 122 commits into
mainfrom
jahnvi/conda-onboarding

Conversation

@jahnvi480

@jahnvi480 Jahnvi Thakkar (jahnvi480) commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

AB#47315


Summary

This pull request introduces a complete, standalone Conda build and release pipeline for the mssql-python package, decoupling Conda packaging from the wheel/PyPI release process. The new pipelines ensure that Conda packages are built, validated, and published independently, with robust audit and gating steps to prevent regressions and incomplete releases.

Key changes include:

1. New Conda Build and Release Pipelines

  • Added OneBranchPipelines/conda-build-pipeline.yml: Implements a full, per-platform Conda build pipeline, repackaging prebuilt wheels into Conda packages for all supported OS/architectures, with validation and artifact consolidation.
  • Added OneBranchPipelines/conda-release-pipeline.yml: Introduces a dedicated release pipeline to validate the consolidated Conda packages and optionally publish them to Anaconda.org, with strict gating to ensure completeness and version consistency.

2. Automated Auditing on Pull Requests

  • Added .github/workflows/conda-audit.yml: Sets up a GitHub Actions workflow that builds and audits a Linux Conda package on every relevant PR, automatically failing the PR if regressions are detected in binary dependencies or audit checks.

3. Improved Separation and Safety

  • The Conda pipelines are now fully decoupled from the wheel/PyPI release pipeline, ensuring that Conda artifacts are not mixed or confused with wheel artifacts and that each release path is independently validated and gated.

4. Platform Coverage and Validation

  • The build pipeline covers all major platforms (Windows, macOS Intel/ARM, Linux x86_64/aarch64), using native and cross-builds as appropriate, and includes runtime validation (e.g., import tests, TLS probes) to ensure package integrity.

5. Secure, Gated Publishing

  • Publishing to Anaconda.org is strictly gated behind successful validation and is only enabled when explicitly requested, with secrets handled securely and never exposed in logs.

These changes significantly improve the reliability, auditability, and maintainability of the Conda packaging and release process for mssql-python.

Adds conda recipes for mssql-python and its mssql-python-odbc companion, a local build/test-before-live harness, and a draft OneBranch conda publish stage. Azure SDK deps resolve from the lean 'microsoft' Anaconda channel under --strict-channel-priority so conda-forge's azure-core recipe (which over-declares flask/six -> celery/boto3/botocore) does not bloat the environment. See conda-forge/azure-core-feedstock#71.
Copilot AI lite review requested due to automatic review settings August 14, 2026 08:50
@github-actions github-actions Bot added the pr-size: medium Moderate update size label Aug 14, 2026
Comment thread conda/mssql-python-odbc/meta.yaml 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

This PR adds initial conda packaging assets for mssql-python (and its mssql-python-odbc companion) so the project can be published to the Microsoft-owned microsoft Anaconda channel, alongside the existing PyPI wheel distribution.

Changes:

  • Adds conda recipes for mssql-python and mssql-python-odbc that repackage existing wheels (no compilation).
  • Adds a local PowerShell harness to build both recipes, create a local channel, and validate import / optional live-connect.
  • Adds a draft OneBranch stage to build/test/publish conda artifacts from signed release artifacts.

Reviewed changes

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

File Description
conda/onebranch-publish-conda-stage.yml Draft OneBranch stage to build from signed artifacts, gate on smoke tests, and publish to the microsoft Anaconda channel.
conda/mssql-python/meta.yaml Conda recipe for repackaging the mssql-python wheel and depending on a version-locked mssql-python-odbc.
conda/mssql-python-odbc/meta.yaml Conda recipe for repackaging the proprietary driver wheel into a companion conda package.
conda/build_and_test_local.ps1 Local “test-before-live” harness to build, index, install, and smoke test the conda packages.
Suppressed comments (1)

conda/onebranch-publish-conda-stage.yml:133

  • Same string-vs-boolean condition issue here: quoting the template expression turns it into a string, which can cause the publish step to be skipped unexpectedly even when publishToConda is true.
          - task: PowerShell@2
            displayName: 'Publish to anaconda.org/microsoft'
            condition: and(succeeded(), eq('${{ parameters.publishToConda }}', true))

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread conda/mssql-python/meta.yaml Outdated
Comment thread conda/mssql-python-odbc/meta.yaml Outdated
Comment thread conda/build_and_test_local.ps1 Outdated
Comment thread conda/onebranch-publish-conda-stage.yml Outdated
Comment thread conda/mssql-python-odbc/meta.yaml Outdated
Comment thread conda/build_and_test_local.ps1 Outdated
Recipes now honor ARTIFACTS_PY/ARTIFACTS_ODBC to install signed wheels offline (--no-index --find-links) instead of always hitting PyPI. build_and_test_local.ps1 adds --override-channels for reproducible solves and its header no longer claims an offline/no-microsoft-channel run. Draft publish stage condition compares the boolean parameter directly (eq(param, true)) instead of a quoted string.
Comment thread conda/mssql-python-odbc/meta.yaml Fixed
- Single-source versions via MSSQL_PYTHON_VERSION/MSSQL_ODBC_VERSION env (wired from the publish-stage params) so the package version and the companion pin can't drift.

- Point mssql-python-odbc license_file at the actual ODBC Driver 18 EULA + VC++ license (was MIT-primary root LICENSE); remove the resolved TODO.

- Drop the no-op azure-identity >=1.12.0 floor (microsoft channel ships CalVer).

- Re-assert the wheel platform floor via __glibc/__osx virtual-package run constraints.

- Add conda/driver_load_probe.py + run it in the gate and local harness so we prove the native ODBC driver loads, not just the Python shim.

- Publish stage: require signed wheels (no PyPI fallback), publish companion-first with --skip-existing and a #706 pair guard, and document the required resources.pipelines declaration.
Comment thread conda/driver_load_probe.py Fixed
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

100%


🎯 Overall Coverage

83%


📈 Total Lines Covered: 7959 out of 9522
📁 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: 57.9%
mssql_python.pybind.ddbc_bindings.h: 61.5%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.ddbc_bindings.cpp: 77.5%
mssql_python.row.py: 77.6%
mssql_python.pybind.connection.connection_pool.cpp: 81.6%
mssql_python.pybind.connection.connection.cpp: 84.4%
mssql_python.logging.py: 85.5%
mssql_python.helpers.py: 89.3%
mssql_python.pooling.py: 90.1%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

@jahnvi480
Jahnvi Thakkar (jahnvi480) marked this pull request as draft August 18, 2026 08:57
Port the productionized conda pipeline from the ADO conda-publish-pipeline branch onto GitHub conda-onboarding, replacing the earlier draft prototype:

- conda recipes: meta.yaml + build.sh + bld.bat for mssql-python and mssql-python-odbc, vendored ODBC/VC++ EULA text, and .gitattributes (LF for shell scripts).

- OneBranchPipelines conda glue: scripts/build-conda-packages.{sh,ps1}, steps/conda-build-validate-step{,-posix}.yml, steps/conda-publish-step.yml, steps/conda-release-step.yml, jobs/consolidate-conda-artifacts-job.yml.

- Wire conda legs into build/release pipelines (buildConda params on win-64/osx/linux legs + ConsolidateConda stage), preserving GitHub-only signWindowsBinaries content.

- Remove superseded draft prototype: conda/onebranch-publish-conda-stage.yml and conda/build_and_test_local.ps1.
@github-actions github-actions Bot added pr-size: large Substantial code update and removed pr-size: medium Moderate update size labels Aug 18, 2026

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

Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.

Suppressed comments (4)

OneBranchPipelines/scripts/build-conda-packages.sh:173

  • The verify env name only includes the Python version. On macOS this script is invoked twice on the same agent (native osx-arm64, then cross-target osx-64), so the second conda create -n verify_... can fail with an existing prefix or reuse the wrong-arch env. Include the effective subdir (native vs $CONDA_SUBDIR) in the env name to avoid collisions.
for py in $pyvers; do
  envName="verify_${py//./}"
  echo "=== [py $py] create verify env from local channel ==="

OneBranchPipelines/stages/build-linux-single-stage.yml:116

  • This apt install runs before the later apt-get update in the job, so it can fail on agents with stale apt indices (common on hosted Ubuntu). Add an apt-get update (and ideally noninteractive) before installing qemu-user-static/libc6-arm64-cross.
              # qemu-user-static: run aarch64 ELF binaries on the x86_64 host.
              # libc6-arm64-cross: the aarch64 glibc runtime (loader + libc/libm/...)
              # under /usr/aarch64-linux-gnu so qemu can resolve /lib/ld-linux-aarch64.so.1
              # for the emulated aarch64 conda build/verify (QEMU_LD_PREFIX points here).
              sudo apt-get install -y qemu-user-static libc6-arm64-cross

OneBranchPipelines/steps/conda-publish-step.yml:91

  • Installing anaconda-client from PyPI without pinning a version can make releases non-reproducible and may unexpectedly break publishing if a new release introduces behavioral changes. Consider pinning to a vetted version (or using a constraints file) so publish behavior is stable.
        $ErrorActionPreference = 'Stop'
        python -m pip install --upgrade pip
        python -m pip install anaconda-client
        # anaconda-client installs the `anaconda` console script onto PATH.
        anaconda --version

OneBranchPipelines/steps/conda-build-validate-step.yml:126

  • The error message references $links (the wheel find-links directory), but conda packages are searched under ${{ parameters.outputDir }}/bld. If this trips, the message will mislead troubleshooting.
      if (-not $built) { Write-Error "No conda packages were produced under $($links)"; exit 1 }

Comment thread conda/mssql-python/meta.yaml Outdated
Comment thread OneBranchPipelines/scripts/build-conda-packages.sh Outdated
Comment thread OneBranchPipelines/scripts/build-conda-packages.ps1 Outdated
…uildAll on Windows

The Windows mssql-python-odbc companion conda is now built ONCE as a Python-agnostic package in the ODBC_BuildAll stage (no python in host, wheel extracted via tar in bld.bat), mirroring the single py3-none-win_* PyPI wheel, instead of once per Python on every binding leg. The per-Python mssql-python binding legs seed that prebuilt companion into their local channel (-Package binding -DriverCondaDir) so the version-locked dependency still resolves. ConsolidateConda now also pulls the ODBC_BuildAll companion, and the #706 release/publish gates use presence-pairing (keeping strict 1:1 only for per-Python companions, c>1). macOS/Linux stay per-Python (unchanged).
P0-1: invert conda/driver_load_probe.py from a fail-OPEN denylist to a
fail-CLOSED allowlist. A repackaged native ODBC driver that fails to load now
FAILS the DB-less pre-publish gate instead of passing on any unrecognized
exception. Only a clean connect or a connection-stage diagnostic the loaded
msodbcsql driver alone can emit (ODBC branding, network provider, TLS, auth)
counts as PASS. Defer 'import mssql_python' into main() so the classifier is
unit-testable without the compiled extension. Adds tests/test_026 (22 no-DB
tests).

P0-3: gate the conda release on package METADATA, not folder names/counts.
New conda/validate_conda_release.py reads each package's authoritative
info/index.json (zstd) and validates real subdir == folder, allowed subdirs,
the full (subdir x Python) binding matrix, exact/consistent versions, and #706
binding<->companion pairing. Catches a mislabeled subdir and the 8e7f217
dropped-win-64-variant regression the count gate missed. Rewires
OneBranchPipelines/steps/conda-release-step.yml to call it. Adds tests/test_027
(11 tests incl. a real .conda round-trip).
Comment thread conda/driver_load_probe.py Fixed
…P0-2)

macos-latest is an Intel Mac and the arm64 Python cannot execute there (no
reverse Rosetta), so the previous osx-arm64 leg silently built NATIVE osx-64
packages and staged them under osx-arm64 (mislabeled). Cross-build for real:

- conda/*/build.sh: when the host-env Python is not executable (non-emulated
  cross-build), extract the universal2 wheel into \ with unzip instead of
  pip -- mirrors the Windows bld.bat tar path; the arm64 slice comes from the
  universal2 wheel. Native + QEMU-emulated legs keep the pip install.
- conda/*/meta.yaml: skip_compile_pyc on macOS so conda-build does not run the
  non-runnable arm64 Python for .pyc byte-compilation (Python regenerates it).
- build-conda-packages.sh: section-7 verify auto-skips the runtime import when
  the target Python can't run on the host (osx-arm64 on Intel); the static
  arm64-slice audit is the stand-in. Native/QEMU legs still import for real.
- build-macos-single-stage.yml: add condaTargetSubdir: osx-arm64 +
  continueOnError to the arm64 leg; mark osx-64 as the native/blocking leg; add
  a BLOCKING static arm64 slice audit (lipo/otool/file) asserting the shipped
  ddbc_bindings ext and macos/arm64 dylibs really contain arm64 Mach-O. Fix the
  misleading 'Apple Silicon, native' comments.
The vendored ODBC Driver 18 links crypto/auth libs that must receive conda's
security updates instead of being frozen into the payload -- mirroring
conda-forge libpq (declares openssl + krb5, vendors neither):

- openssl # [not win]: the driver dlopen's libssl/libcrypto for TLS; it is not
  an ELF NEEDED entry so overlinking can't see it. Windows uses SChannel.
- krb5 # [linux]: libmsodbcsql NEEDs libkrb5.so.3 + libgssapi_krb5.so.2 (not
  bundled). macOS uses Kerberos.framework, Windows uses SSPI.
- vc14_runtime # [win]: msodbcsql18.dll imports VCRUNTIME140.dll but the
  vendored vcredist ships only msvcp140.dll; declare the serviced conda runtime.

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

Copilot reviewed 28 out of 28 changed files in this pull request and generated no new comments.

Suppressed comments (2)

OneBranchPipelines/scripts/build-conda-packages.sh:184

  • The verify env name is only based on the Python version (e.g. "verify_310"). On macOS this script is invoked twice on the same agent (osx-64 and osx-arm64) sharing the same Miniforge install/outputDir, so the second invocation will fail when it tries to create an env that already exists. Include the target subdir in the env name (and/or delete any existing env before creating it) so the two runs don’t collide.
for py in $pyvers; do
  envName="verify_${py//./}"
  echo "=== [py $py] create verify env from local channel ==="
  # -c microsoft (ahead of conda-forge) so azure-core/azure-identity/msal resolve from the
  # lean `microsoft` channel, NOT conda-forge whose azure-core recipe over-declares flask/six
  # -> celery/boto3/botocore (~9 MB); see conda-forge/azure-core-feedstock#71.
  # --strict-channel-priority keeps the freshly built local companion + binding authoritative.
  "$conda" create -y -n "$envName" -c "$bld" -c microsoft -c conda-forge --strict-channel-priority --override-channels "python=$py" mssql-python

conda/validate_conda_release.py:67

  • read_index_json() uses next(...) to locate the info-*.tar.zst member inside a .conda. If the archive is malformed (missing that member), this will raise StopIteration and produce a stack trace rather than a clear validation failure message. Handle the empty case and raise a ValueError with a helpful message instead.
        with zipfile.ZipFile(path) as zf:
            info_name = next(
                n for n in zf.namelist() if n.startswith("info-") and n.endswith(".tar.zst")
            )
            info_blob = zf.read(info_name)

libodbcinst.so.2 has NEEDED libltdl.so.7 but no RUNPATH, so a minimal glibc

Linux base throws 'OSError: libltdl.so.7: cannot open shared object file' on

import. macOS already vendors libltdl.7.dylib; Linux was the inconsistent

outlier. Two parts:

1) eng/scripts/patch-linux-odbc-libs.sh (new): maintainer/CI tool, run in a

   manylinux_2_28 container, that sources the glibc libltdl.so.7, copies it

   next to libodbcinst.so.2, and patchelf --set-rpath '\' so the driver

   resolves it from its own dir. Skips Alpine/musl by design.

2) build-odbc-all-stage.yml: the wheel content verifier now REQUIRES

   libltdl.so.7 in both manylinux_2_28 payloads (fail-closed). This gate stays

   red until a maintainer runs the patch script and commits the produced

   libltdl.so.7 + rpath-patched libodbcinst.so.2 under

   mssql_python_odbc/libs/linux/debian_ubuntu/<arch>/lib/ (cannot be produced

   on the Windows-only odbc build host).
Decision 2(b): Alpine is a supported, PR-tested platform, so its wheels must be

fixed too (not dropped, not documented-as-limitation). Extends the libltdl

self-contained-payload work to musl:

- eng/scripts/patch-linux-odbc-libs.sh now auto-detects libc + arch and patches

  the matching distro subtrees: a glibc-built libltdl (manylinux, dnf) for

  debian_ubuntu/rhel/suse, a musl-built libltdl (Alpine, apk add libtool) for

  alpine. One build per (libc, arch) serves all that arch's distro subtrees. It

  also drops a per-dir LIBLTDL_LGPL_LICENSE.txt notice.

- mssql_python_odbc/libs/LICENSING: document libltdl (GNU Libtool, LGPL-2.1-or-

  later, dynamically linked) covering the existing macOS libltdl.7.dylib and the

  new Linux libltdl.so.7 (compliance; precedent = macOS already vendors it).

- build-odbc-all-stage.yml: musllinux_1_2_{x86_64,aarch64} wheels now also REQUIRE

  libltdl.so.7 (fail-closed), since the Alpine test leg's system libltdl masks

  the OSError today.

- eng/scripts/audit_bundled_binaries.py (new): allowlist-driven ELF/Mach-O/PE

  dependency audit (gate step 3). Every dep must be BUNDLED, BASE (OS/libc), or

  DECLARED (openssl/krb5 [linux], vc14_runtime [win]); anything else fails. ELF

  binaries that need a bundled sibling must carry an \ RUNPATH; macOS

  absolute non-system install names (e.g. /opt/homebrew) fail regardless of

  basename; --require-arch asserts Mach-O slices (the Intel-agent substitute for

  the arm64 runtime import). Validated locally: Windows PASS, Linux FAIL on the

  missing libltdl.so.7, macOS FAIL on libodbc.2.dylib's /opt/homebrew libltdl.
…n/main

Bulk copy IS now available on Windows ARM64 (the mssql_py_core arm64 core was built + shipped and validated in production pipelines), so the reword naming win-arm64 as an 'not shipped on every platform' example is factually stale. cursor.py is also out of scope for this conda-pipeline PR. Reverted it to origin/main exactly -- which KEEPS the #742 (GH-740) money-range Decimal->SQL_NUMERIC fix (decimal_as_numeric, 9 refs present) and removes the stale message. git diff origin/main HEAD -- mssql_python/cursor.py is now EMPTY, so #720 no longer touches cursor.py (no merge-revert risk, no scope creep). Any improved bulkcopy-unavailable message belongs in a separate GH-619 product PR with accurate current platform coverage.
…bel (word boundary)

The promote fallback used an UNANCHORED -match on the public label. The staging label is <label>_staging_<buildid>, which CONTAINS the public label, so �naconda show output for a package that only reached STAGING (a failed move) matched and was reported 'already promoted' -- success reported while the package sits on staging only. Anchored the check with word boundaries (\\b<label>\\b); '_' is a word char so it matches ONLY the standalone public label, never <label>_staging_<id>. Verified with a local -match test: staging-only strings -> False, a real promote -> True.
…wired conda_resolve_check (#4)

#5 (conda-publish-step.yml): condaChannel/condaLabel were compile-time template-interpolated into the token-bearing inline PowerShell, so a value containing a quote would break the script text before any guard runs (a runtime guard cannot help a compile-time substitution). Surfaced them via the step env block and read them at RUNTIME as env:CONDA_CHANNEL / env:CONDA_LABEL throughout the script body (9 uses); the displayName keeps template interpolation (not script text). Injection-proof (operator-only inputs); YAML + PS AST parse clean. #4: removed eng/scripts/conda_resolve_check.py + tests/test_032_conda_resolve_check.py + their conda-audit.yml references. The release-time channel-drift re-solve gate was built + unit-tested but WIRED INTO NOTHING (no pipeline invokes it; ValidateConda has no conda/mamba), so its 'blocks the publish' docstring was aspirational -- it gated nothing. Removing it drops zero runtime protection and clears the dead-code comment cluster; a tracked issue will reintroduce it wired + validated together. Remaining conda-audit tests: 65 pass.
…t odbc-wheel match

PR #744 review #4: assert_macho_arch.py reads the Mach-O cputype slice(s) out of every vendored .dylib/.so and asserts the osx-64/osx-arm64 arch is present. osx-arm64 is cross-built on Intel where its runtime import is skipped, so this static pass is its only arch+presence check. Wired into audit_packages() next to the win-arm64 PE assert; test_035 mirrors test_030.

PR #744 review #5: iter_payload_members now raises ValueError on an unrecognized extension (fail-closed like read_index) instead of silently yielding an empty iterator; test_029 covers it.

PR #744 review #2: gather_wheels demands exactly one odbc-wheel-filter match instead of silently picking the first, so an arch-ambiguous filter cannot vendor the wrong-arch driver.
Correct the macOS checker for the universal binding plus architecture-separated driver trees and reject malformed fat binaries. Require the supported Linux distro inventory, block failed win-arm64 environment installation, and run the Mach-O regression suite in the PR audit workflow.
Require Microsoft ODBC driver files at the exact runtime-resolvable macOS and Windows paths, and force the DB-less driver probe to select msodbcsql18 before importing mssql_python.

Reject structurally incomplete thin Mach-O and PE binaries, cover exact-one ODBC wheel selection, and align pipeline documentation with the actual cross-build topology.
Replace the stale PowerShell-loop reference with the shared Python orchestrator and clarify that consolidation warns only for an empty set while partial sets are reported by count and gated at release.
Restrict emulated best-effort probe behavior to linux-aarch64 on non-Arm hosts, declare and diagnose the zstandard test dependency, and correct per-leg Miniforge/cross-build documentation.

Add regressions proving Windows and macOS cross probes remain blocking when executable, QEMU classification is Linux-only, and .conda audit tests cannot silently skip for a missing backend.
Copilot AI review requested due to automatic review settings September 7, 2026 15:11

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.

🟡 Changes recommended

A few pipeline/script robustness issues (notably YAML parameter quoting and zstd decompression exception handling) should be addressed to avoid fragile release behavior and confusing failures.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 28/29 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment on lines +125 to +126
CONDA_CHANNEL: ${{ parameters.condaChannel }}
CONDA_LABEL: ${{ parameters.condaLabel }}
@@ -0,0 +1,99 @@
{% set version = environ.get('MSSQL_PYTHON_VERSION', '1.14.0') %}
Comment on lines +44 to +52
try: # Python 3.14+
from compression import zstd # type: ignore

return zstd.decompress(raw)
except Exception: # pragma: no cover - exercised via the third-party path
pass
import zstandard # third-party fallback

return zstandard.ZstdDecompressor().decompress(raw)
Wrap any fallback zstandard ImportError with actionable guidance while preserving the original cause. Add regressions for absent, unloadable, and corrupt zstd backends so decompression errors remain distinct.
Copilot AI review requested due to automatic review settings September 7, 2026 15:23

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.

🟡 Changes recommended

The publish pipeline hardening is inconsistent (compile-time interpolation remains for required/allowed subdir lists) and the driver-load probe contains avoidable insecure flags/suppression that should be corrected before shipping.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

conda/driver_load_probe.py:106

  • TrustServerCertificate=yes is unnecessary when Encrypt=no, and keeping it (plus the DevSkim suppression) can be misleading in a repo script. Since this is a DB-less probe against an unreachable local port, the connection string can omit TrustServerCertificate entirely and avoid needing the suppression.
  • Files reviewed: 28/29 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +150 to +151
$required = '${{ parameters.requiredSubdirs }}'.Split(',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
$allowed = '${{ parameters.allowedSubdirs }}'.Split(',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
Run consolidation after failed producer stages while honoring cancellation, and make Windows, macOS, and Linux producer stages independent so one platform failure cannot skip the others.

Add a regression for the full producer fan-out and consolidation fan-in graph, including stage and reusable job conditions.
Copilot AI review requested due to automatic review settings September 7, 2026 16:03

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.

🔵 Needs a closer look

It introduces extensive new release/publish pipeline logic and binary-audit tooling where small mismatches (e.g., parameter semantics and artifact handling) can have high release impact and should be validated by a human owner.

Review details

Suppressed comments (7)

Previously missed (6) — in code that hasn't changed since the last review.

OneBranchPipelines/steps/conda-release-step.yml:168

  • This template passes --mssql-python-odbc-version through to conda/validate_conda_release.py, but that script explicitly ignores the flag (self-contained model). Keeping a no-op parameter here can give a false sense that the ODBC version is being enforced.

Either drop the parameter from this step, or change the validator to warn/fail if the flag is supplied so callers can’t accidentally rely on it.
conda/validate_conda_release.py:295

  • --mssql-python-odbc-version is accepted but ignored. If a caller supplies it (and conda-release-step.yml currently will when odbcVersion is set), the pipeline can appear to be version-pinned when it isn’t.

At minimum, emit an explicit warning to stderr when this flag is provided so the logs clearly show it has no effect (or fail closed if you want to forbid it entirely).
eng/scripts/assert_macho_arch.py:159

  • members = list(_iter_payload_members(path)) forces the entire conda payload (including file bytes) into memory before the loop. Since artifacts can be large and this audit only needs to stream Mach-O members, this can unnecessarily increase peak memory usage.

Consider making _conda_pkg.iter_payload_members() validate/raise before iteration (or adding a dedicated streaming wrapper) so callers can iterate without list(...) while preserving the current malformed-archive handling.
eng/scripts/assert_pe_machine.py:102

  • members = list(_iter_payload_members(path)) loads the entire conda payload into memory up front. These packages can be large, and the PE audit only needs to stream over members; pre-materializing all bytes increases peak memory significantly.

Consider adjusting _conda_pkg.iter_payload_members() (or adding a wrapper) so malformed-archive errors are raised before iteration, allowing callers to iterate directly without building a list while keeping the current fail-closed behavior.
eng/scripts/audit_bundled_binaries.py:335

  • members = list(_iter_payload_members(path)) fully materializes every payload file (and its bytes) before processing. For real conda artifacts this can be very large and may cause high memory usage/OOM in CI; the audit only needs to stream members and can short-circuit when possible.

Consider refactoring _conda_pkg.iter_payload_members() (or adding a new helper) so validation errors are raised before iteration and callers can safely iterate without building a list.
mssql_python/cursor.py:1850

  • This change alters legacy execute()-path type inference (setinputsizes shorter than params) so money-range Decimals bind as SQL_NUMERIC instead of the MONEY/SMALLMONEY VARCHAR shortcut (GH-740). There isn’t a test exercising the setinputsizes-shorter-than-params + money-range Decimal case, so this regression guard is currently untested.

Add a pytest case that sets setinputsizes() for only the first parameter, passes a money-range Decimal as an uncovered later parameter, and asserts the operation does not overflow and returns the expected semantics against a NUMERIC/DECIMAL column.

conda/validate_conda_release.py:46

  • _zstd_decompress() swallows all exceptions from the stdlib backend and then unconditionally imports zstandard. If neither backend is available, users get a raw ModuleNotFoundError without guidance.

Prefer catching ImportError specifically and raising a clear RuntimeError instructing to use Python 3.14+ (compression.zstd) or pip install zstandard (similar to eng/scripts/_conda_pkg.py).

def _zstd_decompress(raw: bytes) -> bytes:
    """Decompress a zstandard blob, preferring the 3.14+ stdlib backend."""
    try:  # Python 3.14+
        from compression import zstd  # type: ignore

  • Files reviewed: 28/29 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

# Conflicts:
#	conda/mssql-python/build.sh
#	mssql_python/cursor.py
Force the zstandard fallback regression on Python 3.14, reject missing or malformed Linux subdir metadata, preserve staged macOS artifacts after a later failure, and parameterize the new tooling's collection types.

Add regressions for the metadata and publication contracts. Validated with the full tooling suite and a real Linux Conda build, clean-prefix install, Driver 18 load, and prefix reachability check.
Copilot AI review requested due to automatic review settings September 8, 2026 07:26

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.

🟡 Changes recommended

There are at least two correctness/reproducibility issues to address (un-pinned conda-build channels and Win-ARM64 conda install documentation mismatch) before this can be safely approved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 24/25 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +319 to +336
cmd = [
conda,
"run",
"-n",
builder,
"conda-build",
recipe,
"--python",
py,
"--no-test",
"--no-anaconda-upload",
"--output-folder",
bld,
]
if target_subdir == "win-arm64":
# Add Anaconda defaults ahead of conda-forge for the win-arm64 host-env solve.
cmd += ["-c", "defaults", "-c", "conda-forge"]
run(cmd, env=env, what=f"conda-build mssql-python (py {py})")
Comment thread README.md
Comment on lines +63 to +66
**Conda:** mssql-python is also published as a self-contained conda package — the ODBC Driver 18 payload and its native dependencies (`krb5`, `openssl`, `libltdl`) are resolved by conda, so none of the system `apt`/`dnf`/`apk`/`zypper` steps above are required. Both channels are needed: `mssql-python` comes from the `microsoft` channel and its dependencies resolve from `conda-forge`.
```bash
conda install -c microsoft -c conda-forge mssql-python
```
Normalize the Windows OneBranch demand indentation and fail immediately with captured output when prefix discovery, driver location, or dependency listing commands fail.

Add regressions for every helper failure, empty prefix output, macOS partial publication, and the exact Windows pool demand structure.
Copilot AI review requested due to automatic review settings September 8, 2026 07:43

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.

🟡 Changes recommended

It contains a YAML indentation error that makes OneBranchPipelines/conda-release-pipeline.yml invalid and a broad exception catch in _zstd_decompress() that can mask real decompression failures.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

README.md:63

  • This README blurb says conda resolves the ODBC driver native dependencies (krb5, openssl, libltdl) so no system packages are required. However, the conda recipe explicitly declares openssl/krb5/libtool only for Linux and notes that macOS encrypted connections still require system OpenSSL. The README should scope this claim to Linux and mention the macOS OpenSSL requirement to avoid misleading conda users.

conda/validate_conda_release.py:46

  • _zstd_decompress() currently catches a broad Exception, which will also swallow real decompression errors (e.g., corrupt .tar.zst) and then fall back to zstandard, obscuring the true failure. It also raises a bare ModuleNotFoundError if neither backend is available. Narrow the exception to ImportError and raise a clear RuntimeError when no backend is present (similar to eng/scripts/_conda_pkg.py).
def _zstd_decompress(raw: bytes) -> bytes:
    """Decompress a zstandard blob, preferring the 3.14+ stdlib backend."""
    try:  # Python 3.14+
        from compression import zstd  # type: ignore

  • Files reviewed: 24/25 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +129 to +131
name: Python-1ES-pool
demands:
- imageOverride -equals PYTHON-1ES-MMS2022
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.

3 participants