Skip to content

CHORE: validate macOS wheel is self-contained by removing system ODBC before tests#658

Merged
bewithgaurav merged 8 commits into
mainfrom
jahnvi/macos-uninstall-unixodbc-pipeline
Jul 15, 2026
Merged

CHORE: validate macOS wheel is self-contained by removing system ODBC before tests#658
bewithgaurav merged 8 commits into
mainfrom
jahnvi/macos-uninstall-unixodbc-pipeline

Conversation

@jahnvi480

@jahnvi480 jahnvi480 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

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).

@github-actions github-actions Bot added the pr-size: small Minimal code update label Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 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 4 commits July 2, 2026 21:36
Revert the 'uninstall system unixODBC' step (it only ran on the x86_64
runner, whose dylib is already correct, and it broke the benchmark by
removing unixODBC that pyodbc needs).

Add test_macos_dylibs_have_no_absolute_dependency_paths in
test_000_dependencies.py. It runs 'otool -L' on every bundled dylib in
both libs/macos/arm64 and libs/macos/x86_64 and fails if a sibling
bundled library is referenced via an absolute path instead of
@loader_path/@rpath. Because otool reads Mach-O load commands of any
architecture, the x86_64 macOS CI lane now catches the arm64-only
packaging bug behind GitHub #656.
Verified against the shipped binaries that:
- arm64/libmsodbcsql.18.dylib hardcodes /opt/homebrew/lib/libodbcinst.2.dylib
  (the GH #656 break); x86_64 gets rewritten to @loader_path at build time
  because configure_dylibs.sh only fixes ARCH=\ on the Intel runner.
- ddbc_bindings dlopens libmsodbcsql.18.dylib DIRECTLY, so libodbc.2.dylib is
  off the runtime load path (and is never touched by configure_dylibs.sh).

The previous test scanned every dylib, so it would have flagged the dead
libodbc.2.dylib and could never go green even after the real fix. Replace it
with test_macos_driver_load_chain_is_relocatable, which starts at the driver
and follows only its bundled sibling deps -- matching what dlopen resolves at
runtime, ignoring external openssl, and catching the arm64 bug from an x86_64
runner via otool.
…river

newer Homebrew (6.x) refuses to load formulae from third-party taps unless trusted, so brew install msodbcsql18 in the macOS benchmark step failed with "Refusing to load formula ... from untrusted tap". the driver never installed, so every pyodbc connection failed with "[unixODBC][Driver Manager]Can't open lib 'ODBC Driver 18 for SQL Server' : file not found" and the benchmark's pyodbc column came out all N/A. the step has continueOnError so the job stayed green.

add brew trust microsoft/mssql-release between the tap and install. verified locally: forcing HOMEBREW_REQUIRE_TAP_TRUST=1 reproduces the error, brew trust clears it, and msodbcsql18 resolves.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added pr-size: medium Moderate update size and removed pr-size: small Minimal code update labels Jul 14, 2026
@bewithgaurav bewithgaurav marked this pull request as ready for review July 15, 2026 07:34
Copilot AI review requested due to automatic review settings July 15, 2026 07:34
@bewithgaurav bewithgaurav changed the title CHORE: uninstall system unixODBC before tests to validate bundled… CHORE: ensure macOS tests exercise the bundled driver, not system ODBC Jul 15, 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

This PR strengthens macOS packaging validation by adding a dependency-inspection test to ensure the bundled msodbcsql18 load chain doesn’t hardcode Homebrew absolute paths (addressing GitHub issue #656), and tweaks the macOS pipeline’s Homebrew tap handling for installing msodbcsql18 during benchmarks.

Changes:

  • Added a macOS-only test that walks libmsodbcsql.18.dylib’s bundled dependency graph using otool -L and fails on absolute-path links to bundled siblings.
  • Added a brew trust attempt before installing msodbcsql18 in the macOS benchmark step.

Reviewed changes

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

File Description
tests/test_000_dependencies.py Adds a macOS relocatability test that inspects Mach-O dependency paths for bundled dylibs.
eng/pipelines/pr-validation-pipeline.yml Adds a Homebrew “trust tap” attempt before installing msodbcsql18 for macOS benchmarks.

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

Comment thread tests/test_000_dependencies.py
Comment thread tests/test_000_dependencies.py Outdated
Comment thread eng/pipelines/pr-validation-pipeline.yml
black (line-length 100) collapses the multi-line subprocess.run into one 99-char line. fixes the Python Linting check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added pr-size: small Minimal code update and removed pr-size: medium Moderate update size labels Jul 15, 2026
…river

the macOS pytest job had no step to remove system/Homebrew unixODBC and msodbcsql18 before running tests, unlike every Linux job. without it, mssql-python could load a system driver manager from the default dyld path and pass, hiding a broken bundle. the otool load-chain test is a static check and does not guarantee the bundled driver manager is the one loaded at runtime.

add an uninstall step to PytestOnMacOS mirroring the Linux jobs: remove Homebrew msodbcsql18/mssql-tools18/unixodbc, delete leftover libodbc/libodbcinst dylibs and odbcinst config from both Homebrew prefixes, then otool -L the bundled driver to confirm it is intact. every command is guarded so it is a no-op when nothing is installed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bewithgaurav bewithgaurav changed the title CHORE: ensure macOS tests exercise the bundled driver, not system ODBC CHORE: validate macOS wheel is self-contained by removing system ODBC before tests Jul 15, 2026
@github-actions github-actions Bot added pr-size: medium Moderate update size and removed pr-size: small Minimal code update labels Jul 15, 2026
the relocatability test silently continued when a driver dylib was missing, so if the packaging layout ever changed the test would pass without validating anything. now it records which arches were checked and asserts at least one bundled driver was found. also corrects the walk comment: queue.pop() is LIFO, so it is depth-first, not breadth-first (order does not affect the result). addresses the two review comments on the test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bewithgaurav

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@bewithgaurav bewithgaurav merged commit 7d22409 into main Jul 15, 2026
29 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: medium Moderate update size

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants