8000 Harden exact-SHA GPU qualification and maintainer release gates by DaoyuanLi2816 · Pull Request #85 · DaoyuanLi2816/mini-verl · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
self-hosted-runner:
labels:
- cuda
- rtx4080
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ jobs:
"CODE_OF_CONDUCT.md",
"SECURITY.md",
"PROJECT_STATE.md",
"environments/known-good-rtx4080-cu130.json",
"environments/known-good-rtx4080-cu130.txt",
"LICENSE",
"README.md",
):
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ jobs:
run: |
python scripts/build_pypi_readme.py --check
python scripts/release_state.py --check
python scripts/check_known_good_environment.py
python scripts/publish_gpu_qualification_schema.py --check
python scripts/check_markdown_links.py
python scripts/check_text_integrity.py
miniverl --help
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
python scripts/publish_verl_bridge_diagrams.py --check
python scripts/publish_verl_opd_compatibility.py --check
python scripts/publish_hardware_record_schema.py --check
python scripts/publish_gpu_qualification_schema.py --check
python scripts/check_known_good_environment.py
- name: Build complete development site strictly
run: mkdocs build --strict
- name: Install deterministic Chromium
Expand Down
221 changes: 166 additions & 55 deletions .github/workflows/gpu.yml
Original file line number Diff line number Diff line change
@@ -1,74 +1,185 @@
name: gpu

# Manual only. There is no GPU runner configured for this repository, so this
# workflow is never part of a required check -- it exists so that a maintainer
# with a self-hosted CUDA runner (Actions Runner >= 2.327.1 for Node 24 actions)
# can execute the GPU tests on demand, and so the
# exact commands are version-controlled rather than living in someone's shell
# history.
#
# To use it, register a self-hosted runner with the label `cuda` and dispatch
# this workflow. Without such a runner the job will simply stay queued; that is
# expected and is not a failure of the project.

# Manual maintainer qualification. The hosted job builds the only candidate
# bytes; the private RTX 4080 job downloads and qualifies that same artifact.
on:
workflow_dispatch:
inputs:
run_recipe:
description: "Also run the full single-GPU recipe (slow, ~10 minutes)"
qualification_level:
description: "Release smoke, or all three canonical full workloads"
type: choice
required: true
default: smoke
options: [smoke, full]
offline:
description: "Require all pinned model snapshots to be present locally"
type: boolean
default: false

permissions:
contents: read

concurrency:
group: gpu-qualification-${{ github.sha }}
cancel-in-progress: false

jobs:
gpu-tests:
name: gpu tests (self-hosted cuda)
runs-on: [self-hosted, cuda]
timeout-minutes: 120
build-candidate:
name: build candidate distributions once
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Require a fresh workflow dispatch
run: |
if [ "$GITHUB_RUN_ATTEMPT" != "1" ]; then
echo "::error::GPU qualification cannot use a rerun attempt; start a new workflow_dispatch run."
exit 1
fi
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Build the closed candidate byte set
run: |
python -m pip install --disable-pip-version-check \
build==1.5.0 hatchling==1.32.0 twine==7.0.0 \
pydantic==2.13.4 PyYAML==6.0.3
python scripts/build_release_candidate.py \
--output candidate \
--commit "$GITHUB_SHA"
python scripts/build_release_candidate.py \
--output candidate \
--commit "$GITHUB_SHA" \
--check
- name: Upload the only candidate distributions
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: candidate-distributions
path: candidate/
if-no-files-found: error
retention-days: 90

- name: Environment
qualify:
name: qualify exact candidate on RTX 4080
needs: build-candidate
runs-on: [self-hosted, cuda, rtx4080]
timeout-minutes: 360
env:
PYTHONUTF8: "1"
PYTHONPATH: ""
PYTHONHOME: ""
defaults:
run:
shell: pwsh
steps:
- name: Require a fresh workflow dispatch
run: |
if ($env:GITHUB_RUN_ATTEMPT -ne "1") {
throw "GPU qualification cannot use a rerun attempt; start a new workflow_dispatch run."
}
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Download this run's candidate distributions
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: candidate-distributions
path: candidate
- name: Verify the dedicated measured device
run: |
$name = nvidia-smi --query-gpu=name --format=csv,noheader
if (($name | Measure-Object).Count -ne 1) { throw "exactly one visible GPU is required" }
if ($name.Trim() -ne "NVIDIA GeForce RTX 4080") { throw "unexpected GPU: $name" }
nvidia-smi
python --version

- name: Install
- name: Validate candidate before installation
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,train,cuda]"

- name: doctor
python -m venv .gpu-qualification-venv
$python = (Resolve-Path '.gpu-qualification-venv/Scripts/python.exe').Path
& $python -m pip install --upgrade pip
& $python -m pip install pydantic==2.13.4 PyYAML==6.0.3
& $python scripts/build_release_candidate.py `
--output candidate `
--commit $env:GITHUB_SHA `
--repository $env:GITHUB_REPOSITORY `
--workflow-path .github/workflows/gpu.yml `
--run-id $env:GITHUB_RUN_ID `
--run-attempt $env:GITHUB_RUN_ATTEMPT `
--check
$manifest = Get-Content -Raw -LiteralPath 'candidate/candidate-manifest.json' | ConvertFrom-Json
$wheel = (Resolve-Path (Join-Path 'candidate' $manifest.wheel.filename)).Path
"QUALIFICATION_WHEEL=$wheel" | Out-File -FilePath $env:GITHUB_ENV -Append
"QUALIFICATION_PYTHON=$python" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Install the exact candidate wheel
run: |
miniverl doctor
miniverl doctor --json > doctor.json
python -c "import json; d=json.load(open('doctor.json')); assert d['verdict']['gpu_training'] is True"

- name: GPU tests
run: pytest -q -m gpu -ra

- name: Full single-GPU recipe
if: ${{ inputs.run_recipe }}
env:
MINIVERL_LOG_LEVEL: INFO
& $env:QUALIFICATION_PYTHON -m pip install 'torch==2.13.0+cu130' --index-url https://download.pytorch.org/whl/cu130
& $env:QUALIFICATION_PYTHON -m pip install "$env:QUALIFICATION_WHEEL[dev,train,cuda]" --constraint environments/known-good-rtx4080-cu130.txt
(Resolve-Path '.gpu-qualification-venv/Scripts').Path | Out-File -FilePath $env:GITHUB_PATH -Append
- name: Run wheel-installed release smoke
run: |
miniverl train recipes/qwen_consumer_gpu_calc.yaml --output runs --run-id ci-4080 --json > train.json
miniverl eval --run runs/ci-4080 --split test --json > eval.json
miniverl report runs/ci-4080 --out runs/ci-4080/report.html
miniverl export-benchmark runs/ci-4080 --out runs/ci-4080/benchmark-submission.json
python - <<'PY'
import json
from miniverl.evaluation.schema import BenchmarkResult
BenchmarkResult.model_validate(json.load(open("runs/ci-4080/benchmark-submission.json")))
print("submission validates against the published schema")
PY

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
$offline = @()
if ('${{ inputs.offline }}' -eq 'true') { $offline = @('--offline') }
New-Item -ItemType Directory -Path qualification | Out-Null
Copy-Item -LiteralPath $env:QUALIFICATION_WHEEL -Destination qualification
& $env:QUALIFICATION_PYTHON scripts/run_gpu_qualification.py `
--commit $env:GITHUB_SHA `
--wheel (Join-Path 'qualification' (Split-Path $env:QUALIFICATION_WHEEL -Leaf)) `
--candidate-manifest candidate/candidate-manifest.json `
--known-good environments/known-good-rtx4080-cu130.json `
--output qualification `
--work qualification-work `
@offline
$candidate = Get-Content -Raw candidate/candidate-manifest.json | ConvertFrom-Json
$manifestSha = (Get-FileHash -Algorithm SHA256 candidate/candidate-manifest.json).Hash.ToLower()
& $env:QUALIFICATION_PYTHON scripts/validate_gpu_qualification.py `
qualification/qualification.json `
--commit $env:GITHUB_SHA `
--wheel-sha256 $candidate.wheel.sha256 `
--candidate-manifest-sha256 $manifestSha `
--required-gpu-name 'NVIDIA GeForce RTX 4080'
- name: Run GPU regression tests
run: '& $env:QUALIFICATION_PYTHON -m pytest -q -m gpu -ra'
- name: Upload exact-commit release smoke
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: gpu-release-smoke
path: qualification/
if-no-files-found: error
retention-days: 90
- name: A46E Run full canonical qualification workloads
if: ${{ inputs.qualification_level == 'full' }}
run: |
$offline = @()
if ('${{ inputs.offline }}' -eq 'true') { $offline = @('--offline') }
& $env:QUALIFICATION_PYTHON -m pip install 'hydra-core>=1.3,<2'
& $env:QUALIFICATION_PYTHON -m pip install --no-deps 'git+https://github.com/verl-project/verl.git@7aed6b230776f963fa09509c10d9c3a767d1102c'
New-Item -ItemType Directory -Path qualification-full | Out-Null
& $env:QUALIFICATION_PYTHON scripts/run_verl_opd_reference_workload.py --out qualification-full-work/direct --result qualification-full/direct.json @offline
& $env:QUALIFICATION_PYTHON scripts/run_verl_pg_k1_workload.py --out qualification-full-work/pg-k1 --result qualification-full/pg-k1.json @offline
& $env:QUALIFICATION_PYTHON scripts/run_smollm2_opd_reference_workload.py --out qualification-full-work/smollm2 --result qualification-full/smollm2.json @offline
& $env:QUALIFICATION_PYTHON scripts/promote_full_gpu_qualification.py `
--qualification qualification/qualification.json `
--direct qualification-full/direct.json `
--pg-k1 qualification-full/pg-k1.json `
--smollm2 qualification-full/smollm2.json
- name: Upload full qualification records
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ success() && inputs.qualification_level == 'full' }}
with:
name: gpu-run
path: |
runs/ci-4080/manifest.json
runs/ci-4080/eval.json
runs/ci-4080/summary.md
runs/ci-4080/benchmark-submission.json
if-no-files-found: ignore
name: gpu-full-qualification
path: qualification/
if-no-files-found: error
retention-days: 90
- name: Remove runner-local environments and model work products
if: always()
run: |
foreach ($relative in @('.gpu-qualification-venv', 'qualification-work', 'qualification-full-work')) {
if (Test-Path -LiteralPath $relative) {
$target = (Resolve-Path -LiteralPath $relative).Path
$workspace = (Resolve-Path -LiteralPath $env:GITHUB_WORKSPACE).Path
if (-not $target.StartsWith($workspace, [System.StringComparison]::OrdinalIgnoreCase)) { throw "refusing cleanup outside workspace: $target" }
Remove-Item -LiteralPath $target -Recurse -Force
}
}
Loading
0