[Release] Make SM70 wheel delivery self-contained

Bundle the release launcher in the source build stage, pin the CUDA 12.8 runtime dependency for SM70 wheels, and validate wheel metadata, ZIP paths, and native loader paths before publication.

Assisted-by: OpenAI Codex
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
This commit is contained in:
yangzhuxinyzx committed 2026-09-30 00:06:51 +08:00
1 parent c5cbf1e247
commit 6f6fc4c527
6 files changed
+192 -11

No files matched your search

+2 -2
View File
@@ -89,8 +89,8 @@ steps:
- "DOCKER_BUILDKIT=1 docker build --build-arg max_jobs=16 --build-arg USE_SCCACHE=1 --build-arg GIT_REPO_CHECK=1 --build-arg CUDA_VERSION=12.8.1 --build-arg torch_cuda_arch_list=\"${CUDA_ARCH_SM70}\" --build-arg BUILD_OS=manylinux --build-arg BUILD_BASE_IMAGE=pytorch/manylinux2_28-builder:cuda12.8 --tag vllm-ci:build-image-sm70 --target build --progress plain -f docker/Dockerfile ."
- "mkdir artifacts"
- "docker run --rm -v $(pwd)/artifacts:/artifacts_host vllm-ci:build-image-sm70 bash -c 'python3 tools/check_sm70_release_artifact.py dist/*.whl && cp -r dist /artifacts_host && chmod -R a+rw /artifacts_host'"
- "bash .buildkite/scripts/upload-nightly-wheels.sh"
- 'bash .buildkite/scripts/annotate-build-artifact.sh "$$BUILDKITE_LABEL" "s3://vllm-wheels/$$BUILDKITE_COMMIT/$(cd artifacts/dist && echo *.whl)"'
- 'buildkite-agent artifact upload "artifacts/dist/*.whl"'
- 'bash .buildkite/scripts/annotate-build-artifact.sh "$$BUILDKITE_LABEL" "Buildkite artifact: artifacts/dist/*.whl"'
env:
DOCKER_BUILDKIT: "1"
+24
View File
@@ -1024,6 +1024,26 @@ verifier out of its fast path.
---
# 📦 Install the SM70 release wheel
For the Qwen3.8-27B NVFP4 + DFlash2 V100 profile, install the release wheel
into a clean Python 3.12 environment. The wheel carries the SM70 extensions,
Flash-V100, FlashQLA, and the versioned launcher; its metadata selects the
CUDA 12.8 PyTorch wheels for this profile.
```bash
python -m venv .venv
. .venv/bin/activate
python -m pip install ./1cat_vllm-<version>-cp312-cp312-manylinux_2_28_x86_64.whl
serve_qwen38_27b_nvfp4_v100 /models/Qwen3.8-27B-NVFP4
```
The launcher uses the installed `vllm` command and packaged extensions. It
does not require a checkout path, a copied `.so`, or `VLLM_*`/`FLASH_*`
environment overrides. Four peer-connected V100-SXM2 32GB GPUs are required
for this profile; pass normal `vllm serve` options after the model path when
changing the service port or resource limits.
# 🔨 Build From Source
Clone:
@@ -1042,6 +1062,10 @@ export CMAKE_CUDA_ARCHITECTURES=70
Then build/install the project using the repository's current build instructions for your CUDA/PyTorch environment.
The two variables above are build-time inputs for a source build only. They
are already fixed in the SM70 release-wheel build and are not needed after
installing that wheel.
Because this project contains custom CUDA extensions, make sure the active compiler/toolkit matches the PyTorch CUDA ABI used by your environment.
---
+1
View File
@@ -342,6 +342,7 @@ RUN --mount=type=cache,target=/opt/uv/cache \
WORKDIR /workspace
COPY pyproject.toml setup.py CMakeLists.txt ./
COPY scripts/serve_qwen38_27b_nvfp4_v100.sh scripts/
COPY cmake cmake/
COPY csrc csrc/
COPY flash_qla flash_qla/
+10 -1
View File
@@ -1207,9 +1207,18 @@ def get_requirements() -> list[str]:
elif _is_cuda():
requirements = _read_requirements("cuda.txt")
cuda_major, cuda_minor = torch.version.cuda.split(".")
# An SM70 wheel is a CUDA 12.8 release artifact. Keep its runtime
# dependency on the matching PyTorch wheel in the wheel metadata so a
# user installing the wheel does not need to know about this build
# detail or export a local-environment override. The explicit env
# switch remains for reproducible packaging jobs that pin the same
# dependency while using a non-SM70 target.
pin_torch_cu128 = bool(int(os.getenv("ONECAT_VLLM_PIN_TORCH_CU128", "0"))) or (
_cuda_arch_contains(7, 0) and torch.version.cuda == "12.8"
)
modified_requirements = []
for req in requirements:
if bool(int(os.getenv("ONECAT_VLLM_PIN_TORCH_CU128", "0"))):
if pin_torch_cu128:
req = ONECAT_TORCH_CU128_URLS.get(req.split("#", 1)[0].strip(), req)
if "vllm-flash-attn" in req and cuda_major != "12":
# vllm-flash-attn is built only for CUDA 12.x.
+79
View File
@@ -3,6 +3,8 @@
from __future__ import annotations
import shutil
import subprocess
import zipfile
from pathlib import Path
@@ -31,6 +33,12 @@ def write_wheel(path: Path, members: list[str]) -> None:
with zipfile.ZipFile(path, "w") as wheel:
for member in members:
wheel.writestr(member, b"fixture; dynamic inspection is disabled")
wheel.writestr(
"1cat_vllm-0.0.0.dist-info/WHEEL",
"Wheel-Version: 1.0\n"
"Generator: test\n"
"Tag: cp312-cp312-manylinux_2_28_x86_64\n",
)
def test_complete_sm70_wheel_manifest_passes(tmp_path: Path) -> None:
@@ -66,6 +74,77 @@ def test_launcher_is_required(tmp_path: Path) -> None:
check_wheel(wheel, inspect_dynamic=False)
def test_release_wheel_requires_cp312_tag(tmp_path: Path) -> None:
wheel = tmp_path / "wrong-tag.whl"
with zipfile.ZipFile(wheel, "w") as archive:
for member in REQUIRED_MEMBERS:
archive.writestr(member, b"fixture")
archive.writestr(
"1cat_vllm-0.0.0.dist-info/WHEEL",
"Wheel-Version: 1.0\n"
"Generator: test\n"
"Tag: cp311-cp311-manylinux_2_28_x86_64\n",
)
with pytest.raises(ArtifactError, match="Python 3.12"):
check_wheel(wheel, inspect_dynamic=False)
def test_unsafe_zip_member_is_rejected_before_dynamic_inspection(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
wheel = tmp_path / "unsafe.whl"
write_wheel(wheel, [*REQUIRED_MEMBERS, "../outside.so"])
def fail_if_called(*args, **kwargs):
pytest.fail("dynamic inspection must not extract unsafe members")
monkeypatch.setattr(
"tools.check_sm70_release_artifact._check_dynamic_dependencies",
fail_if_called,
)
with pytest.raises(ArtifactError, match="unsafe wheel member"):
check_wheel(wheel)
def test_dynamic_inspection_rejects_private_rpath(tmp_path: Path) -> None:
compiler = shutil.which("cc")
if compiler is None:
pytest.skip("cc is required for the native artifact fixture")
assert compiler is not None
source = tmp_path / "fixture.c"
source.write_text("int fixture(void) { return 1; }\n", encoding="utf-8")
native = tmp_path / "fixture.so"
subprocess.run(
[
compiler,
"-shared",
"-fPIC",
str(source),
"-Wl,-rpath,/home/private-build/lib",
"-o",
str(native),
],
check=True,
)
wheel = tmp_path / "private-rpath.whl"
with zipfile.ZipFile(wheel, "w") as archive:
payload = native.read_bytes()
for member in REQUIRED_MEMBERS:
archive.writestr(member, payload)
archive.writestr(
"1cat_vllm-0.0.0.dist-info/WHEEL",
"Wheel-Version: 1.0\n"
"Generator: test\n"
"Tag: cp312-cp312-manylinux_2_28_x86_64\n",
)
with pytest.raises(ArtifactError, match="private build path|RPATH"):
check_wheel(wheel)
def test_release_launcher_has_no_developer_runtime_overlays() -> None:
launcher = (
Path(__file__).resolve().parents[2]
+76 -8
View File
@@ -16,7 +16,7 @@ import shutil
import subprocess
import tempfile
import zipfile
from pathlib import Path
from pathlib import Path, PurePosixPath
import regex as re
@@ -69,6 +69,8 @@ REQUIRED_MEMBERS: tuple[tuple[str, re.Pattern[str]], ...] = (
)
LAUNCHER_SUFFIX = ".data/scripts/serve_qwen38_27b_nvfp4_v100.sh"
WHEEL_METADATA_PATTERN = re.compile(r"^[^/]+\.dist-info/WHEEL$")
RELEASE_PYTHON_TAG = "cp312"
PRIVATE_PATH_MARKERS = (
"/home/",
"/data/",
@@ -87,6 +89,37 @@ def _dynamic_section_uses_private_path(output: str) -> bool:
return any(marker in lowered for marker in PRIVATE_PATH_MARKERS)
def _dynamic_section_errors(member: str, output: str) -> list[str]:
"""Reject loader paths that escape the wheel or name an absolute DSO."""
errors: list[str] = []
if _dynamic_section_uses_private_path(output):
errors.append(f"{member}: private build path in dynamic section")
for line in output.splitlines():
if "(RPATH)" not in line and "(RUNPATH)" not in line:
continue
match = re.search(r"\[(.*?)\]", line)
if match is None:
continue
for path in match.group(1).split(":"):
path = path.strip()
if not path:
continue
if path.startswith("$ORIGIN") or path.startswith("${ORIGIN}"):
continue
errors.append(f"{member}: non-portable RPATH/RUNPATH entry {path!r}")
# A DT_NEEDED entry with a slash is an absolute or caller-relative loader
# path. A wheel may rely on system CUDA/driver libraries by soname, but it
# must not reach back into the build host for a private .so.
for match in re.finditer(r"Shared library: \[(.*?)\]", output):
needed = match.group(1)
if "/" in needed or "\\" in needed:
errors.append(f"{member}: non-portable DT_NEEDED entry {needed!r}")
return errors
def _check_dynamic_dependencies(
wheel: zipfile.ZipFile,
native_members: list[str],
@@ -111,8 +144,7 @@ def _check_dynamic_dependencies(
if result.returncode != 0:
errors.append(f"{member}: readelf failed: {result.stderr.strip()}")
continue
if _dynamic_section_uses_private_path(result.stdout):
errors.append(f"{member}: private build path in RPATH/RUNPATH")
errors.extend(_dynamic_section_errors(member, result.stdout))
return errors
@@ -123,12 +155,19 @@ def check_wheel(path: Path, *, inspect_dynamic: bool = True) -> None:
raise ArtifactError(f"wheel does not exist: {path}")
with zipfile.ZipFile(path) as wheel:
members = set(wheel.namelist())
member_list = wheel.namelist()
members = set(member_list)
errors: list[str] = []
for member in members:
pure = member.replace("\\", "/")
if pure.startswith("/") or "../" in pure:
if len(members) != len(member_list):
errors.append("wheel contains duplicate member names")
for member in member_list:
if "\\" in member or "\x00" in member:
errors.append(f"unsafe wheel member: {member}")
continue
pure = PurePosixPath(member)
if pure.is_absolute() or ".." in pure.parts:
errors.append(f"unsafe wheel member: {member}")
for description, pattern in REQUIRED_MEMBERS:
@@ -138,7 +177,36 @@ def check_wheel(path: Path, *, inspect_dynamic: bool = True) -> None:
if not any(member.endswith(LAUNCHER_SUFFIX) for member in members):
errors.append("missing packaged V100 release launcher")
native_members = sorted(member for member in members if member.endswith(".so"))
wheel_metadata = [
member for member in members if WHEEL_METADATA_PATTERN.fullmatch(member)
]
if not wheel_metadata:
errors.append("missing wheel WHEEL metadata")
else:
tags = []
for member in wheel_metadata:
metadata = wheel.read(member).decode("utf-8", errors="replace")
tags.extend(
line.partition(":")[2].strip()
for line in metadata.splitlines()
if line.startswith("Tag:")
)
if not tags:
errors.append("wheel WHEEL metadata has no Tag entry")
elif not any(tag.startswith(f"{RELEASE_PYTHON_TAG}-") for tag in tags):
errors.append(
"SM70 release wheel must publish a Python 3.12-compatible Tag"
)
# Do not inspect or extract native members until all member names have
# passed validation. This keeps a malformed ZIP from writing outside
# the temporary directory used by the dynamic dependency check.
if errors:
raise ArtifactError("; ".join(errors))
native_members = sorted(
member for member in members if re.search(r"\.so(?:\..*)?$", member)
)
if inspect_dynamic:
errors.extend(_check_dynamic_dependencies(wheel, native_members))