From 2979b15dd3f6b16d33c87ae2a8e59cab83c2f1a4 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Mon, 5 Oct 2026 12:37:35 +0530 Subject: [PATCH 1/5] FIX: Preserve Python-specific release dependencies Compile supported multi-version locks in universal mode so environment markers remain intact across scheduled refreshes. Keep ODBC generation unchanged and add focused regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/refresh-build-dependencies.yml | 9 ++- eng/requirements-build-linux.txt | 10 +++- eng/requirements-build-macos.txt | 18 ++++-- eng/requirements-build-windows.txt | 10 ++-- eng/requirements-test-linux.txt | 19 +++++-- tests/test_release_build_dependencies.py | 56 +++++++++++++++++++ 6 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 tests/test_release_build_dependencies.py diff --git a/.github/workflows/refresh-build-dependencies.yml b/.github/workflows/refresh-build-dependencies.yml index 775fd2dcf..ca56de461 100644 --- a/.github/workflows/refresh-build-dependencies.yml +++ b/.github/workflows/refresh-build-dependencies.yml @@ -52,8 +52,8 @@ jobs: enable-cache: false - name: Compile Linux dependency locks run: | - uv pip compile ${{ github.event_name != 'pull_request' && '--upgrade' || '' }} --generate-hashes --no-emit-index-url --no-header --strip-extras --python-version 3.10 --default-index https://pypi.org/simple --output-file eng/requirements-build-linux.txt eng/requirements-build-linux.in - uv pip compile ${{ github.event_name != 'pull_request' && '--upgrade' || '' }} --generate-hashes --no-emit-index-url --no-header --strip-extras --python-version 3.10 --default-index https://pypi.org/simple --output-file eng/requirements-test-linux.txt eng/requirements-test-linux.in + uv pip compile ${{ github.event_name != 'pull_request' && '--upgrade' || '' }} --universal --generate-hashes --no-emit-index-url --no-header --strip-extras --python-version 3.10 --default-index https://pypi.org/simple --output-file eng/requirements-build-linux.txt eng/requirements-build-linux.in + uv pip compile ${{ github.event_name != 'pull_request' && '--upgrade' || '' }} --universal --generate-hashes --no-emit-index-url --no-header --strip-extras --python-version 3.10 --default-index https://pypi.org/simple --output-file eng/requirements-test-linux.txt eng/requirements-test-linux.in - name: Verify committed Linux locks are current if: github.event_name == 'pull_request' run: | @@ -80,14 +80,17 @@ jobs: os: macos-latest input: eng/requirements-build-macos.in output: eng/requirements-build-macos.txt + universal: --universal - name: windows os: windows-latest input: eng/requirements-build-windows.in output: eng/requirements-build-windows.txt + universal: --universal - name: odbc os: windows-latest input: eng/requirements-build-odbc.in output: eng/requirements-build-odbc.txt + universal: "" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -100,7 +103,7 @@ jobs: - name: Compile ${{ matrix.name }} dependency lock # Python 3.10 is the oldest supported release, so every lock remains # installable throughout the release pipeline's Python 3.10-3.14 matrix. - run: uv pip compile ${{ github.event_name != 'pull_request' && '--upgrade' || '' }} --generate-hashes --no-emit-index-url --no-header --strip-extras --python-version 3.10 --default-index https://pypi.org/simple --output-file "${{ matrix.output }}" "${{ matrix.input }}" + run: uv pip compile ${{ github.event_name != 'pull_request' && '--upgrade' || '' }} ${{ matrix.universal }} --generate-hashes --no-emit-index-url --no-header --strip-extras --python-version 3.10 --default-index https://pypi.org/simple --output-file "${{ matrix.output }}" "${{ matrix.input }}" - name: Verify committed ${{ matrix.name }} lock is current if: github.event_name == 'pull_request' shell: bash diff --git a/eng/requirements-build-linux.txt b/eng/requirements-build-linux.txt index ff44a7886..ce528b83e 100644 --- a/eng/requirements-build-linux.txt +++ b/eng/requirements-build-linux.txt @@ -1,4 +1,8 @@ -exceptiongroup==1.3.1 \ +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via pytest +exceptiongroup==1.3.1 ; python_full_version < '3.11' \ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 # via pytest @@ -36,7 +40,7 @@ setuptools==84.0.0 \ --hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670 \ --hash=sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73 # via -r eng/requirements-build-linux.in -tomli==2.4.1 \ +tomli==2.4.1 ; python_full_version < '3.11' \ --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ @@ -85,7 +89,7 @@ tomli==2.4.1 \ --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 # via pytest -typing-extensions==4.16.0 \ +typing-extensions==4.16.0 ; python_full_version < '3.11' \ --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 # via exceptiongroup diff --git a/eng/requirements-build-macos.txt b/eng/requirements-build-macos.txt index e22b4c6b9..75745d44b 100644 --- a/eng/requirements-build-macos.txt +++ b/eng/requirements-build-macos.txt @@ -73,7 +73,7 @@ azure-identity==1.25.3 \ --hash=sha256:ab23c0d63015f50b630ef6c6cf395e7262f439ce06e5d07a64e874c724f8d9e6 \ --hash=sha256:f4d0b956a8146f30333e071374171f3cfa7bdb8073adb8c3814b65567aa7447c # via -r eng/../requirements.txt -backports-asyncio-runner==1.2.0 \ +backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' \ --hash=sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5 \ --hash=sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162 # via pytest-asyncio @@ -110,7 +110,7 @@ certifi==2026.7.22 \ --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \ --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55 # via requests -cffi==2.1.1 \ +cffi==2.1.1 ; platform_python_implementation != 'PyPy' \ --hash=sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e \ --hash=sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66 \ --hash=sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2 \ @@ -411,6 +411,12 @@ cmake==4.4.3 \ --hash=sha256:d36c9bb471a67970b031d45b29c3b852f14bc8b64ac46b39ccc046fdf652f36d \ --hash=sha256:f5aead3c09bfa662da5d1d65f6c526a449966314913039ce6de3c0ff82ea9266 # via -r eng/requirements-build-macos.in +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via + # pylint + # pytest coverage==7.16.0 \ --hash=sha256:01b18b8a6c9cec8d5f45550e2501426ed982cf2c35016b0acd2ba9b5d8b2fb06 \ --hash=sha256:0466f4a5c0370461b7d8c7eb259d7d1db0b5756f13d66230b04d22a1d380ee11 \ @@ -599,7 +605,7 @@ dill==0.4.1 \ --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa # via pylint -exceptiongroup==1.3.1 \ +exceptiongroup==1.3.1 ; python_full_version < '3.11' \ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 # via pytest @@ -654,7 +660,7 @@ isort==9.0.1 \ --hash=sha256:fb7d55156a1f766a2b097165524f07be61ececa41a71ca33d24a00777f79a829 \ --hash=sha256:fd326823ddbe338357ba1823b7f96481d4421d54c83ebd43c92f1b51314a24ae # via pylint -librt==0.15.0 \ +librt==0.15.0 ; platform_python_implementation != 'PyPy' \ --hash=sha256:04d5387b908676c0b8d5d2f5fb58373b4ea382d81f7a6f0fab8ea2a462bb4738 \ --hash=sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302 \ --hash=sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad \ @@ -1170,7 +1176,7 @@ pycodestyle==2.14.0 \ # via # autopep8 # flake8 -pycparser==3.0 \ +pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi @@ -1259,7 +1265,7 @@ setuptools==84.0.0 \ --hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670 \ --hash=sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73 # via -r eng/../requirements.txt -tomli==2.4.1 \ +tomli==2.4.1 ; python_full_version <= '3.11' \ --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ diff --git a/eng/requirements-build-windows.txt b/eng/requirements-build-windows.txt index 97dd0ed0c..395e7ffc5 100644 --- a/eng/requirements-build-windows.txt +++ b/eng/requirements-build-windows.txt @@ -1,12 +1,12 @@ -backports-asyncio-runner==1.2.0 \ +backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' \ --hash=sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5 \ --hash=sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162 # via pytest-asyncio -colorama==0.4.6 \ +colorama==0.4.6 ; sys_platform == 'win32' \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via pytest -exceptiongroup==1.3.1 \ +exceptiongroup==1.3.1 ; python_full_version < '3.11' \ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 # via pytest @@ -139,7 +139,7 @@ setuptools==84.0.0 \ --hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670 \ --hash=sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73 # via -r eng/requirements-build-windows.in -tomli==2.4.1 \ +tomli==2.4.1 ; python_full_version < '3.11' \ --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ @@ -188,7 +188,7 @@ tomli==2.4.1 \ --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 # via pytest -typing-extensions==4.16.0 \ +typing-extensions==4.16.0 ; python_full_version < '3.13' \ --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 # via diff --git a/eng/requirements-test-linux.txt b/eng/requirements-test-linux.txt index 47be05d47..a2fdeea14 100644 --- a/eng/requirements-test-linux.txt +++ b/eng/requirements-test-linux.txt @@ -73,7 +73,7 @@ azure-identity==1.25.3 \ --hash=sha256:ab23c0d63015f50b630ef6c6cf395e7262f439ce06e5d07a64e874c724f8d9e6 \ --hash=sha256:f4d0b956a8146f30333e071374171f3cfa7bdb8073adb8c3814b65567aa7447c # via -r eng/../requirements.txt -backports-asyncio-runner==1.2.0 \ +backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' \ --hash=sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5 \ --hash=sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162 # via pytest-asyncio @@ -110,7 +110,7 @@ certifi==2026.7.22 \ --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \ --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55 # via requests -cffi==2.1.1 \ +cffi==2.1.1 ; platform_python_implementation != 'PyPy' \ --hash=sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e \ --hash=sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66 \ --hash=sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2 \ @@ -390,6 +390,13 @@ click==8.5.0 \ --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 # via black +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via + # -c eng/requirements-build-linux.txt + # pylint + # pytest coverage==7.16.0 \ --hash=sha256:01b18b8a6c9cec8d5f45550e2501426ed982cf2c35016b0acd2ba9b5d8b2fb06 \ --hash=sha256:0466f4a5c0370461b7d8c7eb259d7d1db0b5756f13d66230b04d22a1d380ee11 \ @@ -574,7 +581,7 @@ dill==0.4.1 \ --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa # via pylint -exceptiongroup==1.3.1 \ +exceptiongroup==1.3.1 ; python_full_version < '3.11' \ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 # via @@ -633,7 +640,7 @@ isort==9.0.1 \ --hash=sha256:fb7d55156a1f766a2b097165524f07be61ececa41a71ca33d24a00777f79a829 \ --hash=sha256:fd326823ddbe338357ba1823b7f96481d4421d54c83ebd43c92f1b51314a24ae # via pylint -librt==0.15.0 \ +librt==0.15.0 ; platform_python_implementation != 'PyPy' \ --hash=sha256:04d5387b908676c0b8d5d2f5fb58373b4ea382d81f7a6f0fab8ea2a462bb4738 \ --hash=sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302 \ --hash=sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad \ @@ -1156,7 +1163,7 @@ pycodestyle==2.14.0 \ # via # autopep8 # flake8 -pycparser==3.0 \ +pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi @@ -1252,7 +1259,7 @@ setuptools==84.0.0 \ # -c eng/requirements-build-linux.txt # -r eng/../requirements.txt # -r eng/requirements-build-linux.in -tomli==2.4.1 \ +tomli==2.4.1 ; python_full_version <= '3.11' \ --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ diff --git a/tests/test_release_build_dependencies.py b/tests/test_release_build_dependencies.py new file mode 100644 index 000000000..b4f65cfc7 --- /dev/null +++ b/tests/test_release_build_dependencies.py @@ -0,0 +1,56 @@ +import re +from pathlib import Path + + +ROOT = Path(__file__).parents[1] +WORKFLOW = ROOT / ".github" / "workflows" / "refresh-build-dependencies.yml" + + +def test_lock_generation_preserves_python_markers(): + workflow = WORKFLOW.read_text(encoding="utf-8") + + for output in ( + "eng/requirements-build-linux.txt", + "eng/requirements-test-linux.txt", + ): + command = next( + line + for line in workflow.splitlines() + if "uv pip compile" in line and output in line + ) + assert "--universal" in command + + assert re.search( + r"name: macos\b.*?universal: --universal", workflow, re.DOTALL + ) + assert re.search( + r"name: windows\b.*?universal: --universal", workflow, re.DOTALL + ) + assert re.search(r"name: odbc\b.*?universal: \"\"", workflow, re.DOTALL) + platform_command = next( + line + for line in workflow.splitlines() + if "uv pip compile" in line and "matrix.output" in line + ) + assert "${{ matrix.universal }}" in platform_command + + +def test_asyncio_backport_is_only_installed_below_python_311(): + affected_locks = ( + "requirements-build-windows.txt", + "requirements-build-macos.txt", + "requirements-test-linux.txt", + ) + + for lock_name in affected_locks: + lock = (ROOT / "eng" / lock_name).read_text(encoding="utf-8") + requirement = next( + line + for line in lock.splitlines() + if line.startswith("backports-asyncio-runner==") + ) + assert re.fullmatch( + r"backports-asyncio-runner==1\.2\.0 ; " + r"python_(?:full_)?version < ['\"]3\.11['\"] \\", + requirement, + ) From 70a837637a4a6b5312be3c23a2d48b956f686059 Mon Sep 17 00:00:00 2001 From: Gaurav Sharma Date: Mon, 5 Oct 2026 16:20:55 +0530 Subject: [PATCH 2/5] FIX: Skip profiler contracts in wheel tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_036_profiler_ci.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_036_profiler_ci.py b/tests/test_036_profiler_ci.py index 8f5e24eed..66ca343b0 100644 --- a/tests/test_036_profiler_ci.py +++ b/tests/test_036_profiler_ci.py @@ -22,7 +22,10 @@ import pytest ROOT = Path(__file__).resolve().parents[1] -if not (ROOT / ".github/scripts/post_profiler_comment.py").is_file(): +if ( + not (ROOT / "eng/profiler_benchmarks").is_dir() + or not (ROOT / ".github/scripts/post_profiler_comment.py").is_file() +): pytest.skip("CI reporting tools are not installed in driver wheels", allow_module_level=True) from eng.profiler_benchmarks import controller From 7970a9e78ae1356bd90d6e1f3fa7f3abaf1e0c23 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Mon, 5 Oct 2026 17:24:11 +0530 Subject: [PATCH 3/5] FIX: Include runner contracts in Linux wheel tests Copy the narrow set of repository-side runner contract files into isolated Linux wheel test directories, and surface subprocess diagnostics if the status artifact is absent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../stages/build-linux-single-stage.yml | 16 ++++++++++++++ tests/test_038_mssql_odbc_daily_validation.py | 21 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/OneBranchPipelines/stages/build-linux-single-stage.yml b/OneBranchPipelines/stages/build-linux-single-stage.yml index 74e3796f2..35ba06019 100644 --- a/OneBranchPipelines/stages/build-linux-single-stage.yml +++ b/OneBranchPipelines/stages/build-linux-single-stage.yml @@ -324,6 +324,14 @@ stages: cp -r /workspace/tests $TEST_DIR/ || echo "WARNING: No tests directory"; # Some tests read repo-side helper scripts/workflows (e.g. .github/scripts/prepare_fork_coverage_comment.py). cp -r /workspace/.github $TEST_DIR/ || echo "WARNING: No .github directory"; + # Keep the isolated wheel test fixture narrow, but include the files + # required by the mssql-odbc runner contract tests. + mkdir -p $TEST_DIR/eng/scripts $TEST_DIR/eng/pipelines $TEST_DIR/eng/versions; + mkdir -p $TEST_DIR/OneBranchPipelines/stages; + cp /workspace/eng/scripts/run-mssql-odbc-tests.sh /workspace/eng/scripts/verify_mssql_odbc_provider.py $TEST_DIR/eng/scripts/; + cp /workspace/eng/pipelines/mssql-odbc-daily-validation-pipeline.yml $TEST_DIR/eng/pipelines/; + cp /workspace/eng/versions/mssql-python-rs-nuget.version $TEST_DIR/eng/versions/; + cp /workspace/OneBranchPipelines/stages/build-linux-single-stage.yml $TEST_DIR/OneBranchPipelines/stages/; cp /workspace/pytest.ini $TEST_DIR/ || echo "WARNING: No pytest.ini"; # Step 8: Run pytest (stops on first failure) @@ -394,6 +402,14 @@ stages: cp -r /workspace/tests $TEST_DIR/ || echo "WARNING: No tests directory"; # Some tests read repo-side helper scripts/workflows (e.g. .github/scripts/prepare_fork_coverage_comment.py). cp -r /workspace/.github $TEST_DIR/ || echo "WARNING: No .github directory"; + # Keep the isolated wheel test fixture narrow, but include the files + # required by the mssql-odbc runner contract tests. + mkdir -p $TEST_DIR/eng/scripts $TEST_DIR/eng/pipelines $TEST_DIR/eng/versions; + mkdir -p $TEST_DIR/OneBranchPipelines/stages; + cp /workspace/eng/scripts/run-mssql-odbc-tests.sh /workspace/eng/scripts/verify_mssql_odbc_provider.py $TEST_DIR/eng/scripts/; + cp /workspace/eng/pipelines/mssql-odbc-daily-validation-pipeline.yml $TEST_DIR/eng/pipelines/; + cp /workspace/eng/versions/mssql-python-rs-nuget.version $TEST_DIR/eng/versions/; + cp /workspace/OneBranchPipelines/stages/build-linux-single-stage.yml $TEST_DIR/OneBranchPipelines/stages/; cp /workspace/pytest.ini $TEST_DIR/ || echo "WARNING: No pytest.ini"; # Step 8: Run pytest (stops on first failure) diff --git a/tests/test_038_mssql_odbc_daily_validation.py b/tests/test_038_mssql_odbc_daily_validation.py index 539e65ea6..e28d814e6 100644 --- a/tests/test_038_mssql_odbc_daily_validation.py +++ b/tests/test_038_mssql_odbc_daily_validation.py @@ -13,6 +13,7 @@ RUNNER = ROOT / "eng" / "scripts" / "run-mssql-odbc-tests.sh" PIPELINE = ROOT / "eng" / "pipelines" / "mssql-odbc-daily-validation-pipeline.yml" PREFLIGHT = ROOT / "eng" / "scripts" / "verify_mssql_odbc_provider.py" +RELEASE_PIPELINE = ROOT / "OneBranchPipelines" / "stages" / "build-linux-single-stage.yml" @unittest.skipUnless(sys.platform.startswith("linux"), "runner requires Linux GNU timeout and bash") @@ -59,7 +60,12 @@ def run_runner(self, behavior: str, total_budget: str = "60s", provider: str = " text=True, check=False, ) - status = (results / "runner.status").read_text(encoding="utf-8").strip() + status_file = results / "runner.status" + self.assertTrue( + status_file.is_file(), + f"runner did not write {status_file}\nstdout:\n{proc.stdout}\nstderr:\n{proc.stderr}", + ) + status = status_file.read_text(encoding="utf-8").strip() reports = { path.name: ElementTree.parse(path).getroot() for path in results.glob("*.xml") } @@ -107,6 +113,19 @@ def test_success_without_junit_is_blocking_harness_failure(self): class PipelineContractTests(unittest.TestCase): + def test_release_wheel_fixture_includes_runner_contract_files(self): + pipeline = RELEASE_PIPELINE.read_text(encoding="utf-8") + + for path in ( + "eng/scripts/run-mssql-odbc-tests.sh", + "eng/scripts/verify_mssql_odbc_provider.py", + "eng/pipelines/mssql-odbc-daily-validation-pipeline.yml", + "eng/versions/mssql-python-rs-nuget.version", + "OneBranchPipelines/stages/build-linux-single-stage.yml", + ): + self.assertEqual(pipeline.count(f"/workspace/{path}"), 2) + self.assertNotIn("cp -r /workspace/eng ", pipeline) + def test_runner_uses_busybox_compatible_timeout_options(self): runner = RUNNER.read_text(encoding="utf-8") From 2bccf8dbb14283e9ce0b9929746eeaef7dd437d0 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Mon, 5 Oct 2026 21:04:41 +0530 Subject: [PATCH 4/5] FIX: Exclude release contracts from wheel tests Keep installed-wheel validation focused on package behavior while release lock and workflow contracts continue to run from a complete source checkout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../stages/build-linux-single-stage.yml | 10 ++++++-- tests/test_release_build_dependencies.py | 24 +++++++++---------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/OneBranchPipelines/stages/build-linux-single-stage.yml b/OneBranchPipelines/stages/build-linux-single-stage.yml index 35ba06019..62b500b5a 100644 --- a/OneBranchPipelines/stages/build-linux-single-stage.yml +++ b/OneBranchPipelines/stages/build-linux-single-stage.yml @@ -337,8 +337,11 @@ stages: # Step 8: Run pytest (stops on first failure) if [ -d $TEST_DIR/tests ]; then echo "Running pytest for ${PYBIN}..."; + # Release-lock tests validate repository workflows and lock files, + # not the installed wheel; run them in source-checkout validation. DB_CONNECTION_STRING="Server=$SQL_IP;Database=master;Uid=SA;Pwd=$DB_PASSWORD;TrustServerCertificate=yes" \ - $PY -m pytest $TEST_DIR/tests -v --maxfail=1 || { + $PY -m pytest $TEST_DIR/tests -v --maxfail=1 \ + --ignore=tests/test_release_build_dependencies.py || { echo "ERROR: Tests failed for ${PYBIN}"; exit 1; }; @@ -415,8 +418,11 @@ stages: # Step 8: Run pytest (stops on first failure) if [ -d $TEST_DIR/tests ]; then echo "Running pytest for ${PYBIN}..."; + # Release-lock tests validate repository workflows and lock files, + # not the installed wheel; run them in source-checkout validation. DB_CONNECTION_STRING="Server=$SQL_IP;Database=master;Uid=SA;Pwd=$DB_PASSWORD;TrustServerCertificate=yes" \ - $PY -m pytest $TEST_DIR/tests -v --maxfail=1 || { + $PY -m pytest $TEST_DIR/tests -v --maxfail=1 \ + --ignore=tests/test_release_build_dependencies.py || { echo "ERROR: Tests failed for ${PYBIN}"; exit 1; }; diff --git a/tests/test_release_build_dependencies.py b/tests/test_release_build_dependencies.py index b4f65cfc7..976cf9ca7 100644 --- a/tests/test_release_build_dependencies.py +++ b/tests/test_release_build_dependencies.py @@ -1,9 +1,9 @@ import re from pathlib import Path - ROOT = Path(__file__).parents[1] WORKFLOW = ROOT / ".github" / "workflows" / "refresh-build-dependencies.yml" +RELEASE_PIPELINE = ROOT / "OneBranchPipelines" / "stages" / "build-linux-single-stage.yml" def test_lock_generation_preserves_python_markers(): @@ -14,18 +14,12 @@ def test_lock_generation_preserves_python_markers(): "eng/requirements-test-linux.txt", ): command = next( - line - for line in workflow.splitlines() - if "uv pip compile" in line and output in line + line for line in workflow.splitlines() if "uv pip compile" in line and output in line ) assert "--universal" in command - assert re.search( - r"name: macos\b.*?universal: --universal", workflow, re.DOTALL - ) - assert re.search( - r"name: windows\b.*?universal: --universal", workflow, re.DOTALL - ) + assert re.search(r"name: macos\b.*?universal: --universal", workflow, re.DOTALL) + assert re.search(r"name: windows\b.*?universal: --universal", workflow, re.DOTALL) assert re.search(r"name: odbc\b.*?universal: \"\"", workflow, re.DOTALL) platform_command = next( line @@ -45,12 +39,16 @@ def test_asyncio_backport_is_only_installed_below_python_311(): for lock_name in affected_locks: lock = (ROOT / "eng" / lock_name).read_text(encoding="utf-8") requirement = next( - line - for line in lock.splitlines() - if line.startswith("backports-asyncio-runner==") + line for line in lock.splitlines() if line.startswith("backports-asyncio-runner==") ) assert re.fullmatch( r"backports-asyncio-runner==1\.2\.0 ; " r"python_(?:full_)?version < ['\"]3\.11['\"] \\", requirement, ) + + +def test_isolated_wheel_suite_excludes_release_repository_contracts(): + pipeline = RELEASE_PIPELINE.read_text(encoding="utf-8") + + assert pipeline.count("--ignore=tests/test_release_build_dependencies.py") == 2 From b5c11999461079f986a746471de5a5951f44c572 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Tue, 6 Oct 2026 17:33:44 +0530 Subject: [PATCH 5/5] FIX: Harden release dependency contract tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_038_mssql_odbc_daily_validation.py | 2 +- tests/test_release_build_dependencies.py | 48 +++++++++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/tests/test_038_mssql_odbc_daily_validation.py b/tests/test_038_mssql_odbc_daily_validation.py index e28d814e6..11c76c710 100644 --- a/tests/test_038_mssql_odbc_daily_validation.py +++ b/tests/test_038_mssql_odbc_daily_validation.py @@ -124,7 +124,7 @@ def test_release_wheel_fixture_includes_runner_contract_files(self): "OneBranchPipelines/stages/build-linux-single-stage.yml", ): self.assertEqual(pipeline.count(f"/workspace/{path}"), 2) - self.assertNotIn("cp -r /workspace/eng ", pipeline) + self.assertNotRegex(pipeline, r"\bcp\s+-r\s+/workspace/eng(?:\s|$)") def test_runner_uses_busybox_compatible_timeout_options(self): runner = RUNNER.read_text(encoding="utf-8") diff --git a/tests/test_release_build_dependencies.py b/tests/test_release_build_dependencies.py index 976cf9ca7..607b6e8a6 100644 --- a/tests/test_release_build_dependencies.py +++ b/tests/test_release_build_dependencies.py @@ -6,6 +6,27 @@ RELEASE_PIPELINE = ROOT / "OneBranchPipelines" / "stages" / "build-linux-single-stage.yml" +def _compile_command(workflow, output): + commands = [ + line.strip() + for line in workflow.splitlines() + if "uv pip compile" in line and output in line + ] + assert ( + len(commands) == 1 + ), f"Expected exactly one compile command for {output}, found {len(commands)}: {commands}" + return commands[0] + + +def _platform_matrix_item(workflow, name): + compile_platform = workflow.split(" compile-platform:", maxsplit=1)[1] + matrix = compile_platform.split("\n steps:", maxsplit=1)[0] + marker = f" - name: {name}\n" + items = matrix.split(marker) + assert len(items) == 2, f"Expected exactly one {name} matrix item" + return items[1].split("\n - name:", maxsplit=1)[0] + + def test_lock_generation_preserves_python_markers(): workflow = WORKFLOW.read_text(encoding="utf-8") @@ -13,20 +34,18 @@ def test_lock_generation_preserves_python_markers(): "eng/requirements-build-linux.txt", "eng/requirements-test-linux.txt", ): - command = next( - line for line in workflow.splitlines() if "uv pip compile" in line and output in line - ) - assert "--universal" in command + assert "--universal" in _compile_command(workflow, output) - assert re.search(r"name: macos\b.*?universal: --universal", workflow, re.DOTALL) - assert re.search(r"name: windows\b.*?universal: --universal", workflow, re.DOTALL) - assert re.search(r"name: odbc\b.*?universal: \"\"", workflow, re.DOTALL) - platform_command = next( - line - for line in workflow.splitlines() - if "uv pip compile" in line and "matrix.output" in line - ) - assert "${{ matrix.universal }}" in platform_command + for name, universal in ( + ("macos", "--universal"), + ("windows", "--universal"), + ("odbc", '""'), + ): + item = _platform_matrix_item(workflow, name) + values = [line.strip() for line in item.splitlines() if "universal:" in line] + assert values == [f"universal: {universal}"] + + assert "${{ matrix.universal }}" in _compile_command(workflow, "matrix.output") def test_asyncio_backport_is_only_installed_below_python_311(): @@ -42,8 +61,7 @@ def test_asyncio_backport_is_only_installed_below_python_311(): line for line in lock.splitlines() if line.startswith("backports-asyncio-runner==") ) assert re.fullmatch( - r"backports-asyncio-runner==1\.2\.0 ; " - r"python_(?:full_)?version < ['\"]3\.11['\"] \\", + r"backports-asyncio-runner==[^ ;]+ ; python_(?:full_)?version < ['\"]3\.11['\"] \\", requirement, )