Red Hat's downstream CI for PyTorch, building and testing on RHEL (Red Hat Enterprise Linux). Integrated with PyTorch's upstream CI via Cross-Repository CI Relay (CRCR).
pytorch/pytorch
│
├─ PR events ──▶ repository_dispatch (via CRCR) ──▶ rhel96-build-test.yml [disabled]
│
├─ nightly branch ──▶ cron schedule ──▶ crcr-nightly.yml [active, CUDA]
│ │
│ ├─ Extracts source main SHA from nightly commit
│ ├─ Builds PyTorch in RHEL 9.6 CUDA container
│ ├─ Runs delta-based test determination
│ └─ Executes categorized tests (cpu, inductor, sgpu, mgpu)
│
└─ nightly SHA ──▶ workflow_dispatch ──▶ crcr-nightly-rocm.yml [manual, ROCm]
│
├─ Builds PyTorch in RHEL 9.6 ROCm container
├─ Runs sanity or critical ROCm tests
└─ HUD/CRCR callbacks currently disabled (PUSH_TO_HUD=false)
| Runner | OS | Accelerator | Status |
|---|---|---|---|
linux.rhel96 |
RHEL 9.6 | CUDA | Active |
linux.rhel96-rocm |
RHEL 9.6 | ROCm | Active (manual validation) |
Runs daily at 08:30 UTC via cron, or manually via workflow_dispatch.
Pipeline: build → determine-tests → cpu-tests → inductor-tests → sgpu-tests → mgpu-tests (sequential)
The workflow can be triggered manually from the Actions tab with two optional inputs:
| Input | Description | Default |
|---|---|---|
sha |
pytorch/pytorch SHA to build against (leave empty for latest nightly) | (empty = latest nightly) |
test_categories |
Which test stages to run after build | all |
Test category options:
| Selection | What runs |
|---|---|
all |
Build + all test stages (same as cron) |
cpu |
Build + determine-tests + CPU tests only |
inductor |
Build + determine-tests + inductor tests only |
sgpu |
Build + determine-tests + single-GPU tests only |
mgpu |
Build + determine-tests + multi-GPU tests only |
build-only |
Build only, skip all tests |
Cron-triggered runs always execute all stages regardless of these inputs. The run title displays the selected category (e.g., [Nightly] RHEL 9.6 @ manual [sgpu]).
- Fetches the two most recent commits from
pytorch/pytorch'snightlybranch - Extracts the source
mainSHA from the nightly release commit message (nightly commits embed the originalmainSHA in parentheses) - Builds PyTorch from source inside a RHEL 9.6 UBI container using
podman build - RHEL subscription credentials are passed via BuildKit
--secretmounts (never appear in image layers ordocker history) - Produces a tagged container image (
rhel9-pytorch-nightly:<sha>) for downstream test jobs - Pushes to Quay.io with a reproducible tag:
Tag components:
quay.io/aipcc/pytorch:rhel9_6_pytorch_nightly_main_git<7char_sha>_cuda13_0rhel9_6(OS),nightly(pipeline),main(PyTorch branch),git<sha>(commit),cuda13_0(CUDA version)
- Computes the diff between the current and previous source SHAs
- Runs
merge_test_results.pyinside the built container (heuristic + structural call graph analysis) - Validates discovered test names against
run_test.py's accepted test list - Excludes CUDA-only tests (e.g.,
test_overrides) fromcpuandinductorcategories - Outputs base64-encoded, categorized test lists (cpu, inductor, sgpu, mgpu)
- Falls back to full test suite (from
test_config.py) if delta produces no results
| Job | Category | GPU Requirement | Per-command timeout | Runs After |
|---|---|---|---|---|
cpu-tests |
CPU-only PyTorch tests | None | 2 hours | determine-tests |
inductor-tests |
TorchInductor + Dynamo + Export | None | 2 hours | cpu-tests |
sgpu-tests |
Single-GPU tests | ≥ 1 GPU | 2 hours | inductor-tests |
mgpu-tests |
Multi-GPU + distributed tests | ≥ 2 GPUs | 12 hours | sgpu-tests |
Each test job:
- Mounts the command list as a file into the container (
-v /tmp/<job>_test_commands.txt:/tmp/test_commands.txt:ro) — avoids shell quoting issues withbash -cargument passing - Writes each command to
/tmp/_run.shand executes viabash /tmp/_run.sh— preserves-kfilter quoting (e.g.,-k "TestA or TestB") that would otherwise be mangled by nestedeval - Uses single-quoted
bash -c '...'for the outer podman shell — eliminates escape gymnastics - Wraps each command with
timeoutto prevent individual hangs from blocking the pipeline (2 hours for cpu/inductor/sgpu, 12 hours for mgpu) - Runs with
CONTINUE_THROUGH_ERROR=True, collecting pass/fail counts and printing a:::SUMMARY:::block - Streams output in real-time via
tee(no buffering) - Reports accurate job status: a final "Fail job if tests failed" step checks the test step's
outcomeand exits with code 1 if there were failures, ensuring the job conclusion isfailuredespitecontinue-on-error: trueon the test step
Triggered only via workflow_dispatch while the linux.rhel96-rocm (MI355X / gfx950) runner and image are being validated. Cron and HUD reporting will be enabled after manual soak.
Pipeline: rocm-build → determine-tests → inductor-tests → sgpu-tests → mgpu-tests
Same category split as CUDA (scripts/test_config.py), sized for a multi-GPU MI355X host.
| Input | Description | Default |
|---|---|---|
sha |
pytorch/pytorch SHA to build against (leave empty for latest nightly) | (empty = latest nightly) |
test_tier |
sanity / critical lists from test_config.py, or build-only |
critical |
test_categories |
all / inductor / sgpu / mgpu |
all |
no_cache |
Force podman --no-cache full rebuild (keep on until the ROCm image is validated) |
true |
| Selection | What runs |
|---|---|
sanity + all |
Build + short sanity lists for inductor, sgpu, mgpu |
critical + all |
Build + critical inductor / sgpu / mgpu suites (default) |
critical + sgpu |
Build + sgpu only (skips inductor/mgpu jobs) |
build-only |
Build only, skip tests |
- Resolves the source
mainSHA frompytorch/pytorchnightly (or uses the manualshainput) - Builds PyTorch from source with
USE_ROCM=1/USE_CUDA=0viadocker/Dockerfile.rhel9-rocm - Pins ROCm 7.14.0 via
amdgpu-install/rocmradeon/el9/26.13(classicrocm/el9/7.14*404s) - Defaults to
--no-cacheso a green build is a real compile (setno_cache=falselater for faster rebuilds) - Verifies the image can
import torchwith a non-emptytorch.version.hipbefore push - Pushes to Quay with tag:
quay.io/aipcc/pytorch:rhel9_6_pytorch_nightly_main_git<7char_sha>_rocm7_14_0
- Resolves inductor / sgpu / mgpu command lists from
scripts/test_config.py(--sanityor--critical) - No delta/heuristic path yet (full tier lists every run)
| Job | Category | GPU requirement | Per-command timeout |
|---|---|---|---|
inductor-tests |
TorchInductor | optional (≥1 for GPU paths) | 2h |
sgpu-tests |
Single-GPU (HIP_VISIBLE_DEVICES=0) |
≥ 1 | 2h (+ quick sanity gate) |
mgpu-tests |
Multi-GPU / distributed (RCCL via test_c10d_nccl) |
≥ 2 | 12h |
Shared behavior:
- Podman:
--ipc=host+/dev/kfd+/dev/dri(no--shm-size) CONTINUE_THROUGH_ERROR=True; summaries report completed with failures (no hard job fail on suite failures)- HUD/CRCR callbacks are disabled (
PUSH_TO_HUD=false) until the pipeline is manually validated
Triggered by CRCR repository_dispatch (pull_request type). Currently disabled (.disabled suffix) while the nightly workflow is being stabilized. Will be re-enabled once nightly results are consistently stable.
Build job:
- Checks out
pytorch/pytorchat the dispatched SHA - Builds PyTorch from source on RHEL
Sanity test job:
import torchverification- Tensor ops and dtype checks
- Autograd backward pass
- Model serialization round-trip
- Core
test_torch.pysmoke tests
Currently at L2 — nightly builds and tests run daily, with results reported back to the PyTorch HUD via the CRCR callback action. Each pipeline stage (build, cpu, inductor, sgpu, mgpu) reports its conclusion individually, giving per-job visibility on the HUD dashboard.
Each job in the nightly pipeline sends a completed callback to the PyTorch CRCR relay with event-type: nightly and delivery-id set to the resolved pytorch/pytorch source SHA. The following job names appear on HUD:
| Job | HUD job-name |
|---|---|
| build | linux-rhel9.6-cuda13.0-py3.12-gcc11-x86_64 / build |
| cpu-tests | linux-rhel9.6-cuda13.0-py3.12-gcc11-x86_64 / test (cpu, linux.rhel96) |
| inductor-tests | linux-rhel9.6-cuda13.0-py3.12-gcc11-x86_64 / test (inductor, linux.rhel96) |
| sgpu-tests | linux-rhel9.6-cuda13.0-py3.12-gcc11-x86_64 / test (sgpu, linux.rhel96) |
| mgpu-tests | linux-rhel9.6-cuda13.0-py3.12-gcc11-x86_64 / test (mgpu, linux.rhel96) |
GPU test jobs only report to CRCR when GPUs are actually available on the runner — skipped tests are not reported, avoiding misleading success entries on HUD.
ROCm jobs (rocm-build, rocm-tests) intentionally do not report to HUD yet: PUSH_TO_HUD=false in crcr-nightly-rocm.yml. Flip that flag once the ROCm pipeline has been manually validated.
The results relay accepts submissions from GitHub Actions repositories whose OIDC
tokens are listed in config/rhel_allowlist.yml. Submission authorization and
HUD forwarding are separate: the Lambda determines forward_to_hud from this
file, so a sender cannot opt itself into HUD.
Use forward_to_hud: false while onboarding or validating a new partner. Its
results are received and shown in the receiver workflow, but are not forwarded
to CRCR/HUD. Enable forwarding only after the partner is ready:
allowed_repos:
- repo: partner-org/partner-repo
forward_to_hud: falseThis repo was promoted to L2 after meeting the following criteria from RFC-0050:
| Criterion | Status |
|---|---|
| Nightly callback success rate ≥ 80% | Met |
| Results visible on PyTorch HUD | Met |
| Timeout rate < 1% | Met |
| Active for ≥ 1 month | Met |
The nightly workflow uses a dual-strategy approach for delta-based test selection:
| Tool | Strategy | Best For |
|---|---|---|
targeted_tests.py |
File-path heuristic mapping | Python file changes, test moves |
structural_tests.py |
C++ call graph + binding analysis | C++ kernel/op changes |
merge_test_results.py |
Union of both + dedup | Combined coverage |
test_config.py |
Static full suite (fallback) | When delta produces nothing |
The unified merger (merge_test_results.py) runs both tools and deduplicates results. If the structural analyzer is not installed or its index is unavailable, the system gracefully falls back to heuristic-only mode.
Test commands are validated against run_test.py's accepted test list before execution to filter out invalid entries. Tests that unconditionally require a CUDA driver (e.g., test_overrides) are excluded from CPU and Inductor categories.
A fixed set of critical tests always runs regardless of what the delta determines. These cover core subsystem health and are prepended to the delta results (deduplicated):
| Category | Critical Tests |
|---|---|
| cpu | test_torch, test_autograd, test_linalg, test_sparse, test_unary_ufuncs, test_binary_ufuncs |
| inductor | inductor/test_torchinductor, inductor/test_cpu_repro |
| sgpu | test_nn, test_torch, test_cuda, test_ops, test_unary_ufuncs, test_binary_ufuncs, test_autograd |
| mgpu | distributed/test_c10d_common, distributed/test_c10d_nccl, distributed/test_distributed_spawn |
To list critical tests for a category: python scripts/test_config.py cpu --critical --commands-only
Environment variables:
STRUCTURAL_ANALYSIS_DEPTH— Override call graph walk depth (default: 3)CONTINUE_THROUGH_ERROR— Set toTrueinside containers; allows all tests to run even if some fail
.github/workflows/
crcr-nightly.yml # Active CUDA nightly pipeline
crcr-nightly-rocm.yml # Manual ROCm build/test (HUD disabled)
rhel96-build-test.yml.disabled # PR workflow (disabled)
docker/
Dockerfile.rhel9 # RHEL 9.6 UBI build image (conda, CUDA, PyTorch from source)
Dockerfile.rhel9-rocm # RHEL 9.6 UBI build image (conda, ROCm, PyTorch from source)
scripts/
merge_test_results.py # Unified test merger (heuristic + structural)
targeted_tests.py # File-path heuristic test selector
torchtalk_tests.py # Structural call graph analyzer wrapper
test_config.py # Static categorized test suites (full-suite fallback)
requirements-structural.txt # Dependencies for structural analysis
- The
linux.rhel96(CUDA) and/orlinux.rhel96-rocm(ROCm) self-hosted runners must be registered and online podmanmust be available on the runner for container-based builds- This repo must be on the CRCR allowlist to receive dispatches:
L2: - TorchedHat/pytorch-redhat-ci
- For GPU test jobs, the runner must have NVIDIA GPUs with drivers installed
| Secret | Used By | Purpose |
|---|---|---|
RHEL_SUBSCRIPTION_ACTIVATION_KEY |
Build (Dockerfile) | RHEL subscription for dnf access |
RHEL_SUBSCRIPTION_ORG_ID |
Build (Dockerfile) | RHEL org ID for subscription-manager |
QUAY_USERNAME |
Build (push step) | Quay.io robot account or username |
QUAY_PASSWORD |
Build (push step) | Quay.io password or token |