mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-30 06:30:37 +02:00
feat: add Jetson contributor validation path
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
"""Physical NVIDIA Jetson CUDA placement and operation probe."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from accelerate.hooks import AlignDevicesHook, add_hook_to_module
|
||||
|
||||
from obliteratus import device
|
||||
from obliteratus.abliterate import AbliterationPipeline
|
||||
|
||||
|
||||
pytestmark = pytest.mark.gpu
|
||||
|
||||
|
||||
def test_jetson_cuda_runtime_contract():
|
||||
assert platform.machine().lower() in {"aarch64", "arm64"}
|
||||
assert Path("/etc/nv_tegra_release").is_file()
|
||||
assert torch.version.cuda is not None
|
||||
assert torch.cuda.is_available()
|
||||
assert torch.cuda.device_count() > 0
|
||||
assert device.is_cuda()
|
||||
assert device.get_device("auto") == "cuda"
|
||||
tensor = torch.arange(16, device="cuda", dtype=torch.float32).reshape(4, 4)
|
||||
result = tensor @ tensor.T
|
||||
assert result.device.type == "cuda"
|
||||
assert torch.isfinite(result).all()
|
||||
|
||||
|
||||
def test_jetson_cuda_offloaded_surgery_contract():
|
||||
module = nn.Module()
|
||||
module.proj = nn.Linear(4, 4, bias=False)
|
||||
original = module.proj.weight.detach().clone()
|
||||
hook = AlignDevicesHook(execution_device="cuda", offload=True)
|
||||
add_hook_to_module(module.proj, hook)
|
||||
|
||||
count = AbliterationPipeline._project_out_advanced(
|
||||
module,
|
||||
torch.tensor([[1.0], [0.0], [0.0], [0.0]], device="cuda"),
|
||||
["proj"],
|
||||
)
|
||||
output = module.proj(torch.ones(1, 4, device="cuda"))
|
||||
|
||||
expected = original.clone()
|
||||
expected[:, 0] = 0
|
||||
assert count == 1
|
||||
assert output.device.type == "cuda"
|
||||
assert module.proj.weight.device.type == "meta"
|
||||
torch.testing.assert_close(hook.weights_map["weight"], expected)
|
||||
@@ -129,6 +129,24 @@ def test_pull_request_gate_is_fast_risk_mapped_and_uses_shared_floor():
|
||||
assert "tests/conditional/" in policy["excluded_test_prefixes"]
|
||||
|
||||
|
||||
def test_arm64_preflight_proves_portability_without_claiming_jetson_cuda():
|
||||
workflow = WORKFLOW.read_text(encoding="utf-8")
|
||||
arm = workflow.split(" arm64-preflight:\n", maxsplit=1)[1].split(
|
||||
" pr-core:\n",
|
||||
maxsplit=1,
|
||||
)[0]
|
||||
|
||||
assert "runs-on: ubuntu-24.04-arm" in arm
|
||||
assert "github.event_name == 'pull_request'" in arm
|
||||
assert "github.event_name == 'push' && github.ref == 'refs/heads/main'" in arm
|
||||
assert "-m build --wheel" in arm
|
||||
assert "import obliteratus" in arm
|
||||
assert "-m obliteratus --help" in arm
|
||||
assert "tests/test_device_boundaries.py" in arm
|
||||
assert "tests/test_jetson_support_tooling.py" in arm
|
||||
assert "jetson-runtime" not in arm
|
||||
|
||||
|
||||
def test_release_depth_jobs_only_run_for_tags_or_manual_validation():
|
||||
workflow = WORKFLOW.read_text(encoding="utf-8")
|
||||
release_condition = (
|
||||
|
||||
@@ -42,6 +42,7 @@ def test_cuda_job_replaces_locked_cpu_torch_with_same_version_cuda_build():
|
||||
cuda_job = workflow.split(" cuda:\n", maxsplit=1)[1].split(" mps:\n", maxsplit=1)[0]
|
||||
|
||||
assert "torch.__version__.split" in cuda_job
|
||||
assert "--extra quantization" in cuda_job
|
||||
assert "UV_TORCH_BACKEND=cu130 uv pip install" in cuda_job
|
||||
assert "--reinstall-package torch" in cuda_job
|
||||
assert '"torch==$CUDA_TORCH_VERSION"' in cuda_job
|
||||
@@ -49,6 +50,28 @@ def test_cuda_job_replaces_locked_cpu_torch_with_same_version_cuda_build():
|
||||
assert 'uv pip check --python "$CONDITIONAL_ENV/bin/python"' in cuda_job
|
||||
|
||||
|
||||
def test_jetson_job_is_manual_physical_trusted_and_retains_sanitized_evidence():
|
||||
workflow = (ROOT / ".github" / "workflows" / "conditional-tests.yml").read_text()
|
||||
jetson_job = workflow.split(" jetson:\n", maxsplit=1)[1].split(
|
||||
" mps:\n",
|
||||
maxsplit=1,
|
||||
)[0]
|
||||
|
||||
assert "github.event_name == 'workflow_dispatch' && inputs.run_jetson" in jetson_job
|
||||
assert "runs-on: [self-hosted, linux, ARM64, jetson]" in jetson_job
|
||||
assert "scripts/setup_jetson.py" in jetson_job
|
||||
assert "scripts/run_conditional_gate.py jetson-runtime" in jetson_job
|
||||
assert "scripts/jetson_support.py" in jetson_job
|
||||
assert "conditional-jetson-${{ github.run_attempt }}" in jetson_job
|
||||
assert "retention-days: 30" in jetson_job
|
||||
assert "actions/setup-python" not in jetson_job
|
||||
|
||||
policy = json.loads((ROOT / "ci" / "conditional-test-policy.json").read_text())
|
||||
gate = next(value for value in policy["gates"] if value["id"] == "jetson-runtime")
|
||||
assert gate["job"] == "jetson"
|
||||
assert gate["runner"] == "self-hosted, linux, ARM64, jetson"
|
||||
|
||||
|
||||
def test_policy_rejects_unknown_cpu_exclusion_gate(tmp_path):
|
||||
policy = json.loads((ROOT / "ci" / "conditional-test-policy.json").read_text())
|
||||
quality = {
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
"""CPU-testable contracts for the experimental Jetson support path."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.metadata
|
||||
import json
|
||||
import sys
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from scripts import jetson_support
|
||||
from scripts import run_conditional_gate
|
||||
from scripts import setup_jetson
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[1]
|
||||
|
||||
|
||||
def test_host_evidence_is_allow_listed(monkeypatch, tmp_path):
|
||||
tegra = tmp_path / "nv_tegra_release"
|
||||
tegra.write_text("# R36 (release), REVISION: 4.3\nserial=secret\n")
|
||||
os_release = tmp_path / "os-release"
|
||||
os_release.write_text(
|
||||
'ID=ubuntu\nVERSION_ID="22.04"\nPRETTY_NAME="Ubuntu 22.04"\nSECRET=value\n'
|
||||
)
|
||||
monkeypatch.setattr(jetson_support.platform, "machine", lambda: "aarch64")
|
||||
monkeypatch.setattr(jetson_support.platform, "python_version", lambda: "3.10.12")
|
||||
monkeypatch.setattr(jetson_support, "_capture", lambda command, **kwargs: "6.2+b17")
|
||||
|
||||
facts = jetson_support.collect_host_facts(
|
||||
tegra_release=tegra,
|
||||
os_release=os_release,
|
||||
)
|
||||
|
||||
assert facts == {
|
||||
"architecture": "aarch64",
|
||||
"python_version": "3.10.12",
|
||||
"os": {
|
||||
"id": "ubuntu",
|
||||
"version_id": "22.04",
|
||||
"pretty_name": "Ubuntu 22.04",
|
||||
},
|
||||
"l4t_release": "# R36 (release), REVISION: 4.3",
|
||||
"jetpack_package": "6.2+b17",
|
||||
}
|
||||
assert "secret" not in json.dumps(facts).lower()
|
||||
|
||||
|
||||
def test_runtime_evidence_reports_cuda_without_device_identity(monkeypatch):
|
||||
properties = SimpleNamespace(name="Orin", total_memory=64 * 1024**3)
|
||||
fake_cuda = SimpleNamespace(
|
||||
is_available=lambda: True,
|
||||
device_count=lambda: 1,
|
||||
get_device_properties=lambda _index: properties,
|
||||
get_device_capability=lambda _index: (8, 7),
|
||||
)
|
||||
fake_torch = SimpleNamespace(
|
||||
__version__="2.8.0a0+nv25.06",
|
||||
version=SimpleNamespace(cuda="12.6"),
|
||||
cuda=fake_cuda,
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "torch", fake_torch)
|
||||
|
||||
def missing_package(_name):
|
||||
raise importlib.metadata.PackageNotFoundError
|
||||
|
||||
monkeypatch.setattr(jetson_support.importlib.metadata, "version", missing_package)
|
||||
|
||||
facts = jetson_support.collect_runtime_facts()
|
||||
|
||||
assert facts["cuda_available"] is True
|
||||
assert facts["device_name"] == "Orin"
|
||||
assert facts["compute_capability"] == [8, 7]
|
||||
assert facts["total_memory_gb"] == 64.0
|
||||
assert set(facts) == {
|
||||
"torch_imported",
|
||||
"torch_version",
|
||||
"torch_cuda_version",
|
||||
"cuda_available",
|
||||
"cuda_device_count",
|
||||
"device_name",
|
||||
"compute_capability",
|
||||
"total_memory_gb",
|
||||
"bitsandbytes_version",
|
||||
}
|
||||
|
||||
|
||||
def test_report_validation_blocks_non_jetson_or_non_cuda_and_warns_on_bnb():
|
||||
report = {
|
||||
"host": {"architecture": "x86_64", "l4t_release": None},
|
||||
"runtime": {
|
||||
"torch_imported": True,
|
||||
"torch_cuda_version": None,
|
||||
"cuda_available": False,
|
||||
"bitsandbytes_version": "0.47.0",
|
||||
},
|
||||
}
|
||||
|
||||
errors, warnings = jetson_support.validate_report(report)
|
||||
|
||||
assert len(errors) == 3
|
||||
assert any("ARM64" in error for error in errors)
|
||||
assert any("Jetson L4T" in error for error in errors)
|
||||
assert any("not a CUDA build" in error for error in errors)
|
||||
assert warnings and "unsupported" in warnings[0]
|
||||
|
||||
|
||||
def test_gate_evidence_and_issue_body_cannot_copy_arbitrary_fields(tmp_path):
|
||||
evidence = tmp_path / "gate.json"
|
||||
evidence.write_text(json.dumps({
|
||||
"gate": "jetson-runtime",
|
||||
"status": "passed",
|
||||
"git_sha": "a" * 40,
|
||||
"counts": {"tests": 1, "token": "nested-secret"},
|
||||
"token": "must-not-escape",
|
||||
"hostname": "must-not-escape",
|
||||
}))
|
||||
|
||||
summary = jetson_support._gate_summary(evidence)
|
||||
body = jetson_support.issue_body({"gate_evidence": summary})
|
||||
|
||||
assert summary == {
|
||||
"gate": "jetson-runtime",
|
||||
"status": "passed",
|
||||
"git_sha": "a" * 40,
|
||||
"counts": {"tests": 1},
|
||||
}
|
||||
assert "must-not-escape" not in body
|
||||
assert "nested-secret" not in body
|
||||
assert "excludes environment variables" in body
|
||||
|
||||
|
||||
def test_dependency_export_rejects_torch_and_bitsandbytes(tmp_path):
|
||||
requirements = tmp_path / "requirements.txt"
|
||||
requirements.write_text("transformers==4.56.0\npytest==8.4.1\n")
|
||||
setup_jetson._require_exclusions(requirements)
|
||||
|
||||
for forbidden in (
|
||||
"torch==2.8.0",
|
||||
"torch @ https://example.invalid/torch.whl",
|
||||
"bitsandbytes[diagnostics]==0.47.0",
|
||||
):
|
||||
requirements.write_text(forbidden + "\n")
|
||||
with pytest.raises(RuntimeError, match="forbidden packages"):
|
||||
setup_jetson._require_exclusions(requirements)
|
||||
|
||||
|
||||
def test_jetson_bootstrap_preserves_vendor_runtime_and_uses_locked_no_deps(
|
||||
monkeypatch,
|
||||
tmp_path,
|
||||
):
|
||||
project = tmp_path / "project"
|
||||
(project / "scripts").mkdir(parents=True)
|
||||
(project / "scripts" / "jetson_support.py").write_text("# fixture\n")
|
||||
(project / "uv.lock").write_text("# fixture\n")
|
||||
venv = tmp_path / "jetson-venv"
|
||||
commands: list[list[str]] = []
|
||||
|
||||
def fake_run(command, *, cwd):
|
||||
command = list(command)
|
||||
commands.append(command)
|
||||
if command[1:4] == ["-m", "venv", "--system-site-packages"]:
|
||||
(venv / "bin").mkdir(parents=True)
|
||||
(venv / "pyvenv.cfg").write_text("include-system-site-packages = true\n")
|
||||
if "--output-file" in command:
|
||||
output = Path(command[command.index("--output-file") + 1])
|
||||
output.write_text("transformers==4.56.0\n")
|
||||
|
||||
monkeypatch.setattr(setup_jetson, "_run", fake_run)
|
||||
|
||||
setup_jetson.prepare(
|
||||
project=project,
|
||||
venv=venv,
|
||||
python="vendor-python",
|
||||
uv_python="tool-python",
|
||||
reuse=False,
|
||||
)
|
||||
|
||||
assert commands[0][0] == "vendor-python"
|
||||
assert "--check" in commands[0]
|
||||
export = next(command for command in commands if "export" in command)
|
||||
assert export.count("--no-emit-package") == 2
|
||||
assert "torch" in export and "bitsandbytes" in export
|
||||
installs = [command for command in commands if "install" in command]
|
||||
assert len(installs) == 2
|
||||
assert all("--no-deps" in command for command in installs)
|
||||
assert any("check" in command for command in commands)
|
||||
|
||||
|
||||
def test_bootstrap_rejects_unsafe_or_non_vendor_reusable_targets(tmp_path):
|
||||
project = tmp_path / "project"
|
||||
project.mkdir()
|
||||
with pytest.raises(ValueError, match="unsafe"):
|
||||
setup_jetson._require_safe_target(project, project)
|
||||
|
||||
existing = tmp_path / "existing"
|
||||
existing.mkdir()
|
||||
(existing / "pyvenv.cfg").write_text("include-system-site-packages = false\n")
|
||||
with pytest.raises(ValueError, match="does not expose JetPack"):
|
||||
setup_jetson._require_new_or_reusable_venv(existing, reuse=True)
|
||||
|
||||
|
||||
def test_bitsandbytes_is_opt_in_for_quantization_only():
|
||||
metadata = tomllib.loads((ROOT / "pyproject.toml").read_text())
|
||||
base = metadata["project"]["dependencies"]
|
||||
extras = metadata["project"]["optional-dependencies"]
|
||||
|
||||
assert not any(value.startswith("bitsandbytes") for value in base)
|
||||
assert extras["quantization"] == ["bitsandbytes>=0.46.1"]
|
||||
|
||||
|
||||
def test_jetson_issue_form_requires_reproducible_sanitized_hardware_evidence():
|
||||
form = yaml.safe_load(
|
||||
(ROOT / ".github" / "ISSUE_TEMPLATE" / "jetson-runtime.yml").read_text(),
|
||||
)
|
||||
fields = {value.get("id"): value for value in form["body"] if value.get("id")}
|
||||
|
||||
assert set(fields) == {
|
||||
"device",
|
||||
"jetpack",
|
||||
"commit",
|
||||
"reproduction",
|
||||
"expected",
|
||||
"actual",
|
||||
"evidence",
|
||||
"confirmations",
|
||||
}
|
||||
assert all(value.get("validations", {}).get("required") for value in fields.values())
|
||||
confirmations = fields["confirmations"]["attributes"]["options"]
|
||||
assert all(option["required"] for option in confirmations)
|
||||
assert any("secrets" in option["label"] for option in confirmations)
|
||||
|
||||
|
||||
def test_jetson_conditional_prerequisites_are_physical_and_cuda(monkeypatch, tmp_path):
|
||||
fake_torch = SimpleNamespace(cuda=SimpleNamespace(is_available=lambda: True))
|
||||
monkeypatch.setitem(sys.modules, "torch", fake_torch)
|
||||
monkeypatch.setattr(run_conditional_gate.platform, "machine", lambda: "aarch64")
|
||||
tegra = tmp_path / "nv_tegra_release"
|
||||
tegra.write_text("# R36\n")
|
||||
monkeypatch.setattr(run_conditional_gate, "JETSON_RELEASE", tegra)
|
||||
assert run_conditional_gate.missing_prerequisites("jetson-runtime") == []
|
||||
|
||||
monkeypatch.setattr(run_conditional_gate.platform, "machine", lambda: "x86_64")
|
||||
fake_torch.cuda.is_available = lambda: False
|
||||
tegra.unlink()
|
||||
assert run_conditional_gate.missing_prerequisites("jetson-runtime") == [
|
||||
"a CUDA-capable PyTorch runtime",
|
||||
"an ARM64 host",
|
||||
"a Jetson L4T runtime",
|
||||
]
|
||||
Reference in New Issue
Block a user