ENH: Specify Python 3.11 for nightly health / remote runner #310
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| # Comprehensive CI workflow combining unit tests, integration tests, GPU tests, and code quality checks | |
| # | |
| # Test organization: | |
| # - Unit tests: Run on all PRs and pushes, cross-platform (Ubuntu + Windows), multiple Python versions | |
| # - Integration tests: Run with external data, Ubuntu only | |
| # - GPU tests: Self-hosted runners with CUDA support | |
| # - Code quality: Linting and formatting checks | |
| # | |
| # Test markers (gated by opt-in pytest flags; default = skip): | |
| # - slow -> --run-slow | |
| # - requires_gpu -> --run-gpu | |
| # - requires_simpleware -> --run-simpleware (also implies GPU) | |
| # - requires_physicsnemo -> --run-physicsnemo (needs the [physicsnemo] extra) | |
| # - experiment -> --run-experiments | |
| # - tutorial -> --run-tutorials | |
| # --run-all enables every bucket above at once. | |
| # Tests that need external data download it automatically via fixtures. | |
| on: | |
| push: | |
| branches: [ main, master, develop ] | |
| pull_request: | |
| branches: [ main, master, develop ] | |
| workflow_dispatch: | |
| jobs: | |
| # ============================================================================ | |
| # Cross-Platform Unit Tests (Ubuntu + Windows) | |
| # ============================================================================ | |
| unit-tests: | |
| name: Unit Tests (${{ matrix.os }}, Python ${{ matrix.python-version }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| python-version: ['3.11', '3.12'] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| lfs: true | |
| - name: Free up disk space on Ubuntu | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| echo "Disk space before cleanup:" | |
| df -h | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /usr/local/lib/android | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /opt/hostedtoolcache/CodeQL | |
| sudo docker image prune --all --force | |
| echo "Disk space after cleanup:" | |
| df -h | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| cache-dependency-path: pyproject.toml | |
| - name: Cache pip packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-${{ matrix.python-version }}- | |
| ${{ runner.os }}-pip- | |
| - name: Install system dependencies (Ubuntu) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libgl1 \ | |
| libglib2.0-0 \ | |
| libgomp1 \ | |
| libsm6 \ | |
| libxrender1 \ | |
| libxext6 \ | |
| libxrandr2 \ | |
| libxi6 \ | |
| xvfb \ | |
| libosmesa6 | |
| sudo apt-get clean | |
| sudo rm -rf /var/lib/apt/lists/* | |
| - name: Upgrade pip and build tools | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel | |
| - name: Install package with test dependencies | |
| run: | | |
| pip install -e ".[test]" | |
| - name: Clear pip cache | |
| run: | | |
| pip cache purge || true | |
| - name: List installed packages | |
| run: | | |
| pip list | |
| - name: Run unit tests (fast, no GPU/slow/experiment) - Ubuntu | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| xvfb-run -a --server-args="-screen 0 1024x768x24" \ | |
| pytest tests/ -v --cov=physiotwin4d --cov-report=xml --cov-report=term --cov-report=html | |
| - name: Run unit tests (fast, no GPU/slow/experiment) - Windows | |
| if: matrix.os == 'windows-latest' | |
| run: | | |
| pytest tests/ -v --cov=physiotwin4d --cov-report=xml --cov-report=term --cov-report=html | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| file: ./coverage.xml | |
| flags: unittests | |
| name: codecov-unit-${{ matrix.os }}-py${{ matrix.python-version }} | |
| fail_ci_if_error: false | |
| - name: Upload coverage artifacts | |
| uses: actions/upload-artifact@v6 | |
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' | |
| with: | |
| name: coverage-report-unit-tests | |
| path: htmlcov/ | |
| retention-days: 7 | |
| # ============================================================================ | |
| # Integration Tests with External Data (Ubuntu only, on PRs) | |
| # ============================================================================ | |
| integration-tests: | |
| name: Integration Tests (with data) | |
| runs-on: ubuntu-latest | |
| needs: unit-tests | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| lfs: true | |
| - name: Free up disk space | |
| run: | | |
| echo "Disk space before cleanup:" | |
| df -h | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /usr/local/lib/android | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /opt/hostedtoolcache/CodeQL | |
| sudo docker image prune --all --force | |
| echo "Disk space after cleanup:" | |
| df -h | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| cache-dependency-path: pyproject.toml | |
| - name: Cache pip packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-integration-${{ hashFiles('pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-integration- | |
| ${{ runner.os }}-pip- | |
| - name: Cache test data | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| tests/data/ | |
| tests/results/ | |
| key: test-data-${{ hashFiles('tests/test_*.py') }}-v2 | |
| restore-keys: | | |
| test-data- | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libgl1 \ | |
| libglib2.0-0 \ | |
| libgomp1 \ | |
| libsm6 \ | |
| libxrender1 \ | |
| libxext6 \ | |
| libxrandr2 \ | |
| libxi6 \ | |
| xvfb \ | |
| libosmesa6 | |
| sudo apt-get clean | |
| sudo rm -rf /var/lib/apt/lists/* | |
| - name: Upgrade pip and build tools | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel | |
| - name: Install package with test dependencies | |
| run: | | |
| pip install -e ".[test]" | |
| - name: Clear pip cache | |
| run: | | |
| pip cache purge || true | |
| - name: Run data download tests | |
| run: | | |
| pytest tests/test_download_heart_data.py -v --cov=physiotwin4d --cov-report=xml | |
| continue-on-error: true | |
| - name: Run data conversion tests | |
| run: | | |
| pytest tests/test_convert_image_4d_to_3d.py -v --cov=physiotwin4d --cov-append --cov-report=xml | |
| continue-on-error: true | |
| - name: Run contour tools tests | |
| run: | | |
| pytest tests/test_contour_tools.py -v --cov=physiotwin4d --cov-append --cov-report=xml | |
| continue-on-error: true | |
| - name: Run USD conversion tests | |
| run: | | |
| xvfb-run -a --server-args="-screen 0 1024x768x24" \ | |
| pytest tests/test_convert_vtk_to_usd_polymesh.py -v --cov=physiotwin4d --cov-append --cov-report=xml | |
| continue-on-error: true | |
| - name: Run USD utility tests | |
| run: | | |
| xvfb-run -a --server-args="-screen 0 1024x768x24" \ | |
| pytest tests/test_usd_merge.py tests/test_usd_time_preservation.py -v --cov=physiotwin4d --cov-append --cov-report=xml | |
| continue-on-error: true | |
| - name: Run all integration tests | |
| run: | | |
| xvfb-run -a --server-args="-screen 0 1024x768x24" \ | |
| pytest tests/ -v | |
| continue-on-error: true | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| file: ./coverage.xml | |
| flags: integration-tests | |
| name: codecov-integration | |
| fail_ci_if_error: false | |
| # ============================================================================ | |
| # GPU Tests (Self-hosted runners with CUDA) | |
| # ============================================================================ | |
| # NOTE: GPU tests are DISABLED for automatic runs because they wait indefinitely | |
| # if no self-hosted runner is available. Enable manually via workflow_dispatch | |
| # or by setting the 'run-gpu-tests' label on the PR. | |
| gpu-tests: | |
| name: GPU Tests | |
| runs-on: [self-hosted, Windows, X64, gpu] | |
| needs: unit-tests | |
| timeout-minutes: 30 | |
| # Only run GPU tests on manual trigger or if 'run-gpu-tests' label is present | |
| if: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'run-gpu-tests') | |
| continue-on-error: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| lfs: true | |
| - name: Create venv in RUNNER_TEMP | |
| # Python 3.11 is required because the [physicsnemo] extra pulls in | |
| # nvidia-physicsnemo, which requires Python >= 3.11. | |
| run: | | |
| & "C:\Program Files\Python311\python.exe" -m venv "$env:RUNNER_TEMP\physiotwin4d-venv" | |
| echo "$env:RUNNER_TEMP\physiotwin4d-venv\Scripts" >> $env:GITHUB_PATH | |
| - name: Check GPU availability | |
| run: nvidia-smi | |
| - name: Cache uv packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~\AppData\Local\uv\cache | |
| key: ${{ runner.os }}-uv-${{ hashFiles('pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-uv- | |
| - name: Upgrade pip and build tools | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel uv | |
| - name: Install package with test dependencies | |
| # uv respects [tool.uv.sources], routing torch to pytorch-cu130 for cuda13. | |
| # The [physicsnemo] extra brings in nvidia-physicsnemo so Tutorial 9 and | |
| # requires_physicsnemo-marked tests can run on this GPU runner. | |
| # Invoke via python -m uv so uv targets the active venv interpreter. | |
| run: | | |
| python -m uv pip install -e ".[test,cuda13,physicsnemo]" | |
| - name: Assert CUDA is accessible | |
| run: | | |
| python -c " | |
| import sys, torch, cupy | |
| print(f'PyTorch {torch.__version__} | CUDA toolkit {torch.version.cuda} | CuPy {cupy.__version__}') | |
| if not torch.cuda.is_available(): | |
| print('ERROR: torch.cuda.is_available() returned False', file=sys.stderr) | |
| sys.exit(1) | |
| n = torch.cuda.device_count() | |
| if n == 0: | |
| print('ERROR: torch.cuda.device_count() == 0', file=sys.stderr) | |
| sys.exit(1) | |
| cn = cupy.cuda.runtime.getDeviceCount() | |
| if cn == 0: | |
| print('ERROR: cupy.cuda.runtime.getDeviceCount() == 0', file=sys.stderr) | |
| sys.exit(1) | |
| print(f'OK: {n} GPU(s) visible to PyTorch and CuPy') | |
| " | |
| - name: List installed packages | |
| run: | | |
| pip list | |
| - name: Run GPU tests | |
| # External self-hosted GPU runner: enable every opt-in bucket via --run-all. | |
| # Tests whose host requirements (e.g. a licensed Simpleware install) | |
| # aren't met on the runner will runtime-skip cleanly via their | |
| # internal availability guards. | |
| run: | | |
| pytest tests/ -v --run-all --cov=physiotwin4d --cov-report=xml --cov-report=term --cov-report=html | |
| env: | |
| CUDA_VISIBLE_DEVICES: 0 | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| file: ./coverage.xml | |
| flags: gpu-tests | |
| name: codecov-gpu | |
| fail_ci_if_error: false | |
| - name: Upload coverage artifacts | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: coverage-report-gpu | |
| path: htmlcov/ | |
| retention-days: 7 | |
| # ============================================================================ | |
| # Code Quality Checks | |
| # ============================================================================ | |
| code-quality: | |
| name: Code Quality Checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| lfs: true | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.11" | |
| cache: 'pip' | |
| - name: Install dev dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install ruff mypy | |
| pip install -e ".[dev]" | |
| - name: Check formatting with Ruff | |
| run: | | |
| ruff format --check . | |
| continue-on-error: false | |
| - name: Lint with Ruff | |
| run: | | |
| ruff check . | |
| continue-on-error: false | |
| - name: Type check with mypy | |
| run: | | |
| mypy src/ | |
| continue-on-error: false | |
| # ============================================================================== | |
| # Notes on Excluded Tests | |
| # ============================================================================== | |
| # | |
| # The following tests are excluded from CI and should be run locally: | |
| # | |
| # Slow/GPU-intensive tests: | |
| # - tests/test_register_images_ANTS.py (slow, computationally intensive) | |
| # - tests/test_register_images_ICON.py (requires CUDA for ICON) | |
| # - tests/test_transform_tools.py (depends on slow registration tests) | |
| # - tests/test_segment_chest_total_segmentator.py (requires CUDA for TotalSegmentator) | |
| # | |
| # Experiment tests (EXTREMELY SLOW - hours to complete): | |
| # - tests/test_experiments.py (runs all notebooks in experiments/ subdirectories) | |
| # These tests are NEVER run in CI/CD and must be run manually | |
| # They execute end-to-end workflows that may take multiple hours | |
| # | |
| # To run locally: | |
| # pytest tests/ -v --run-slow # Run all slow tests | |
| # pytest tests/ -v --run-gpu --run-slow # GPU + slow (typical local dev profile) | |
| # pytest tests/ -v --run-simpleware --run-gpu --run-slow # Full Simpleware coverage | |
| # pytest tests/test_register_images_ANTS.py -v --run-slow | |
| # | |
| # Self-hosted GPU runner enables ALL buckets via --run-all | |
| # (--run-gpu --run-slow --run-simpleware --run-physicsnemo --run-experiments --run-tutorials). | |
| # That runner installs the [physicsnemo] extra in addition to [test,cuda13]. | |
| # | |
| # To run experiment tests (manual only, extremely slow): | |
| # pytest tests/test_experiments.py -v --run-experiments | |
| # pytest tests/test_experiments.py::test_experiment_heart_gated_ct_to_usd -v --run-experiments |