This directory contains GitHub Actions workflows for automated testing and CI/CD.
Runs on every push and pull request to main branches. Includes:
-
unit-tests: Cross-platform unit tests
- Runs on Ubuntu and Windows
- Python 3.11 and 3.12
- Installs
.[test]only, so no CUDA toolchain is pulled in - Excludes slow tests and tests requiring external data
- Generates coverage reports
-
integration-tests: Integration tests with external data
- Runs on Ubuntu only, for pull requests
- Downloads and caches test data
- Tests data processing pipelines
-
gpu-tests: Tests on self-hosted GPU runners
- DISABLED BY DEFAULT - Only runs when:
- Manually triggered via workflow_dispatch, OR
- PR has the
run-gpu-testslabel
- Requires self-hosted runner with
[self-hosted, Windows, X64, gpu]labels - Uses PyTorch with CUDA 13.0 support
- Timeout: 30 minutes
- DISABLED BY DEFAULT - Only runs when:
-
code-quality: Static code analysis
- Ruff formatting and linting checks
- mypy type checking
- Ruff and mypy checks fail the build if issues are found
Manual trigger only (workflow_dispatch); the nightly schedule lives in
nightly-health.yml. Includes:
- test-slow-gpu: Slow tests requiring GPU
- Tests marked with
slowmarker - Extended timeout: 60 minutes
- Uses self-hosted GPU runners
- Will wait indefinitely if no runner is available
- Tests marked with
Two-job workflow for building and deploying Sphinx documentation:
Job 1: build-docs (runs on all events - PRs and pushes)
- Installs documentation dependencies
- Builds HTML documentation with Sphinx
- Checks for warnings
- Uploads documentation artifacts (retained for 7 days)
Job 2: deploy (runs only on push to main)
- Downloads built documentation
- Deploys to GitHub Pages using GitHub's official deployment action
- Uses
github-pagesenvironment with protection rules - No gh-pages branch needed (modern deployment workflow)
This separation ensures PRs can build and validate docs without triggering environment protection rules.
The deploy job also fetches status.json from the orphan nightly-status
branch (via the GitHub contents API) and copies it into the Pages output, so the
nightly-health badge in the top-level README.md resolves.
Runs at 07:00 UTC daily, or on manual trigger with a reason input. On the
self-hosted Windows GPU runner it installs
.[test,docs,cuda13,dev,physicsnemo] and runs the entire suite with
--run-all, which enables every opt-in bucket. The run itself is
continue-on-error, so a failing test records a red status rather than
failing the workflow.
A second build-dashboard job turns the JUnit XML and coverage JSON into an
HTML dashboard via .github/scripts/build_dashboard.py, then force-pushes
status.json to the orphan nightly-status branch for docs.yml to pick up.
Builds the wheel and source distribution, validates them with Twine, and
publishes to TestPyPI or PyPI using trusted publishing. It runs manually via
workflow_dispatch or automatically when a GitHub release is published.
The workflows use multiple caching layers to speed up builds:
-
Python package cache via
setup-pythonaction- Caches pip packages based on
pyproject.tomlhash
- Caches pip packages based on
-
Additional pip cache via
actions/cache- Caches
~/.cache/pipdirectory - Separate keys for CPU, GPU, and integration tests
- Hierarchical restore keys for fallback
- Caches
GPU tests do NOT run automatically to prevent jobs from waiting indefinitely in the queue when no runner is available.
To run GPU tests, you must either:
- Manually trigger the workflow: Go to Actions > CI > Run workflow
- Add the
run-gpu-testslabel to your pull request
GPU tests require self-hosted runners with:
- Windows OS
- NVIDIA GPU with CUDA 13.0 support
- Runner labels:
[self-hosted, Windows, X64, gpu]
Why are GPU tests disabled by default?
- GitHub Actions jobs wait indefinitely for a self-hosted runner if none are available
- The
timeout-minutessetting only applies AFTER a runner picks up the job - This can block CI pipelines and create confusion when runners are offline
-
Install GitHub Actions Runner:
# Download and configure runner from GitHub repository Settings > Actions > Runners -
Install NVIDIA Drivers and CUDA:
# Install or update the NVIDIA display driver and CUDA Toolkit 13.0 # using NVIDIA's Windows installers. nvidia-smi nvcc --version
Verify both commands are available in the runner service environment before starting jobs.
-
Configure Runner Labels:
- Add labels:
self-hosted,Windows,X64,gpu - Verify GPU is accessible:
nvidia-smi
- Add labels:
-
Start the Runner:
.\run.cmd
Option 1: Manual Workflow Trigger
- Go to your repository on GitHub
- Click "Actions" tab
- Select "CI" workflow from the left sidebar
- Click "Run workflow" button
- Select branch and click "Run workflow"
Option 2: Add Label to PR
- Open your pull request
- Add the
run-gpu-testslabel - The CI workflow will automatically include GPU tests
Option 3: Run Locally
# Install with CUDA + PhysicsNeMo (matches the self-hosted GPU runner).
# Requires Python >= 3.11 (nvidia-physicsnemo does not support 3.10).
uv pip install -e ".[test,cuda13,physicsnemo]"
# Run GPU tests
pytest tests/ -v --run-gpu
pytest tests/ -v --run-all # Match CI: enable every --run-* bucketGitHub-hosted runners do not have GPU support. All GPU tests require self-hosted runners with NVIDIA GPUs.
Test dependencies are installed from pyproject.toml:
pip install -e ".[test]"This installs:
- pytest >= 7.0.0
- pytest-cov >= 4.0.0
- pytest-xdist >= 3.0.0
- pytest-timeout >= 2.0.0
- coverage[toml] >= 7.0.0
Tests should be marked appropriately:
import pytest
@pytest.mark.unit
def test_simple_function():
"""Fast unit test"""
pass
@pytest.mark.integration
def test_full_pipeline():
"""Integration test"""
pass
@pytest.mark.slow
def test_long_running():
"""Long-running test"""
pass
@pytest.mark.requires_gpu
def test_gpu_function():
"""Test requiring GPU"""
if not torch.cuda.is_available():
pytest.skip("GPU not available")
pass# Install dependencies
pip install -e ".[test]"
# Run unit tests
pytest tests/ -m "unit and not requires_gpu"
# Run with coverage
pytest tests/ -m "unit and not requires_gpu" --cov=physiotwin4d# Install with CUDA + PhysicsNeMo (matches the self-hosted GPU runner).
# Requires Python >= 3.11 (nvidia-physicsnemo does not support 3.10).
uv pip install -e ".[test,cuda13,physicsnemo]"
# Run GPU tests
pytest tests/ --run-gpu
# Enable every --run-* bucket at once (slow, GPU, simpleware,
# physicsnemo, tutorials)
pytest tests/ --run-allCoverage reports are:
- Uploaded to Codecov (if configured)
- Stored as artifacts for 7 days
- Available as HTML reports in the
htmlcov/directory
GPU tests are disabled by default. If you want to run them:
- Check if GPU tests should run: They only run on manual trigger or with
run-gpu-testslabel - Verify self-hosted runner is online: Settings > Actions > Runners
- Check runner labels: Runner must have
self-hosted,Windows,X64, andgpulabels - Verify GPU accessibility: Run
nvidia-smion the runner machine - Check workflow logs: Look for "Waiting for a runner" or "runner assignment" messages
If GPU tests are stuck "Waiting for a runner":
- The runner is offline or not properly configured
- Cancel the workflow run (GPU tests won't hold up other jobs due to
continue-on-error: true)
If builds are slow:
- Check cache hit/miss in workflow logs
- Verify
pyproject.tomlhasn't changed unexpectedly - Try clearing caches: Settings > Actions > Caches
For test failures:
- Check individual test logs in the workflow run
- Run tests locally to reproduce
- Use
pytest -v --tb=longfor detailed error traces - Check if tests are marked correctly (unit/integration/slow/requires_gpu)