Skip to content

Commit bf74e2c

Browse files
zhmiaoCopilot
andcommitted
ci(release): add Windows x86_64 GPU wheel build + smoke + publish wiring
Before this change, the GPU wheel only shipped Linux x86_64 (manylinux_2_28). End users on Windows running `pip install sparrow-engine-gpu` got 'No matching distribution found' because PyPI had no Windows platform tag for the GPU wheel. The CPU wheel already had a 3-OS matrix (Linux + macOS arm64 + Windows x86_64). The GPU wheel had Windows scaffolded in Phase C but gated off; Phase F landed Linux-only and Windows was never re-enabled. New jobs: - `build-gpu-windows`: runs-on: windows-latest. No CUDA Toolkit step. cudarc `fallback-dynamic-loading` feature (vendor/cudarc/build.rs:70) activates the dynamic-loading cfg from the feature flag alone — no nvcc / driver probing. nvjpeg-sys ships pre-generated bindings. ort uses `load-dynamic`. So compile-time CUDA is not required. Setup mirrors build-cpu-windows + Linux GPU's uv+maturin pattern: checkout, setup-python 3.11, setup-uv, `uv tool install maturin`, bash-shell run of `SPARROW_ENGINE_FLAVOR=gpu ./build.sh`. The OS branches in build.sh (previous commit) skip auditwheel + the --compatibility linux flag + the Linux-only nvidia-* runtime deps. - `smoke-gpu-windows`: pip-install the wheel + onnxruntime-gpu, import-only test. NOT calling sparrow_engine.init() because the GPU flavor's Device::Auto/Cpu coerce to Cuda(0) (flavor-strict post-MT-4.1-2), and windows-latest has no GPU — init() would fail at CUDA context creation. Import-only still validates pip resolution, cdylib load, PyO3 binding init, and the METADATA Provides-Dist mutex with sparrow-engine. Publish wiring: - publish-pypi-gpu (line ~414) `needs:` now [build-gpu-linux, build-gpu-windows, smoke-gpu-windows]. The existing download-artifact step (pattern: wheel-gpu-*) auto-collects the new wheel-gpu-windows artifact via merge-multiple, no collector change needed. - publish-testpypi-gpu (line ~457) same wiring. The maturin install step uses `uv tool dir --bin` (Linux: ~/.local/bin, Windows: %USERPROFILE%\.local\bin in recent uv) rather than hard-coded paths; actionlint shellcheck pass had flagged the hard-coded $USERPROFILE escape sequence as fragile. Verification: - yaml.safe_load() on the workflow: parses OK (16 jobs). - actionlint: zero new findings in the build-gpu-windows / smoke-gpu-windows / publish-*-gpu line ranges. 12 pre-existing info-level findings on unrelated jobs (use-find-not-ls in other steps) are unchanged. Manual verification still pending: - workflow_dispatch with target=build-only after this lands, to confirm the windows-latest runner completes the build and the artifact uploads. - workflow_dispatch with target=testpypi to publish to TestPyPI. - pip install -i https://test.pypi.org/simple/ sparrow-engine-gpu on a Windows test box. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent eb79f72 commit bf74e2c

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,95 @@ jobs:
316316
path: sparrow-engine/target/wheels/sparrow_engine_gpu-*.whl
317317
if-no-files-found: error
318318

319+
build-gpu-windows:
320+
name: Build GPU wheel (Windows x86_64)
321+
runs-on: windows-latest
322+
# No CUDA Toolkit on the runner. cudarc's `fallback-dynamic-loading`
323+
# feature (vendor/cudarc/build.rs:70-78) activates the `dynamic-loading`
324+
# cfg from the feature flag alone, with no nvcc / driver probing.
325+
# nvjpeg-sys (vendor/nvjpeg-sys/build.rs) is a 2-line stub that ships
326+
# pre-generated bindings — no bindgen at build time. `ort` uses
327+
# `load-dynamic`, so libonnxruntime is dlopen'd at runtime. None of
328+
# these crates needs the CUDA SDK at compile time.
329+
steps:
330+
- uses: actions/checkout@v4
331+
332+
- uses: actions/setup-python@v5
333+
with:
334+
python-version: '3.11'
335+
336+
- uses: astral-sh/setup-uv@v3
337+
338+
- name: Install maturin
339+
shell: bash
340+
run: |
341+
uv tool install maturin
342+
# uv tool dir --bin prints the platform-correct bin directory
343+
# (~/.local/bin on Linux, %USERPROFILE%\.local\bin on Windows
344+
# in recent uv versions) — avoids hard-coding either path.
345+
uv tool dir --bin >> "$GITHUB_PATH"
346+
347+
- name: Build GPU wheel via build.sh
348+
shell: bash
349+
run: |
350+
# Git Bash on windows-latest reports OSTYPE=msys; build.sh's
351+
# IS_WINDOWS detection catches that and skips the Linux-only
352+
# `--compatibility linux` flag and `auditwheel repair` step.
353+
cd sparrow-engine/sparrow-engine-python
354+
SPARROW_ENGINE_FLAVOR=gpu ./build.sh
355+
356+
- name: Inspect built wheel
357+
shell: bash
358+
run: |
359+
ls -la sparrow-engine/target/wheels/
360+
for w in sparrow-engine/target/wheels/sparrow_engine_gpu-*.whl; do
361+
echo "Wheel: $w"
362+
case "$w" in
363+
*cp311-abi3*win_amd64*) echo " OK: abi3 + win_amd64";;
364+
*) echo " FAIL: expected cp311-abi3-win_amd64 tag"; exit 1;;
365+
esac
366+
done
367+
368+
- uses: actions/upload-artifact@v4
369+
with:
370+
name: wheel-gpu-windows
371+
path: sparrow-engine/target/wheels/sparrow_engine_gpu-*.whl
372+
if-no-files-found: error
373+
374+
smoke-gpu-windows:
375+
name: Smoke test GPU Windows wheel (Python ${{ matrix.python }})
376+
needs: build-gpu-windows
377+
runs-on: windows-latest
378+
strategy:
379+
fail-fast: false
380+
matrix:
381+
python: ['3.11', '3.12', '3.13']
382+
steps:
383+
- uses: actions/download-artifact@v4
384+
with:
385+
name: wheel-gpu-windows
386+
path: dist
387+
388+
- uses: actions/setup-python@v5
389+
with:
390+
python-version: ${{ matrix.python }}
391+
392+
- name: Install wheel + onnxruntime-gpu, run import
393+
shell: bash
394+
run: |
395+
python -m pip install --upgrade pip
396+
# PEP 508 markers in METADATA skip nvidia-cudnn-cu12 / cublas /
397+
# curand / cufft on sys_platform != 'linux'; onnxruntime-gpu has
398+
# native Windows wheels and resolves normally.
399+
python -m pip install dist/sparrow_engine_gpu-*.whl
400+
# GitHub-hosted windows-latest has no NVIDIA GPU, so we cannot
401+
# call sparrow_engine.init() — Device::Auto/Cpu coerce to
402+
# Cuda(0) on the GPU flavor (flavor-strict post-MT-4.1-2),
403+
# which would fail at CUDA context creation. Import-only test
404+
# still validates: pip resolution, cdylib load, PyO3 binding,
405+
# METADATA Provides-Dist mutex with sparrow-engine.
406+
python -c "import sparrow_engine; print('Smoke OK on', __import__('sys').version, '— version:', sparrow_engine.__version__)"
407+
319408
# -------- TestPyPI publish (workflow_dispatch) --------
320409

321410
publish-testpypi-cpu:
@@ -411,6 +500,8 @@ jobs:
411500
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-')
412501
needs:
413502
- build-gpu-linux
503+
- build-gpu-windows
504+
- smoke-gpu-windows
414505
runs-on: ubuntu-latest
415506
environment:
416507
name: pypi-gpu
@@ -456,6 +547,8 @@ jobs:
456547
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
457548
needs:
458549
- build-gpu-linux
550+
- build-gpu-windows
551+
- smoke-gpu-windows
459552
runs-on: ubuntu-latest
460553
environment:
461554
name: testpypi-gpu

0 commit comments

Comments
 (0)