Skip to content

FEAT: add standalone conda build pipeline + build-validation tooling - #744

Open
Jahnvi Thakkar (jahnvi480) wants to merge 16 commits into
mainfrom
jahnvi/conda-build-pipeline
Open

FEAT: add standalone conda build pipeline + build-validation tooling#744
Jahnvi Thakkar (jahnvi480) wants to merge 16 commits into
mainfrom
jahnvi/conda-build-pipeline

Conversation

@jahnvi480

@jahnvi480 Jahnvi Thakkar (jahnvi480) commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

AB#47315

Summary

This pull request introduces a new standalone OneBranch pipeline for building, validating, and consolidating self-contained mssql-python conda packages across all supported platforms and Python versions. The pipeline is carefully structured to ensure per-platform correctness, best-effort artifact gathering, and robust isolation between build legs. It also includes a template job for consolidating conda artifacts and a .gitattributes update for shell scripts.

New Standalone Conda Build Pipeline
Major features and structure:

  • Adds conda-build-pipeline.yml: Implements a full multi-stage pipeline that builds and validates self-contained mssql-python conda packages for Windows, macOS, and Linux, including cross-platform builds (e.g., win-arm64, osx-arm64, linux-aarch64) using platform-appropriate agents and emulation where needed. Each leg is carefully isolated to prevent failures in one subdir from impacting others, and best-effort logic is used for cross-arch builds.

  • Artifact Consolidation:

    • Adds consolidate-conda-artifacts-job.yml: Provides a job template that collects all per-platform conda packages into a single artifact, preserving the subdir structure. The consolidation is best-effort in the build pipeline, with hard gating deferred to the release pipeline to avoid blocking primary wheel deliverables.

Build and Release Process Improvements

  • Best-Effort and Gating Strategy: The pipeline is designed so that conda packaging failures do not block wheel releases. Completeness and artifact presence are strictly enforced only at release time, not during the build. [1] [2]

Cross-Platform and Cross-Arch Handling

  • Cross-Build Logic: Implements cross-building for win-arm64, osx-arm64, and linux-aarch64 using CONDA_SUBDIR and QEMU emulation as appropriate, with architecture-specific validation and clear blast-radius isolation to prevent a single platform╬ô├ç├ûs failure from impacting others.

Repository Hygiene

  • Shell Script Line Endings:
    • Adds OneBranchPipelines/scripts/.gitattributes enforcing LF line endings for .sh files under that folder (the conda recipe scripts are covered separately by conda/.gitattributes).

Build-Validation Tooling + Tests
The "build-validation tooling" named in the title -- the exact set of files in this slice:

  • OneBranchPipelines/scripts/build_conda_packages.py: the single cross-platform orchestrator (gather wheels -> Miniforge/conda-build -> build the self-contained package -> masking-immune audit -> solve a fresh env + import + driver-load + optional reachability gate -> stage). Replaces the former per-OS PowerShell/bash build scripts.
  • OneBranchPipelines/steps/conda-build-validate-step.yml + -posix.yml: the Windows / POSIX step templates that invoke the orchestrator.
  • eng/scripts/audit_bundled_binaries.py, assert_pe_machine.py, _conda_pkg.py: the masking-immune RUNPATH / ELF-arch / PE-machine static audit plus the shared conda-package reader.
  • conda/driver_load_probe.py: the DB-less driver-load proof.
  • tests/test_029_bundled_binary_audit.py, test_030_pe_machine_assert.py, test_033_driver_load_probe.py, test_034_conda_verify_cwd.py: unit coverage for the audit, the PE-machine assert, the driver-load probe, and the orchestrator's verify() neutral-cwd invariant.

Not in this slice (deliberately): the live Encrypt=yes TLS gate (conda/tls_connect_probe.py and its test_028/test_031) is a release-pipeline concern -- the build pipeline never enables it (no leg sets CONDA_TLS_PROBE_CONN), so it ships with the release slice, not here. The masking-immune static RUNPATH audit already guards the OpenSSL layout at build time.

Copilot AI lite review requested due to automatic review settings September 3, 2026 03:47
@github-actions github-actions Bot added the pr-size: large Substantial code update label Sep 3, 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.

🟡 Changes recommended

The Conda publish/release step templates contain a required-subdirs default mismatch and misleading guard error messages that can enable incorrect publishing behavior or slow diagnosis.

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

Pull request overview

Adds standalone Conda build + release pipeline infrastructure for mssql-python, plus the validation/audit tooling (and unit tests) used to gate Conda artifacts independently of the wheel/PyPI release flow.

Changes:

  • Introduces OneBranch standalone Conda build and Conda release/publish pipelines with shared step/job templates for per-platform build, consolidation, release gating, and publishing.
  • Adds build-time and release-time validation tooling: Linux RUNPATH self-containment audit, Windows PE machine-type audit, release metadata gate, and release-time dependency re-solve gate.
  • Adds a suite of pure unit tests (no DB / no native extension required) covering the new probes and validation scripts.
File summaries
File Description
OneBranchPipelines/conda-build-pipeline.yml Standalone Conda build pipeline consuming wheel artifacts and producing consolidated Conda artifacts.
OneBranchPipelines/conda-release-pipeline.yml Standalone Conda release pipeline that gates completeness/consistency and optionally publishes to Anaconda.org.
OneBranchPipelines/jobs/consolidate-conda-artifacts-job.yml Consolidates per-leg staged Conda packages into a single conda/ artifact tree.
OneBranchPipelines/steps/conda-build-validate-step.yml Windows template to build/validate Conda packages from wheels and stage artifacts.
OneBranchPipelines/steps/conda-build-validate-step-posix.yml POSIX template to build/validate Conda packages from wheels and stage artifacts.
OneBranchPipelines/steps/conda-release-step.yml Release-readiness gate: re-audit + metadata validate consolidated Conda artifacts.
OneBranchPipelines/steps/conda-publish-step.yml Publishes consolidated Conda packages to Anaconda.org using staged-then-promoted flow.
OneBranchPipelines/scripts/build-conda-packages.ps1 Windows implementation of Conda build+validate from wheel inputs (incl. audits and probes).
OneBranchPipelines/scripts/build-conda-packages.sh POSIX implementation of Conda build+validate from wheel inputs (incl. audits and probes).
OneBranchPipelines/scripts/.gitattributes Enforces LF for .sh scripts.
conda/driver_load_probe.py DB-less driver load probe used as a Conda validation gate.
conda/tls_connect_probe.py Live Encrypt=yes TLS probe to prove OpenSSL backend reachability (fail-closed classification).
conda/validate_conda_release.py Metadata-based release readiness gate for Conda artifact completeness and consistency.
eng/scripts/assert_pe_machine.py Audits PE machine type of vendored Windows binaries inside Conda packages.
eng/scripts/audit_bundled_binaries.py Static, masking-immune audit of Linux vendored binaries’ RUNPATH/NEEDED/deps/vendoring rules.
eng/scripts/conda_resolve_check.py Release-time dependency re-solve (dry-run) to catch channel drift before publish.
tests/test_026_driver_load_probe.py Unit tests for conda/driver_load_probe.py classification and main() behavior via stubs.
tests/test_027_conda_release_metadata.py Unit tests for conda/validate_conda_release.py matrix/subdir/version enforcement.
tests/test_028_tls_connect_probe.py Unit tests for TLS probe classification, tokenization, redaction, and skip/guard behavior.
tests/test_029_bundled_binary_audit.py Unit tests for Linux RUNPATH audit logic using synthetic ELF + conda package fixtures.
tests/test_030_pe_machine_assert.py Unit tests for PE machine-type parser + .conda round-trip audit.
tests/test_031_tls_probe_required.py Unit tests for TLS probe required-mode semantics (fail closed on misconfig).
tests/test_032_conda_resolve_check.py Unit tests for conda re-solve helper (channel selection, cmd shape, target enumeration).
Review details
  • Files reviewed: 23/23 changed files
  • Comments generated: 3
  • Review effort level: Lite

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

Comment thread OneBranchPipelines/steps/conda-publish-step.yml Outdated
Comment thread OneBranchPipelines/steps/conda-publish-step.yml Outdated
Comment thread OneBranchPipelines/steps/conda-release-step.yml Outdated
@jahnvi480 Jahnvi Thakkar (jahnvi480) changed the title FEAT: add standalone conda build & release pipelines + validation tooling FEAT: add standalone conda build pipeline + build-validation tooling Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

100%


🎯 Overall Coverage

82%


📈 Total Lines Covered: 7776 out of 9444
📁 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: 58.9%
mssql_python.pybind.ddbc_bindings.h: 61.5%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.ddbc_bindings.cpp: 75.6%
mssql_python.__init__.py: 77.6%
mssql_python.row.py: 77.6%
mssql_python.pybind.connection.connection_pool.cpp: 82.8%
mssql_python.pybind.connection.connection.cpp: 84.4%
mssql_python.logging.py: 85.5%
mssql_python.connection.py: 85.9%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

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

There are a few correctness/robustness issues in the new conda validation tooling (notably ODBC braced-string splitting edge cases and script rerunnability) plus misleading failure messages that should be fixed before relying on these gates.

Review details

Suppressed comments (5)

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

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

  • conda create -n conda_builder ... will fail on reruns because the env name is constant and the script never removes/reuses an existing env. This makes the script non-idempotent for local dev or agent/workdir reuse (and set -e will abort immediately).
    conda/tls_connect_probe.py:135
  • _split_top_level treats every } as ending a braced value, but MS-ODBCSTR allows escaping a literal closing brace inside {...} as }}. With the current logic, a value containing }} can drop brace depth early and cause semicolons inside the braced value to be split at top level, corrupting the connection string rewrite.
    conda/driver_load_probe.py:135
  • The probe is used for the self-contained conda package (ODBC payload vendored into mssql-python), so "missing companion" is misleading in the failure message and makes triage harder.
    conda/tls_connect_probe.py:110
  • describe() prefixes any SSL Provider / certificate verification failure as "OpenSSL backend unreachable", but those errors can happen with OpenSSL present (they indicate a TLS handshake failure, not a missing libssl/libcrypto). This wording makes failures misleading to triage.

This issue also appears on line 279 of the same file.

conda/tls_connect_probe.py:282

  • The failure exit text always says "TLS/OPENSSL BACKEND UNREACHABLE", but failures can also be due to TLS handshake errors (cert, protocol, etc.) where OpenSSL is reachable. Consider a neutral prefix so the exit reason matches what actually failed.
    if tls_completed(outcome):
        print("TLS_OK (OpenSSL backend reachable; " + describe(outcome) + ")")
        return
    sys.exit("TLS/OPENSSL BACKEND UNREACHABLE: " + describe(outcome))
  • Files reviewed: 16/16 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…unt, harden TLS probe

- Rename test_026_driver_load_probe.py -> test_033 (main already has
  test_026_windows_dll_search.py from #735; restores the unique-NNN convention;
  027/032 stay reserved for the release slice).
- consolidate-conda-artifacts-job.yml: fix stale accounting — the pipeline now
  cross-builds win-arm64 (py3.12-3.14), so TOTAL is 28, not 25 (only musllinux is
  excluded now); a release gate keyed to "25 / no win-arm64" would be wrong.
- build-conda-packages.sh: make the conda_builder create idempotent (env remove
  first) so a reused agent/workdir doesn't fail under set -e.
- tls_connect_probe.py: neutral "TLS handshake did not complete" wording (an
  ssl-routines / cert error is not necessarily an unreachable backend); handle
  MS-ODBCSTR }} escaped braces in _split_top_level.
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
… idempotent conda_builder, neutral TLS wording + }} brace escaping
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…y audit (twin of assert_pe_machine.py); +3 tests
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…it (lipo/otool)' claims; osx-arm64 arch is trusted from the universal2 wheel tag (no Mach-O check), real guards are the PE + ELF audits
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…ad targetArch param/condition (win-arm64 is distinguished by condaTargetSubdir); 74 probe tests pass
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…r + two odbc no-op branches from build-conda-packages.ps1 (dead in the self-contained model; the .sh twin never had them); -27 lines, PS AST clean

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.

Summary of review (automated pre-review, pending maintainer judgment)

Reviewed the added conda build pipeline and validation tooling for correctness, secret handling, validation-gate coverage, and repository conventions. The tooling is well-structured and high-coverage (ran the 5 new test files: 97 passed, 4 skipped for the optional zstd .conda path). All specifics are left as inline comments.

Requesting changes on two Medium items (inline): the brace-splitter divergence from the shipped connection-string parser in tls_connect_probe.py (connect-string corruption + password fragment in logs), and the non-secret condaTlsProbeConn pipeline parameter. The remaining inline comments are Low-severity suggestions and two questions.

Recommendation: Request Changes

Comment thread conda/tls_connect_probe.py Outdated
Comment thread OneBranchPipelines/conda-build-pipeline.yml Outdated
Comment thread conda/tls_connect_probe.py Outdated
Comment thread conda/driver_load_probe.py Outdated
Comment thread OneBranchPipelines/scripts/build-conda-packages.sh Outdated
Comment thread eng/scripts/assert_pe_machine.py
Comment thread eng/scripts/audit_bundled_binaries.py
Comment thread OneBranchPipelines/scripts/build-conda-packages.sh Outdated
Comment thread eng/scripts/audit_bundled_binaries.py Outdated
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…bloat cleanup

Correctness / arch:
- audit_bundled_binaries.py: add an ELF e_machine arch gate (linux-64==x86_64,
  linux-aarch64==aarch64) -- the Linux twin of assert_pe_machine.py; +3 tests.
- consolidate-conda-artifacts-job.yml: reconcile the win-arm64 accounting (25->28).
- build-conda-packages.sh + conda-build-pipeline.yml: correct the false osx-arm64
  "static arm64-slice audit (lipo/otool)" claims (no such check exists) to admit the
  arm64 slice is trusted from the universal2 wheel tag; real guards are PE + ELF.

Hygiene:
- rename test_026_driver_load_probe.py -> test_033 (dup with main's test_026).
- build-conda-packages.sh: idempotent conda_builder env create (set -e safe).
- tls_connect_probe.py: MS-ODBCSTR }} brace escaping in _split_top_level.

Bloat removal (ponytail):
- drop the YAGNI _DRIVER_LOAD_FAILURE_MARKERS / _OPENSSL_UNREACHABLE_MARKERS tables
  (describe() decoration only; classifiers use separate positive-marker lists).
- remove the dead targetArch param + always-true ne(...,'arm64') condition + args.
- remove the vestigial -Package/-DriverCondaDir params + odbc no-op branches from
  build-conda-packages.ps1.

Probe/audit unit tests pass; black clean. Validated on a NonOfficial ADO build run.
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…on", }} test, distro-scope doc

- assert_pe_machine.py: assert the native binding (mssql_python/ddbc_bindings*.pyd)
  AND the vendored ODBC driver DLLs are BOTH present, independently (was: >=1 native
  file) -- win-arm64 skips the runtime import, so this is its presence gate. +2 tests.
- driver_load_probe.py: drop "missing companion" from the failure label -- the package
  is self-contained (vendors the ODBC payload), there is no separate companion.
- tests/test_028: add a }} escaped-brace case for _split_top_level (Medium: '}}' is a
  literal '}' in MS-ODBCSTR, so a value with '}}' + internal ';' must not be mis-split).
- audit_bundled_binaries.py: document that the Linux audit validates every DISCOVERED
  distro/arch dir but does not assert a required distro SET (the wheel is the source of
  truth), so a wholesale-missing distro is a wheel-build concern, not caught here.

96 probe/audit unit tests pass; black clean.
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…CRET variable, not a plaintext queue-time parameter (ADO leaves params unmasked); define condaTlsProbeConn in a variable group/Key Vault, unset = probe skips (Sumit, Medium)
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…onn, small fixes

- assert_pe_machine.py: assert the native binding (ddbc_bindings*.pyd) AND the vendored
  ODBC driver DLLs are BOTH present, independently (win-arm64 skips the runtime import,
  so this is its presence gate); +2 tests.
- conda-build-pipeline.yml: source the TLS probe connection string from a SECRET variable
  (variable group / Key Vault), not a plaintext queue-time parameter that ADO leaves
  unmasked in the run UI/logs.
- driver_load_probe.py: drop "missing companion" from the failure label (self-contained
  package -- vendors the ODBC payload; there is no separate companion).
- tests/test_028: add a }} escaped-brace case for _split_top_level (MS-ODBCSTR: '}}' is a
  literal '}', so a value with '}}' + internal ';' must not be mis-split).
- audit_bundled_binaries.py: document that the Linux audit does not assert a required
  distro SET (the repackaged wheel is the source of truth).

96 probe/audit unit tests pass; black clean.
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 3, 2026
…rse, shared conda reader, ldd/TLS hardening

Should-fix (before the release pipeline publishes):
- assert_pe_machine.py: the Windows presence gate now requires the CORE driver
  (msodbcsql18*.dll) specifically, not just any vendored .dll -- a support-DLL-only
  package (e.g. only mssql-auth) with the core driver missing would otherwise pass, and
  on win-arm64 (runtime import skipped) this is the sole check. +1 test.
- tls_connect_probe.py: drop the false-positive-prone 18456+'login' arm (a pre-TLS
  'Login timeout ... 18456' carries both '18456' and 'login' and would false-pass this
  fail-closed gate); keep only the locale-independent SQLSTATE 28000. +2 tests.
- conda-build-pipeline.yml: wire CONDA_TLS_PROBE_REQUIRED via a new enableMandatoryTlsGate
  parameter (default off) so the mandatory-TLS mode is turnable-on at release together
  with the secret conn -- it was previously set nowhere (the fail-closed mode shipped inert).
- eng/scripts/_conda_pkg.py: extract the shared .conda/zstd/tar + info/index.json reader
  used by BOTH audit scripts (was duplicated; pylint R0801) into one sibling module.

Real gaps:
- audit_bundled_binaries.py: parse the openssl range pin properly (operator+version per
  clause) so '>=3,<40' no longer false-passes ('<40' merely CONTAINS '<4'), while conda's
  canonical '<4.0a0' still passes and a bare '>=3' (no upper) correctly fails. +2 tests.
- build-conda-packages.sh: the ldd reachability gate now clears LD_LIBRARY_PATH (so the
  RUNPATH $ORIGIN climb ALONE must reach the prefix -- an ambient LD_LIBRARY_PATH could
  otherwise mask a broken RUNPATH) and requires resolution under $PREFIX/lib, not anywhere
  under $PREFIX.

Ponytail:
- audit_bundled_binaries.py: drop the RUNPATH canonical-ORDER check (the loader searches
  all entries regardless of order; the set-membership check already pins {$ORIGIN, climb}).
- consolidate-conda-artifacts-job.yml: correct the total-count log (25 -> 28).

black + flake8 + bash -n clean; mypy clean on the scripts; 112 probe/audit unit tests pass.
Second slice of the conda onboarding split (#720), after the recipe (#734, merged).
Adds the OneBranch conda-build pipeline that runs conda-build against the recipe already
on main and produces a consolidated conda/ artifact tree, plus the pure, no-DB validation
tooling the pipeline gates on.

Pipeline (OneBranchPipelines/):
- conda-build-pipeline.yml + the per-platform build/validate step templates
  (conda-build-validate-step{,-posix}.yml) and the consolidate-artifacts job (win-arm64
  py3.12-3.14 included in the accounting).
- build-conda-packages.{ps1,sh}: drive conda-build per leg; idempotent builder env
  (set -e safe). The TLS-probe connection string is sourced from a SECRET variable
  (variable group / Key Vault) mapped into the step env, never a plaintext queue-time
  parameter that ADO would leave unmasked in logs.

Validation tooling (conda/, eng/scripts/) + pure no-DB tests (tests/test_028-033):
- tls_connect_probe.py / driver_load_probe.py: import-time TLS-handshake and driver-load
  probes. The connection-string splitter honors MS-ODBCSTR }} brace escaping so a braced
  password is never mis-split at an internal ';'; neutral failure labels for triage.
- assert_pe_machine.py: Windows PE machine assert -- verifies the native binding
  (ddbc_bindings*.pyd) AND the vendored ODBC driver DLLs are both present and match the
  package arch (win-arm64 skips the runtime import, so this is its presence gate).
- audit_bundled_binaries.py: Linux RUNPATH self-containment audit + ELF e_machine arch
  gate (linux-64==x86_64, linux-aarch64==aarch64).

Scope: BUILD pipeline only. The release/publish steps, release-metadata validator, and
re-solve drift gate move to a follow-up PR. The osx-arm64 slice is trusted from the
universal2 wheel tag (no Mach-O audit is claimed); the enforced arch guards are the PE
and ELF checks. The GitHub conda-audit workflow and the product-code changes are
separate slices.

All probe/audit unit tests pass; black clean. Validated on a NonOfficial ADO build run.

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.

one thought on the two build scripts (build-conda-packages.ps1 + .sh).

they're the same pipeline written twice:

  • same 7 steps, same 7 args
  • one in powershell, one in bash
  • ~440 lines of parallel logic, plus the same comments duplicated on both sides

the reason i'd push on it: they've already drifted.

  • step 2 installs conda-build<26 into a dedicated env in the .sh, but into base in the .ps1
  • the .sh comment literally explains that base is the thing that goes unsolvable on a 3.14-pinned runner
  • so the ps1 is doing what the sh warns against. that's the gap
  • two files means every change is a two-file change, and this one already diverged

also a big chunk of the ps1 complexity only exists because the yaml dot-sources it with &:

  • the ErrorActionPreference flip, the 2>$null, the cmd /c "exit 0" reset at the end
  • if the step just ran python build_conda_packages.py ... as a normal process, it reads the exit code and all of that goes away

suggestion: one build_conda_packages.py orchestrator.

  • conda is python and every agent already has a bootstrap interpreter, so it runs everywhere
  • the platform-specific bits (installer invocation, qemu vs ToS env, the linux-only reachability + tls gates) are a handful of if branches, not a second 360-line script
  • both step templates collapse to one python ... call too

on effort: this is small.

  • the scripts are short and well scoped, and the logic already exists
  • it's collapsing two files into one, not writing new pipeline
  • only real work is one ADO run to confirm the legs stay green

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.

Review Summary

  • PR intent: Add a standalone conda build pipeline plus build-validation tooling (PE/ELF architecture asserts, bundled-binary audit, and TLS / driver-load reachability probes) for the mssql-python conda packages.
  • Scope reviewed: The full PR diff at head SHA ae9e4a64 — the OneBranch conda pipeline YAML + step/job templates, the build scripts (build-conda-packages.ps1 / .sh), and the Python validation tooling under conda/ and eng/scripts/.
  • Overall assessment: The build/validation design is careful and largely fail-closed. One Medium pipeline-wiring defect prevents the TLS gate from ever receiving its secret connection string, plus four Low-severity robustness gaps in the audit/probe tooling.
  • Overall risk: Medium
  • Proposed decision: Request Changes
  • Design compliance: Not Applicable (no formal design/ADR governs this tooling)

Findings: 1 Medium, 4 Low — all posted inline.

Recommendation: Request Changes. The Medium (job-level secret never reaches the posix bash: step) should be fixed so the TLS gate actually runs against the intended connection; the four Low items are robustness hardening of the audit/probe tooling.

Comment thread OneBranchPipelines/conda-build-pipeline.yml Outdated
Comment thread conda/tls_connect_probe.py Outdated
Comment thread eng/scripts/audit_bundled_binaries.py Outdated
Comment thread eng/scripts/audit_bundled_binaries.py
Comment thread eng/scripts/_conda_pkg.py Outdated
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 4, 2026
- tls_connect_probe.py: _split_top_level now models a SINGLE-LEVEL braced value matching the
  production parser (connection_string_parser.py::_parse_braced_value) -- an inner `{` is a
  literal (not a nested open) and a `{` is only a brace-open at a value's START, so
  `Pwd={a{b};Encrypt=no` splits correctly and force_tls no longer emits a duplicate
  `encrypt` that mssql_python.connect would reject. (The production parser cannot be imported
  here: it pulls in mssql_python -> the native ddbc_bindings extension, which this standalone
  probe must stay importable / unit-testable without.)
- audit_bundled_binaries.py::_openssl_range_ok: the upper bound is valid only as an EXCLUSIVE
  `<` at numeric release 4.0(.0...) -- `<4`, `<4.0`, `<4.0.0`, `<4.0a0` pass; `<=4`, `<4.1`,
  `<4.0.1` (each admitting some openssl 4.x) now correctly FAIL.
- audit_bundled_binaries.py: an unknown `linux-*` subdir with no _SUBDIR_MACHINE mapping now
  FAILS CLOSED instead of proceeding with expected_machine=None and silently skipping the ELF
  architecture gate.
- _conda_pkg.py::iter_payload_members: a `.conda` missing its pkg-*.tar.zst now RAISES (was a
  bare `return` -> silent empty iteration); both audit scripts convert that to a violation.

The Medium (the job-level TLS secret never reaching the posix `bash:` step) is already
resolved: the live Encrypt=yes gate was removed from the build pipeline entirely in the
previous commit (it moves to the release pipeline, where the secret is always present so the
gate is unconditional).

+7 unit tests (119 pass). black + flake8 clean; mypy clean on the scripts.
…d-pipeline live TLS gate, Sumit round-2 hardening

Applies the accumulated review fixes on top of the main merge:

Pipeline:
- build-conda-packages.ps1: build in a dedicated `conda_builder` env (conda-build<26 +
  zstandard) instead of `-n base`, matching the .sh -- a base pinned to a python no
  conda-build<26 supports (e.g. 3.14) would otherwise be unsolvable.
- conda-build-pipeline.yml: drop the live Encrypt=yes TLS gate (and its enableMandatoryTlsGate
  toggle / CONDA_TLS_PROBE_* wiring) from the BUILD pipeline -- it needs a reachable server + a
  secret conn, so it moves to the release pipeline (unconditional there). Fixes the linux-64
  TLS_PROBE_MISCONFIGURED failure (undefined secret passed through as a literal) and Sumit's
  Medium (a job-level secret never reaches the posix bash step without a step-level env:).

Probe/audit hardening (Sumit round 2):
- tls_connect_probe.py: _split_top_level now matches the production _parse_braced_value grammar
  (single-level braces; `{` only opens at a value's start), so a braced password with an inner
  `{` can no longer yield a duplicate Encrypt.
- audit_bundled_binaries.py: the openssl upper bound is accepted only as an exclusive `<` at
  4.0(.0...); an unknown `linux-*` subdir now fails closed instead of skipping the arch gate.
- _conda_pkg.py: iter_payload_members raises on a `.conda` missing its pkg-*.tar.zst payload.

+ unit tests. black + flake8 + pytest (119) + mypy clean.
…st_031

- steps/conda-build-validate-step{,-posix}.yml: pythonVersion(s) and its sibling
  condaTargetSubdir now flow via a step-level env: block and are format-validated in-script
  (empty / X.Y[,X.Y]* and a known conda subdir) instead of being interpolated raw into the
  bash / PowerShell text -- matches the repo's secret-handling convention (as already done
  for condaTlsProbeConn). Adversarially tested: injection, bad format, and an unknown subdir
  are all rejected.
- test_031: focus on REQUIRED-mode; drop the tls_completed() cases already covered in
  test_028 (no coverage loss; removes a now-stale 18456-arm test name).
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 4, 2026
Same silent-pass class the reviewer filed, on lines the earlier passes didn't reach:

- conda-build-pipeline.yml: drop the unpinned `--privileged multiarch/qemu-user-static`
  public image (SDL); qemu-user-static + binfmt-support already register the aarch64 handler
  on apt install -- verify /proc/sys/fs/binfmt_misc/qemu-aarch64 and fail loudly instead.
- audit_bundled_binaries.py::_openssl_range_ok: replace the prefix-regex parse with an
  ALLOWLIST of canonical bound spellings -- '>=3|>=1' (conda OR), a garbage clause,
  '<4garbage', '<=4', '<4.1' now all FAIL CLOSED.
- audit_bundled_binaries.py: an EMPTY RUNPATH entry (trailing/double ':' = current-directory
  search) now fails -- _entries() dropped it, letting '$ORIGIN:' pass the exact-{$ORIGIN,
  climb} check.
- audit_bundled_binaries.py: the DT_NEEDED check now anchors on the '.so' soname, so
  libkrb5support.so no longer satisfies a required libkrb5.so.
- tls_connect_probe.py::tls_completed: match SQLSTATE 28000 ONLY in SQLSTATE context
  (bracketed/quoted or after the label), not as a bare substring (a ':28000' port or a
  'sql28000' host would false-pass -- same class as the dropped bare-'18456' arm).
- tls_connect_probe.py::_redact: mask a no-'=' segment to '<<NO-VALUE>>' instead of echoing
  the raw token (a mis-split braced password could land there).
- build-conda-packages.ps1: document that the win-arm64 best-effort real-create masking is
  BOUNDED by the blocking build + dry-run solve + PE/static arch gates (no logic change).

+5 adversarial unit tests (120 pass). black + flake8 + bash -n + PS AST clean.
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 4, 2026
…macosx* like the odbc glob, so a stray Linux cpXY wheel can't be picked up. NOTE: recipe file from #734 (on main); deliver via its own PR to main, not #744.
Same silent-pass class as the earlier review, on lines the prior passes didn't reach:

- conda-build-pipeline.yml: drop the unpinned `--privileged multiarch/qemu-user-static`
  public image (SDL); the apt qemu-user-static + binfmt-support already register the aarch64
  handler -- verify /proc/sys/fs/binfmt_misc/qemu-aarch64 and fail loudly instead.
- audit_bundled_binaries.py: _openssl_range_ok is now an ALLOWLIST of canonical bound
  spellings (conda '>=3|>=1' OR-group, garbage clauses, '<4garbage', '<=4', '<4.1' fail
  closed); an EMPTY RUNPATH entry ('$ORIGIN:' current-dir search) now fails; the DT_NEEDED
  check anchors on '.so' so libkrb5support.so no longer satisfies libkrb5.so.
- tls_connect_probe.py: SQLSTATE 28000 matched only in SQLSTATE context (not a bare
  substring that a ':28000' port / 'sql28000' host would false-pass); _redact masks a no-'='
  segment to '<<NO-VALUE>>' instead of echoing the raw token.
- build-conda-packages.ps1: document that the win-arm64 best-effort real-create masking is
  bounded by the blocking build + dry-run solve + PE/static arch gates (no logic change).

+5 adversarial unit tests. black + flake8 + bash -n + PS AST clean.
@jahnvi480
Jahnvi Thakkar (jahnvi480) marked this pull request as draft September 4, 2026 05:32
… + print, not 2>$null) so a real infra failure is diagnosable (devil's-eye) (sync GitHub 241fc08)
…rchestrator

Replace build-conda-packages.ps1 + build-conda-packages.sh with one cross-platform Python orchestrator (OneBranchPipelines/scripts/build_conda_packages.py) and collapse both conda-build-validate step templates to invoke it; eliminates the duplicated PowerShell/bash logic and the cmd /c exit-0 hack. Also folds in three-lens review findings: #1 assert_pe_machine.py requires a vendored mssql-auth DLL on win-arm64 (loader throws at connect if absent); #3 audit_bundled_binaries.py scans the whole payload for vendored crypto .so, not just /libs/linux/; #4 build_conda_packages.py clears bld/ and LINKS and pins mssql-python==version in the verify env; #5 conda-build-pipeline.yml documents that the win-arm64 python set (3.12-3.14) is a deliberately fixed matrix. Tests: add win-arm64 missing-auth-DLL (test_030) and vendored-crypto-outside-libs-linux (test_029) failure cases.
The orchestrator refactor deleted build-conda-packages.sh; update the stale comment that named it so no reference to a deleted script remains in this slice.
The Encrypt=yes TLS gate (_tls_gate + conda/tls_connect_probe.py) is opt-in via CONDA_TLS_PROBE_CONN/REQUIRED, which NO build-pipeline leg sets -- it always skips. It belongs to the release pipeline, so remove it from the build slice: strip _tls_gate from build_conda_packages.py and delete conda/tls_connect_probe.py + tests/test_028_tls_connect_probe.py + tests/test_031_tls_probe_required.py. Recoverable from history for the release-pipeline PR.
…e; aarch64 continueOnError

Finding #1 (blocking): verify() ran python -c import mssql_python from the agent cwd (checkout root with the un-built mssql_python/ + mssql_python_odbc/ source), so sys.path[0]='' shadowed the conda-installed package (ImportError: No ddbc_bindings module found). Fix: a thin verify() wrapper os.chdir's to the neutral per-leg build dir (Python equivalent of the deleted scripts' cd); body renamed to _verify_impl. New tests/test_034_conda_verify_cwd.py asserts the import probes run from the workdir (proven to fail without the wrapper). Finding #2 (medium): linux-aarch64 lacked continueOnError:true while sharing the BuildConda job with native linux-64 -- a QEMU flake failed the job and skipped the condition:succeeded() publish, discarding validated linux-64 packages. Added continueOnError:true (blast-radius isolation; release gate still requires the subdir) and reconciled the MANDATORY-blocking comment.
…x (+recipe_root abspath, failure-path test)

Review consensus should-fixes hardening the finding #1 cwd fix: (1) _import_probe() asserts mssql_python(_odbc).__file__ is under sys.prefix, so a stray PYTHONPATH/.pth that os.chdir cannot catch still FAILS the leg. (2) main() abspaths --recipe-root so verify()'s os.chdir cannot misresolve the driver_load_probe from a relative arg. (3) test_034 adds a failure-path cwd-restoration case; import matcher updated for the new -c body.

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 documentation/scope mismatches (including discrepancies between the PR description and the files actually present) that should be corrected before merging.

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

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

Comment thread OneBranchPipelines/conda-build-pipeline.yml
Comment thread OneBranchPipelines/steps/conda-build-validate-step.yml Outdated
Comment thread OneBranchPipelines/steps/conda-build-validate-step.yml Outdated
…n list, condaSubdir cross-target)

Review low findings on conda-build-validate-step.yml: pythonVersion comment said single X.Y but the param takes a comma-separated list (win-64 follows pythonVersions; win-arm64 uses a fixed 3.12-3.14 set); condaSubdir said 'Must match the platform of THIS agent', wrong for the win-arm64 cross leg (x64 agent via condaTargetSubdir). Reworded both. Comment-only; the posix twin already had accurate wording.
… --flag=value (PS 5.1 empty-arg drop)

The ADO win-64 conda leg failed with 'argument --conda-target-subdir: expected one argument'. Windows PowerShell 5.1 DROPS an empty '' arg to a native command, so on win-64 (empty condaTargetSubdir) the space form vanished and argparse saw the next flag. Reproduced under PS 5.1: space form exits 2, =value parses. Switched both optionally-empty args to --flag=value (one token even when empty). POSIX/bash unaffected. Verified win-64 + win-arm64 parse.

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

There are a few concrete correctness/robustness issues in the new tests/templates and consolidation script (sys.path leakage, quoting/strict bash mode, and .gitattributes scope vs PR description) that should be addressed before approval.

Review details

Suppressed comments (6)

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

OneBranchPipelines/jobs/consolidate-conda-artifacts-job.yml:82

  • This script uses unquoted variable expansions (e.g., $(ob_outputDirectory), $(Pipeline.Workspace)) and only set -e. Quoting and set -euo pipefail makes the consolidation more robust (paths with spaces, unset vars) and prevents subtle failures from being masked.

This issue also appears on line 87 of the same file.
tests/test_029_bundled_binary_audit.py:40

  • _load_module() prepends eng/scripts to sys.path but never removes it, which can leak into other tests and change import resolution order for the rest of the session. Wrap the prepend in a try/finally and pop it back off after exec_module().
    tests/test_030_pe_machine_assert.py:36
  • _load_module() prepends eng/scripts to sys.path but never restores it, which can affect imports in later tests. Use a try/finally around the sys.path change so it’s removed after exec_module().
    OneBranchPipelines/scripts/.gitattributes:1
  • PR description says this enforces LF for all .sh scripts, but this .gitattributes only applies under OneBranchPipelines/scripts/ (and there’s a separate conda/.gitattributes). If the intent is repo-wide enforcement, consider moving the rule to a root .gitattributes (and avoiding per-directory duplication).
    OneBranchPipelines/steps/conda-build-validate-step-posix.yml:47
  • The parameter comment says the step appends the target subdir to outputDir, but the orchestrator actually appends condaSubdir (the leg’s display/staging subdir). This mismatch makes it harder to reason about how directory isolation works on shared agents.

OneBranchPipelines/jobs/consolidate-conda-artifacts-job.yml:92

  • The find/cp pipeline uses unquoted $(Pipeline.Workspace) / $(ob_outputDirectory) expansions. These are paths and should be quoted to avoid word-splitting or globbing if the agent directory contains spaces or special characters.
          while IFS= read -r p; do
            subdir=$(basename "$(dirname "$p")")
            mkdir -p "$(ob_outputDirectory)/conda/$subdir"
            cp -v "$p" "$(ob_outputDirectory)/conda/$subdir/"
            found=1
          done < <(find $(Pipeline.Workspace)/all-artifacts -type f \( -name 'mssql-python*.conda' -o -name 'mssql-python*.tar.bz2' \))
  • Files reviewed: 14/14 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

…ict-mode+quoting, test sys.path leak, posix comment

_import_probe uses os.path.abspath (not realpath) so a conda softlink install is not false-failed; still fails closed on cwd/PYTHONPATH/.pth shadows. consolidate: set -euo pipefail + quoted path expansions. test_029/030: try/finally pops eng/scripts off sys.path after exec_module (no session leak). posix outputDir comment: appends condaSubdir, not target subdir.
…ot discard linux-64 packages

The 'Install libc6-arm64-cross + register QEMU binfmt' setup step was BLOCKING while the linux-aarch64 leg it feeds is continueOnError. A transient apt/binfmt flake failed the whole shared Linux job -> condition:succeeded() publish skipped -> validated linux-64 packages discarded, contradicting the aarch64 blast-radius comment. Added continueOnError:true (fail-loud exit 1 + red step still surface a real failure; release gate still blocks a MISSING aarch64 subdir).

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.

Scheduled Copilot review — conda build pipeline + build-validation tooling

Recommendation: Approve with Minor Changes (2 Low, non-blocking). Overall risk: Low. Design compliance: Not Applicable (no formal ADR governs this CI/packaging tooling).

Reviewed the true PR scope (three-dot origin/main...55e56c43, 14 conda/build-tooling files) for correctness, security/trust boundaries, supply chain, cross-platform/arch coverage, fail-closed behavior, tests, and repository conventions. All prior-round findings are resolved. The orchestrator is injection-safe (list-form subprocess), SHA256-verifies Miniforge before execution, keeps fail-closed static audits, and pins the conda version to the ESRP-signed wheel filename. Two small non-blocking gaps are noted inline.

Questions / Needs Verification (non-blocking)

  • win-arm64 real conda create failure is treated as infrastructure (build_conda_packages.py ~467-477): any nonzero exit from the real create is logged and skipped after the blocking --dry-run solve. Arch is still enforced by the PE-machine assert and solvability by the dry-run, and nothing ships (build-only) — please confirm a genuinely-broken-but-solvable package can't stage undetected on this leg.
  • gather_wheels uses odbc_matches[0] (build_conda_packages.py ~97) while copying all mssql matches. Per-leg odbcWheelFilter values are arch-specific and pip's platform-tag enforcement would reject a wrong-arch pick, so [0] is safe in practice — please confirm the odbc drop can only ever contain one wheel matching each filter, else consider asserting len(odbc_matches) == 1.
  • Linux audit does not assert a required distro SET (audit_bundled_binaries.py 438-443, documented): a wholesale-missing distro payload passes the audit, and on the emulated linux-aarch64 leg the driver-probe failure is swallowed — please confirm the wheel-build validation is the intended source of truth for distro-set completeness.

)
# win-arm64 is cross-built on x64 where its runtime import is skipped, so its arch is
# trusted from the wheel filename UNLESS the PE machine assert reads it out of the payload.
if target_subdir == "win-arm64":

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.

audit_packages runs the PE-machine assert only for win-arm64. osx-arm64 is cross-built on the Intel agent so its runtime import auto-skips (verify lines 504-511), and — as the pipeline comment at conda-build-pipeline.yml:216-218 notes — its arch is trusted purely from the universal2 filename tag with no Mach-O check. A payload missing its arm64 slice would build, solve, skip execution, and stage. This is Low (documented, continueOnError, build-only, slices validated upstream at wheel build), but consider closing the asymmetry with win-arm64 by adding a static Mach-O slice assert (parse the fat header / lipo -archs) for the osx legs.

Comment thread eng/scripts/_conda_pkg.py
f = tf.extractfile(m)
if f is not None:
yield m.name, f.read()
elif path.endswith(".tar.bz2"):

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.

iter_payload_members has no final else, so a path that is neither .conda nor .tar.bz2 yields an empty iterator silently (fail-open), whereas read_index (line 88) raises ValueError("unrecognized conda package extension") for the same case. It's currently unreachable (the audit globs only *.conda/*.tar.bz2), so this is latent — but for fail-closed consistency and future callers, consider adding else: raise ValueError(f"{path}: unrecognized conda package extension").

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.

4 participants