Skip to content

Commit fe55173

Browse files
rparolinclaude
andcommitted
ci: validate pixi run test source-build (PR smoke + nightly GPU)
Main CI tests prebuilt wheels and never exercises the pixi source build, so that developer path rots silently on CUDA-pin / generated-source / conda-forge / cython-build drift (NVIDIA#2182, NVIDIA#2183). Add a workflow that runs the pixi source build: - build-smoke (PRs touching the at-risk files): CPU-only. Source-builds bindings + core, imports them, builds the cython test extensions and checks placement. Catches the compile / ABI / .so-placement regressions without a GPU. - full-test (nightly + manual): GPU runner, full `pixi run test`. Shared pixi install factored into a composite action with an explicit, asserted version pin. Relates to NVIDIA#2183 (validate the source-build path over time); the regressions this guards against are NVIDIA#2182, fixed by NVIDIA#2180. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 627163e commit fe55173

2 files changed

Lines changed: 170 additions & 0 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: "Setup pixi"
6+
description: "Install a pinned pixi onto PATH and fail loudly if the version drifts."
7+
inputs:
8+
pixi-version:
9+
description: "Pinned pixi version (e.g. v0.66.0)."
10+
required: true
11+
runs:
12+
using: "composite"
13+
steps:
14+
- name: Install pixi
15+
shell: bash
16+
env:
17+
# The official installer honors PIXI_VERSION; pass it explicitly so the
18+
# pin is visible at the call site, not implicit via a workflow-level env.
19+
PIXI_VERSION: ${{ inputs.pixi-version }}
20+
run: |
21+
curl -fsSL https://pixi.sh/install.sh | bash
22+
echo "${HOME}/.pixi/bin" >> "${GITHUB_PATH}"
23+
24+
- name: Verify pinned pixi version
25+
shell: bash
26+
env:
27+
PIXI_VERSION: ${{ inputs.pixi-version }}
28+
run: |
29+
export PATH="${HOME}/.pixi/bin:${PATH}"
30+
expected="${PIXI_VERSION#v}"
31+
got="$(pixi --version | awk '{print $2}')"
32+
if [ "${got}" != "${expected}" ]; then
33+
echo "::error::pixi installed ${got} but the pin requested ${expected} (installer did not honor PIXI_VERSION)"
34+
exit 1
35+
fi
36+
echo "pixi ${got} (pinned)"
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Validates the `pixi run test` developer workflow (source build + Cython
6+
# codegen + test suite via the pixi-managed environment).
7+
#
8+
# WHY THIS EXISTS: the main CI (ci.yml / test-wheel-*.yml) tests prebuilt
9+
# *wheels*; it never exercises the pixi *source build*. That path rots silently
10+
# whenever the CUDA pin, generated bindings, conda-forge packages, or the
11+
# cython-test build mechanics drift (see #2182, #2183). This workflow is the
12+
# only thing that runs the pixi source build end to end.
13+
#
14+
# Two tiers, to spend GPU minutes deliberately (GPUs are scarce):
15+
# - build-smoke (PRs): CPU-only. Source-builds bindings + core, imports them,
16+
# builds the cython test extensions and checks placement. Catches the
17+
# compile / ABI / .so-placement regressions WITHOUT a GPU.
18+
# - full-test (nightly + manual): GPU runner, full `pixi run test`.
19+
20+
name: "CI: pixi run test (source build)"
21+
22+
concurrency:
23+
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
24+
cancel-in-progress: true
25+
26+
on:
27+
schedule:
28+
# 2:37 AM UTC daily — offset from ci-nightly.yml (2:17) to avoid contending
29+
# for the same scheduled-workflow window.
30+
- cron: "37 2 * * *"
31+
pull_request:
32+
# Only the files that can break the source-build path (see #2182/#2183).
33+
paths:
34+
- "**/pixi.toml"
35+
- "**/pixi.lock"
36+
- "cuda_bindings/build_hooks.py"
37+
- "cuda_core/build_hooks.py"
38+
- "cuda_bindings/cuda/bindings/**" # generated bindings sources
39+
- "cuda_bindings/tests/cython/**"
40+
- "cuda_core/tests/cython/**"
41+
- "ci/versions.yml"
42+
- ".github/workflows/ci-pixi-source-test.yml"
43+
- ".github/actions/setup-pixi/**"
44+
workflow_dispatch:
45+
inputs:
46+
cuda-env:
47+
description: "pixi environment to test (cu13 or cu12)."
48+
type: string
49+
default: "cu13"
50+
51+
defaults:
52+
run:
53+
shell: bash --noprofile --norc -xeuo pipefail {0}
54+
55+
env:
56+
PIXI_VERSION: "v0.66.0" # keep in sync with the version developers run locally
57+
58+
jobs:
59+
# ── PR guard: CPU-only build + import + placement smoke ──
60+
build-smoke:
61+
name: "build smoke (cu13, linux-64, CPU)"
62+
if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }}
63+
runs-on: ubuntu-latest
64+
timeout-minutes: 45
65+
steps:
66+
- name: Checkout ${{ github.event.repository.name }}
67+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
68+
69+
- name: Setup pixi
70+
uses: ./.github/actions/setup-pixi
71+
with:
72+
pixi-version: ${{ env.PIXI_VERSION }}
73+
74+
- name: Source-build + import + cython-placement smoke
75+
env:
76+
CUDA_ENV: ${{ inputs.cuda-env || 'cu13' }}
77+
run: |
78+
# pathfinder: pure-Python, no GPU.
79+
pixi run -e "${CUDA_ENV}" test-pathfinder
80+
81+
# bindings + core: force the source build (catches nvrtc/driver
82+
# compile errors like #2182) and import them (catches ABI mismatches).
83+
pixi run --manifest-path cuda_bindings -e "${CUDA_ENV}" \
84+
python -c "import cuda.bindings.driver, cuda.bindings.nvrtc, cuda.bindings.runtime; print('bindings import OK')"
85+
pixi run --manifest-path cuda_core -e "${CUDA_ENV}" \
86+
python -c "import cuda.core; print('core import OK')"
87+
88+
# cython test extensions: build them and confirm each .so landed next
89+
# to its .pyx in tests/cython (catches the placement regression #2180).
90+
pixi run --manifest-path cuda_bindings -e "${CUDA_ENV}" build-cython-tests
91+
pixi run --manifest-path cuda_core -e "${CUDA_ENV}" build-cython-tests
92+
for d in cuda_bindings/tests/cython cuda_core/tests/cython; do
93+
if ! compgen -G "${d}/*.cpython-*.so" > /dev/null; then
94+
echo "::error::no compiled cython test .so in ${d} (placement regression)"
95+
exit 1
96+
fi
97+
done
98+
echo "cython test extensions placed correctly"
99+
100+
# ── Nightly: full `pixi run test` on a GPU runner ──
101+
full-test:
102+
name: "pixi run test (${{ inputs.cuda-env || 'cu13' }}, linux-64, GPU)"
103+
if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository_owner == 'nvidia' }}
104+
runs-on: "linux-amd64-gpu-l4-latest-1" # same label scheme as test-wheel-linux.yml
105+
timeout-minutes: 90
106+
container:
107+
options: -u root --security-opt seccomp=unconfined --shm-size 16g
108+
image: ubuntu:22.04
109+
steps:
110+
- name: Ensure GPU is working
111+
run: nvidia-smi
112+
113+
- name: Install system packages
114+
run: |
115+
apt-get update
116+
# curl/ca-certificates for the pixi installer; git for checkout;
117+
# libgl1/libegl1 for pyglet (pulled in by the test env).
118+
apt-get install -y --no-install-recommends \
119+
curl ca-certificates git libgl1 libegl1
120+
121+
- name: Checkout ${{ github.event.repository.name }}
122+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
123+
124+
- name: Setup proxy cache
125+
uses: nv-gha-runners/setup-proxy-cache@main
126+
continue-on-error: true
127+
128+
- name: Setup pixi
129+
uses: ./.github/actions/setup-pixi
130+
with:
131+
pixi-version: ${{ env.PIXI_VERSION }}
132+
133+
- name: pixi run test
134+
run: pixi run -e "${{ inputs.cuda-env || 'cu13' }}" test

0 commit comments

Comments
 (0)