Skip to content
Merged
25 changes: 23 additions & 2 deletions .github/workflows/refresh-build-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,25 +130,43 @@ jobs:
- name: Linux build
artifact: linux
lock: requirements-build-linux.txt
versions: "3.10 3.11 3.12 3.13 3.14"
versions: "3.10 3.11 3.12 3.13 3.14 3.15"
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: Linux test Python 3.15
artifact: linux
lock: requirements-test-linux.txt
versions: "3.15"
platforms: "x86_64-manylinux_2_28 aarch64-manylinux_2_28 x86_64-unknown-linux-musl aarch64-unknown-linux-musl"
allowSourceBuilds: true
- 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"
- name: macOS Python 3.15
artifact: macos
lock: requirements-build-macos.txt
versions: "3.15"
platforms: "x86_64-apple-darwin aarch64-apple-darwin"
allowSourceBuilds: true
# 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: Windows build host Python 3.15
artifact: windows
lock: requirements-build-windows.txt
versions: "3.15"
platforms: "x86_64-pc-windows-msvc"
allowSourceBuilds: true
- name: ODBC
artifact: odbc
lock: requirements-build-odbc.txt
Expand All @@ -168,8 +186,11 @@ jobs:
LOCK_FILE: generated-lock/${{ matrix.lock }}
PLATFORMS: ${{ matrix.platforms }}
PYTHON_VERSIONS: ${{ matrix.versions }}
ALLOW_SOURCE_BUILDS: ${{ matrix.allowSourceBuilds || false }}
run: |
set -euo pipefail
binary_args=(--only-binary :all:)
if [ "$ALLOW_SOURCE_BUILDS" = true ]; then binary_args=(); fi
for platform in $PLATFORMS; do
for version in $PYTHON_VERSIONS; do
echo "Validating $LOCK_FILE for Python $version on $platform"
Expand All @@ -179,7 +200,7 @@ jobs:
--target "$RUNNER_TEMP/lock-validation" \
--python-version "$version" \
--python-platform "$platform" \
--only-binary :all: \
"${binary_args[@]}" \
--require-hashes \
--default-index https://pypi.org/simple \
-r "$LOCK_FILE"
Expand Down
52 changes: 30 additions & 22 deletions OneBranchPipelines/build-release-package-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# OneBranch Release Pipeline for mssql-python
# =========================================================================================
# Builds Python wheels for all supported platforms with SDL compliance:
# - Windows: Python 3.10-3.14 (x64 + ARM64)
# - macOS: Python 3.10-3.14 (Universal2 = x86_64 + ARM64 in single binary)
# - Linux: Python 3.10-3.14 on manylinux/musllinux (x86_64 + ARM64)
# - Windows: Python 3.10-3.15 (x64 + ARM64)
# - macOS: Python 3.10-3.15 (Universal2 = x86_64 + ARM64 in single binary)
# - Linux: Python 3.10-3.15 on manylinux/musllinux (x86_64 + ARM64)
#
# Security Features:
# - ESRP code signing (Windows .pyd files only)
Expand Down Expand Up @@ -82,19 +82,19 @@ parameters:
# PLATFORM CONFIGURATIONS
# =========================
# Each platform uses different matrix strategy:
# - Windows: Explicit per-version stages (9 stages for x64/ARM64 combos)
# - macOS: Explicit per-version stages (5 stages for universal2 builds)
# - Windows: Explicit per-version stages (11 stages for x64/ARM64 combos)
# - macOS: Explicit per-version stages (6 stages for universal2 builds)
# - Linux: Per-distro stages, builds ALL Python versions in loop (4 stages)

# Windows Configuration Matrix
# Each entry creates separate stage: Win_py<pyVer>_<arch>
# pyVer format: '310' = Python 3.10, '314' = Python 3.14
# pyVer format: '310' = Python 3.10, '315' = Python 3.15
# arch: 'x64' (Intel/AMD 64-bit) or 'arm64' (ARM64, cross-compiled on x64)
# Note: ARM64 builds use x64 host with ARM64 python.lib for cross-compilation
- name: windowsConfigs
type: object
default:
# x64 builds (5 versions: 3.10-3.14)
# x64 builds (6 versions: 3.10-3.15)
- pyVer: '310'
arch: 'x64'
- pyVer: '311'
Expand All @@ -105,7 +105,9 @@ parameters:
arch: 'x64'
- pyVer: '314'
arch: 'x64'
# ARM64 builds (4 versions: 3.11-3.14)
- pyVer: '315'
arch: 'x64'
# ARM64 builds (5 versions: 3.11-3.15)
# 3.10 excluded due to limited ARM64 support
- pyVer: '311'
arch: 'arm64'
Expand All @@ -115,23 +117,26 @@ parameters:
arch: 'arm64'
- pyVer: '314'
arch: 'arm64'
- pyVer: '315'
arch: 'arm64'

# macOS Configuration Matrix
# Each entry creates separate stage: MacOS_py<pyVer>
# All builds are Universal2 (x86_64 + ARM64 in single binary)
# pyVer format: '310' = Python 3.10, '314' = Python 3.14
# pyVer format: '310' = Python 3.10, '315' = Python 3.15
- name: macosConfigs
type: object
default:
# 5 versions: 3.10-3.14 (all universal2)
# 6 versions: 3.10-3.15 (all universal2)
- pyVer: '310'
- pyVer: '311'
- pyVer: '312'
- pyVer: '313'
- pyVer: '314'
- pyVer: '315'

# Linux Configuration Matrix
# Each entry creates ONE stage that builds ALL Python versions (3.10-3.14)
# Each entry creates ONE stage that builds ALL Python versions (3.10-3.15)
# tag: 'manylinux' (glibc-based, e.g., Ubuntu/CentOS) or 'musllinux' (musl-based, e.g., Alpine)
# arch: CPU architecture for Docker platform
# platform: Docker platform identifier for multi-arch builds
Expand Down Expand Up @@ -375,17 +380,17 @@ extends:
# =========================
# PIPELINE STAGES
# =========================
# mssql-python: 9 Windows + 5 macOS + 4 Linux + 1 Consolidate = 19 stages
# mssql-python: 11 Windows + 6 macOS + 4 Linux + 1 Consolidate = 22 stages
# mssql-python-odbc: 1 ODBC_BuildAll (single host, all 7 wheels) + 1 ConsolidateOdbc = 2 stages
# Total: 21 stages, always built together. The mssql-python build stages depend on
# Total: 24 stages, always built together. The mssql-python build stages depend on
# ConsolidateOdbc so they install the external ODBC wheel before pytest.
stages:
# =========================
# WINDOWS BUILD STAGES
# =========================
# Strategy: Explicit stage per Python version × architecture
# Total: 9 stages (5 x64 + 4 ARM64)
# Python versions: 3.10-3.14 (x64), 3.11-3.14 (ARM64)
# Total: 11 stages (6 x64 + 5 ARM64)
# Python versions: 3.10-3.15 (x64), 3.11-3.15 (ARM64)
# Each stage:
# 1. Installs Python (UsePythonVersion or NuGet for 3.14)
# 2. Downloads ARM64 python.lib if cross-compiling
Expand Down Expand Up @@ -416,7 +421,7 @@ extends:
# MACOS BUILD STAGES
# =========================
# Strategy: Explicit stage per Python version
# Total: 5 stages (3.10-3.14)
# Total: 6 stages (3.10-3.15)
# All builds are Universal2 (x86_64 + ARM64 in single .so binary)
# Each stage:
# 1. Installs Python via UsePythonVersion@0
Expand Down Expand Up @@ -446,20 +451,20 @@ extends:
# =========================
# Strategy: One stage per distribution × architecture
# Total: 4 stages (manylinux_2_28×2 + musllinux×2)
# Each stage builds ALL Python versions (3.10-3.14) in a loop
# Each stage builds ALL Python versions (3.10-3.15) in a loop
# Distributions:
# - manylinux_2_28: glibc 2.28+ (RHEL 8, Ubuntu 20.04+, etc.) — forward-compatible with newer glibc
# - musllinux: musl-based (Alpine Linux)
# Architectures: x86_64 (AMD/Intel), aarch64 (ARM64)
# Each stage:
# 1. Starts PyPA Docker container (manylinux_2_28 or musllinux_1_2)
# 2. Starts SQL Server Docker container
# 3. For each Python version (cp310-cp314):
# 3. For each Python version (cp310-cp315):
# a. Builds .so native extension
# b. Builds wheel
# c. Installs wheel in isolated directory
# d. Runs pytest against SQL Server
# 4. Publishes artifacts (all 5 wheels)
# 4. Publishes artifacts (all 6 wheels)
# 5. Component Governance + AntiMalware scanning
- ${{ each config in parameters.linuxConfigs }}:
- template: /OneBranchPipelines/stages/build-linux-single-stage.yml@self
Expand All @@ -479,7 +484,7 @@ extends:
# CONSOLIDATE STAGE
# =========================
# Purpose: Collect all artifacts from platform builds into single dist/ folder
# Dependencies: All 18 build stages (9 Windows + 5 macOS + 4 Linux)
# Dependencies: All 21 build stages (11 Windows + 6 macOS + 4 Linux)
# Stages run in parallel, Consolidate waits for ALL to complete
# Outputs:
# - dist/wheels/*.whl (all platform wheels)
Expand All @@ -490,22 +495,25 @@ extends:
- stage: Consolidate
displayName: 'Consolidate All Artifacts'
dependsOn:
# Windows dependencies (9 stages)
# Windows dependencies (11 stages)
- Win_py310_x64
- Win_py311_x64
- Win_py312_x64
- Win_py313_x64
- Win_py314_x64
- Win_py315_x64
- Win_py311_arm64
- Win_py312_arm64
- Win_py313_arm64
- Win_py314_arm64
# macOS dependencies (5 stages)
- Win_py315_arm64
# macOS dependencies (6 stages)
- MacOS_py310
- MacOS_py311
- MacOS_py312
- MacOS_py313
- MacOS_py314
- MacOS_py315
# Linux dependencies (4 stages)
- Linux_manylinux_2_28_x86_64
- Linux_manylinux_2_28_aarch64
Expand Down
14 changes: 7 additions & 7 deletions OneBranchPipelines/jobs/consolidate-artifacts-job.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Consolidate Artifacts Job Template
# Downloads artifacts from all platform build stages and consolidates into single dist/ folder
# Works with individual stage artifacts (18 stages total: 9 Windows + 5 macOS + 4 Linux)
# Each Linux stage builds 5 Python versions (3.10-3.14), resulting in 34 total wheels
# Works with individual stage artifacts (21 stages total: 11 Windows + 6 macOS + 4 Linux)
# Each Linux stage builds 6 Python versions (3.10-3.15), resulting in 41 total wheels
parameters:
- name: oneBranchType
type: string
Expand Down Expand Up @@ -75,17 +75,17 @@ jobs:
echo ""
WHEEL_COUNT=$(ls -1 $(ob_outputDirectory)/dist/*.whl 2>/dev/null | wc -l)
echo "Total wheel count: $WHEEL_COUNT"
echo "Expected: 34 wheels (9 Windows + 5 macOS + 20 Linux)"
echo "Expected: 41 wheels (11 Windows + 6 macOS + 24 Linux)"

# Hard-fail on a wheel-count mismatch (symmetric with the odbc consolidate job).
# A mismatch means a build stage silently produced a partial set (e.g. a missing
# Python version) or the build matrix changed without updating this count.
# Update the expected 34 whenever the Windows/macOS/Linux build matrix changes.
if [ "$WHEEL_COUNT" -ne 34 ]; then
echo "ERROR: Expected 34 wheels but found $WHEEL_COUNT" >&2
# Update the expected 41 whenever the Windows/macOS/Linux build matrix changes.
if [ "$WHEEL_COUNT" -ne 41 ]; then
echo "ERROR: Expected 41 wheels but found $WHEEL_COUNT" >&2
exit 1
else
echo "SUCCESS: All 34 wheels consolidated!"
echo "SUCCESS: All 41 wheels consolidated!"
fi
displayName: 'Consolidate wheels from all platforms'

Expand Down
8 changes: 4 additions & 4 deletions OneBranchPipelines/stages/build-linux-single-stage.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Linux Single Configuration Stage Template
# Builds Python wheels for a specific Linux distribution and architecture
# Builds for Python 3.10, 3.11, 3.12, 3.13, 3.14 within single job
# Builds for Python 3.10, 3.11, 3.12, 3.13, 3.14, 3.15 within single job
# Tests each wheel after building with isolated pytest execution
parameters:
# Stage identifier (e.g., 'Linux_manylinux_2_28_x86_64')
Expand Down Expand Up @@ -256,14 +256,14 @@ stages:
ls -1 "$(Build.SourcesDirectory)/odbc_wheels"/*.whl
displayName: 'Flatten ODBC wheels for pip find-links'

# Build wheels for all Python versions (3.10-3.14) and test each one
# Build wheels for all Python versions (3.10-3.15) and test each one
- script: |
set -euo pipefail
if [[ "$(LINUX_TAG)" == "manylinux_2_28" ]]; then SHELL_EXE=bash; else SHELL_EXE=sh; fi
docker exec build-$(LINUX_TAG)-$(ARCH) $SHELL_EXE -lc 'mkdir -p /workspace/dist'

# Loop through all Python versions: build wheel -> test wheel -> repeat
for PYBIN in cp310 cp311 cp312 cp313 cp314; do
for PYBIN in cp310 cp311 cp312 cp313 cp314 cp315; do
echo ""
echo "====================================================="
echo "Building and testing $PYBIN on $(LINUX_TAG)/$(ARCH)"
Expand Down Expand Up @@ -440,7 +440,7 @@ stages:
echo "====================================================="
echo "✓ All Python versions built and tested successfully!"
echo "====================================================="
displayName: 'Build and test wheels for Python 3.10-3.14'
displayName: 'Build and test wheels for Python 3.10-3.15'
env:
DB_PASSWORD: $(DB_PASSWORD)

Expand Down
1 change: 1 addition & 0 deletions OneBranchPipelines/stages/build-windows-single-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ stages:
"3.12" { "3.12.7" }
"3.13" { "3.13.0" }
"3.14" { "3.14.0" }
"3.15" { "3.15.0" }
}

$nugetUrl = "https://www.nuget.org/api/v2/package/pythonarm64/$nugetVersion"
Expand Down
Loading