Skip to content
250 changes: 250 additions & 0 deletions .github/workflows/refresh-build-dependencies.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
name: Refresh release build dependencies

on:
pull_request:
paths:
- .github/workflows/refresh-build-dependencies.yml
- eng/requirements-build-*.in
- eng/requirements-build-*.txt
- eng/requirements-test-linux.in
- eng/requirements-test-linux.txt
- requirements.txt
- OneBranchPipelines/stages/build-*-single-stage.yml
- OneBranchPipelines/stages/build-odbc-all-stage.yml
schedule:
- cron: "0 8 * * 1"
timezone: America/Los_Angeles
workflow_dispatch:

permissions:
contents: read

concurrency:
group: refresh-release-build-dependencies
cancel-in-progress: false

jobs:
validate-configuration:
name: Validate refresh configuration
runs-on: ubuntu-latest
env:
BUILD_DEPENDENCY_WORK_ITEM: ${{ vars.BUILD_DEPENDENCY_WORK_ITEM }}
steps:
- name: Validate tracking work item
run: |
if [[ ! "$BUILD_DEPENDENCY_WORK_ITEM" =~ ^[0-9]+$ ]]; then
echo "::error::Set BUILD_DEPENDENCY_WORK_ITEM to the ADO work item ID used by automated dependency refresh PRs."
exit 1
fi

compile-linux:
name: Compile Linux locks
needs: validate-configuration
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
persist-credentials: false
- uses: astral-sh/setup-uv@3259c6206f993105e3a61b142c2d97bf4b9ef83d # v7.1.0
with:
version: "0.12.6"
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
- name: Verify committed Linux locks are current
if: github.event_name == 'pull_request'
run: |
set -euo pipefail
git ls-files --error-unmatch -- eng/requirements-build-linux.txt eng/requirements-test-linux.txt >/dev/null
git diff --exit-code -- eng/requirements-build-linux.txt eng/requirements-test-linux.txt
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-lock-linux
path: |
eng/requirements-build-linux.txt
eng/requirements-test-linux.txt
if-no-files-found: error

compile-platform:
name: Compile ${{ matrix.name }} lock
needs: validate-configuration
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: macos
os: macos-latest
input: eng/requirements-build-macos.in
output: eng/requirements-build-macos.txt
- name: windows
os: windows-latest
input: eng/requirements-build-windows.in
output: eng/requirements-build-windows.txt
- name: odbc
os: windows-latest
input: eng/requirements-build-odbc.in
output: eng/requirements-build-odbc.txt
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
persist-credentials: false
- uses: astral-sh/setup-uv@3259c6206f993105e3a61b142c2d97bf4b9ef83d # v7.1.0
with:
version: "0.12.6"
enable-cache: false
- 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 }}"
- name: Verify committed ${{ matrix.name }} lock is current
if: github.event_name == 'pull_request'
shell: bash
run: |
set -euo pipefail
git ls-files --error-unmatch -- "${{ matrix.output }}" >/dev/null
git diff --exit-code -- "${{ matrix.output }}"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-lock-${{ matrix.name }}
path: ${{ matrix.output }}
if-no-files-found: error

validate-locks:
name: Validate ${{ matrix.name }} compatibility
needs:
- compile-linux
- compile-platform
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: Linux build
artifact: linux
lock: requirements-build-linux.txt
versions: "3.10 3.11 3.12 3.13 3.14"
platforms: "x86_64-manylinux_2_28 aarch64-manylinux_2_28 x86_64-unknown-linux-musl aarch64-unknown-linux-musl"
- name: Linux test
artifact: linux
lock: requirements-test-linux.txt
versions: "3.10 3.11 3.12 3.13 3.14"
platforms: "x86_64-manylinux_2_28 aarch64-manylinux_2_28 x86_64-unknown-linux-musl aarch64-unknown-linux-musl"
- name: macOS
artifact: macos
lock: requirements-build-macos.txt
versions: "3.10 3.11 3.12 3.13 3.14"
platforms: "x86_64-apple-darwin aarch64-apple-darwin"
# Windows ARM64 stages install build tools into the x64 host
# interpreter before cross-compiling the target extension.
- name: Windows build host
artifact: windows
lock: requirements-build-windows.txt
versions: "3.10 3.11 3.12 3.13 3.14"
platforms: "x86_64-pc-windows-msvc"
- name: ODBC
artifact: odbc
lock: requirements-build-odbc.txt
versions: "3.12"
platforms: "x86_64-pc-windows-msvc"
steps:
- uses: astral-sh/setup-uv@3259c6206f993105e3a61b142c2d97bf4b9ef83d # v7.1.0
with:
version: "0.12.6"
enable-cache: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: build-lock-${{ matrix.artifact }}
path: generated-lock
- name: Verify wheels for the release matrix
env:
LOCK_FILE: generated-lock/${{ matrix.lock }}
PLATFORMS: ${{ matrix.platforms }}
PYTHON_VERSIONS: ${{ matrix.versions }}
run: |
set -euo pipefail
for platform in $PLATFORMS; do
for version in $PYTHON_VERSIONS; do
echo "Validating $LOCK_FILE for Python $version on $platform"
uv pip install \
--dry-run \
--no-cache \
--target "$RUNNER_TEMP/lock-validation" \
--python-version "$version" \
--python-platform "$platform" \
--only-binary :all: \
--require-hashes \
--default-index https://pypi.org/simple \
-r "$LOCK_FILE"
done
done

open-pull-request:
name: Open dependency refresh pull request
needs: validate-locks
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
BUILD_DEPENDENCY_WORK_ITEM: ${{ vars.BUILD_DEPENDENCY_WORK_ITEM }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
fetch-depth: 0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: build-lock-*
path: generated-locks
merge-multiple: true
- name: Commit lockfile updates
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
branch="automation/refresh-build-dependencies"
remote_sha="$(git ls-remote --heads origin "refs/heads/$branch" | cut -f1)"

git switch --force-create "$branch"
cp generated-locks/*.txt eng/

if git diff --quiet -- eng/requirements-build-*.txt eng/requirements-test-linux.txt; then
echo "Release build dependencies are already current."
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add eng/requirements-build-*.txt eng/requirements-test-linux.txt
git commit -m "CHORE: refresh release build dependencies"

if [ -n "$remote_sha" ]; then
git push --force-with-lease="refs/heads/$branch:$remote_sha" origin "HEAD:refs/heads/$branch"
else
git push origin "HEAD:refs/heads/$branch"
fi

pr_count="$(gh pr list --head "$branch" --base main --state open --json number --jq length)"
if [ "$pr_count" = "0" ]; then
cat > "$RUNNER_TEMP/dependency-refresh-pr.md" <<EOF
### Work Item / Issue Reference

> AB#${BUILD_DEPENDENCY_WORK_ITEM}

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

Refreshes the platform-specific, SHA-256-locked dependencies used to build and test release wheels. The weekly workflow resolves the latest stable versions compatible with Python 3.10+ from PyPI on Linux, macOS, and Windows.
EOF
gh pr create \
--base main \
--head "$branch" \
--title "CHORE: refresh release build dependencies" \
--body-file "$RUNNER_TEMP/dependency-refresh-pr.md"
fi
16 changes: 8 additions & 8 deletions OneBranchPipelines/stages/build-linux-single-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ stages:
echo "Using: $(python --version)";

# Step 2: Install build dependencies
python -m pip install -q -U pip setuptools wheel pybind11;
python -m pip install -q --require-hashes -r /workspace/eng/requirements-build-linux.txt;

# Step 3: Build native extension (.so)
echo "Building native extension...";
Expand Down Expand Up @@ -307,6 +307,9 @@ stages:
echo "ERROR: No wheel found for ${PYBIN}";
exit 1;
fi;
# Install the reviewed runtime/test closure first so installing
# mssql-python cannot resolve Azure Identity dependencies from PyPI.
$PY -m pip install -q --require-hashes -r /workspace/eng/requirements-test-linux.txt;
echo "Installing: $WHEEL";
$PY -m pip install -q "$WHEEL";
Comment thread
sumitmsft marked this conversation as resolved.

Expand All @@ -316,13 +319,10 @@ stages:

# Step 7: Setup test environment
echo "Setting up test environment...";
$PY -m pip install -q pytest;
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";
cp /workspace/pytest.ini $TEST_DIR/ || echo "WARNING: No pytest.ini";
cp /workspace/requirements.txt $TEST_DIR/ || true;
$PY -m pip install -q -r $TEST_DIR/requirements.txt || true;

# Step 8: Run pytest (stops on first failure)
if [ -d $TEST_DIR/tests ]; then
Expand All @@ -349,7 +349,7 @@ stages:
echo "Using: $(python --version)";

# Step 2: Install build dependencies
python -m pip install -q -U pip setuptools wheel pybind11;
python -m pip install -q --require-hashes -r /workspace/eng/requirements-build-linux.txt;

# Step 3: Build native extension (.so)
echo "Building native extension...";
Expand Down Expand Up @@ -377,6 +377,9 @@ stages:
echo "ERROR: No wheel found for ${PYBIN}";
exit 1;
fi;
# Install the reviewed runtime/test closure first so installing
# mssql-python cannot resolve Azure Identity dependencies from PyPI.
$PY -m pip install -q --require-hashes -r /workspace/eng/requirements-test-linux.txt;
echo "Installing: $WHEEL";
$PY -m pip install -q "$WHEEL";

Expand All @@ -386,13 +389,10 @@ stages:

# Step 7: Setup test environment
echo "Setting up test environment...";
$PY -m pip install -q pytest;
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";
cp /workspace/pytest.ini $TEST_DIR/ || echo "WARNING: No pytest.ini";
cp /workspace/requirements.txt $TEST_DIR/ || true;
$PY -m pip install -q -r $TEST_DIR/requirements.txt || true;

# Step 8: Run pytest (stops on first failure)
if [ -d $TEST_DIR/tests ]; then
Expand Down
9 changes: 2 additions & 7 deletions OneBranchPipelines/stages/build-macos-single-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,11 @@ stages:
# PYTHON DEPENDENCIES
# =========================
# Install build dependencies:
# - requirements.txt: runtime dependencies (if any)
# - cmake: CMake Python wrapper
# - pybind11: C++/Python binding library (headers needed for compilation)
# - requirements-build-macos.txt: tested, hash-locked build and test dependencies
- script: |
python --version
python -m pip --version
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip install cmake pybind11
python -m pip install --require-hashes -r eng/requirements-build-macos.txt
displayName: 'Install dependencies'

# =========================
Expand Down Expand Up @@ -242,7 +238,6 @@ stages:
# Wheel filename: mssql_python-X.Y.Z-cp3XX-cp3XX-macosx_XX_X_universal2.whl
# bdist_wheel = build binary wheel distribution (contains pre-compiled .so)
- script: |
python -m pip install --upgrade pip wheel setuptools
python setup.py bdist_wheel
displayName: 'Build wheel package'

Expand Down
3 changes: 1 addition & 2 deletions OneBranchPipelines/stages/build-odbc-all-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ stages:

- powershell: |
$ErrorActionPreference = "Stop"
python -m pip install --upgrade pip
python -m pip install setuptools wheel build twine
python -m pip install --require-hashes -r eng/requirements-build-odbc.txt
displayName: 'Install build tooling'

# Build every target wheel by driving setup_odbc.py's ODBC_TARGET_* overrides.
Expand Down
6 changes: 1 addition & 5 deletions OneBranchPipelines/stages/build-windows-single-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ stages:
- powershell: |
$ErrorActionPreference = "Stop"
Write-Host "Installing Python dependencies..."
python -m pip install --upgrade pip
python -m pip install setuptools wheel pybind11 pytest psutil pyodbc
python -m pip install --require-hashes -r eng/requirements-build-windows.txt
Write-Host "Dependencies installed successfully"
displayName: 'Install Python dependencies'

Expand Down Expand Up @@ -394,7 +393,6 @@ stages:
# Build Python wheel package from source distribution
# ARCHITECTURE environment variable controls target platform tagging
- script: |
python -m pip install --upgrade pip wheel setuptools
set ARCHITECTURE=$(targetArch)
python setup.py bdist_wheel
displayName: 'Build wheel package'
Expand All @@ -410,8 +408,6 @@ stages:
- pwsh: |
$ErrorActionPreference = 'Stop'

python -m pip install --upgrade wheel | Out-Null

$verifyRoot = Join-Path "$(Agent.TempDirectory)" "wheel-sign-verify"
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $verifyRoot
New-Item -ItemType Directory -Force -Path $verifyRoot | Out-Null
Expand Down
6 changes: 6 additions & 0 deletions eng/requirements-build-linux.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Dependencies required to build and run pytest against Linux release wheels.
pip
pybind11
pytest
setuptools
wheel
Loading
Loading