#!/usr/bin/env bats # Seam: ComfyUI ROCm GPU configuration on Dankclaw. # The gfx1151-enabled PyTorch build is the only verified GPU path on Strix # Halo. The compose must: # - build from a Containerfile based on the gfx1151 ROCm torch image # - pass /dev/kfd and /dev/dri to the container # - NOT pass --cpu # - add the video and render groups # # STATUS (2026-08-19): the GPU migration is applied. The compose builds # comfyui-rocm:gfx1151 from Containerfile.comfyui (AMD's gfx1151 torch base), # passes /dev/kfd + /dev/dri, adds the video group (render is unmappable in # rootless), and drops --cpu. # The four compose-wiring tests below are active (un-skipped). See # serverAppConfigs-docs/Research/ComfyUI ROCm and Vulkan.md. load helpers @test "comfyui builds from the gfx1151 ROCm Containerfile" { local compose="$REPO_ROOT/dankclaw/compose.yaml" python3 - "$compose" <<'PYEOF' import sys, yaml with open(sys.argv[1]) as fh: data = yaml.safe_load(fh) svc = data["services"]["comfyui"] assert svc.get("build"), "comfyui must have a build: section (Containerfile)" assert svc.get("image", "").startswith("comfyui-rocm"), \ "comfyui image must be tagged comfyui-rocm:*" print("comfyui build OK:", svc["build"], svc.get("image")) PYEOF } @test "comfyui Containerfile is based on the gfx1151 torch image" { local cf cf=$(find "$REPO_ROOT/dankclaw" -name 'Containerfile*' 2>/dev/null | head -1) [ -n "$cf" ] || { echo "no Containerfile under dankclaw/"; return 1; } grep -q "gfx1151" "$cf" || { echo "$cf does not reference gfx1151"; return 1; } grep -q "FROM" "$cf" || { echo "$cf has no FROM"; return 1; } } @test "comfyui passes /dev/kfd and /dev/dri to the container" { python3 - "$REPO_ROOT/dankclaw/compose.yaml" <<'PYEOF' import sys, yaml with open(sys.argv[1]) as fh: data = yaml.safe_load(fh) devices = data["services"]["comfyui"].get("devices", []) joined = "\n".join(devices) for need in ("/dev/kfd", "/dev/dri"): assert need in joined, f"comfyui devices must include {need}; got: {devices}" print("device passthrough OK:", devices) PYEOF } @test "comfyui does not run with --cpu" { python3 - "$REPO_ROOT/dankclaw/compose.yaml" <<'PYEOF' import sys, yaml with open(sys.argv[1]) as fh: data = yaml.safe_load(fh) cmd = data["services"]["comfyui"].get("command", []) joined = " ".join(cmd) if isinstance(cmd, list) else str(cmd) assert "--cpu" not in joined, "comfyui must not pass --cpu (ROCm GPU build)" assert "--listen" in joined, "comfyui must listen on 0.0.0.0" print("command OK:", cmd) PYEOF } @test "comfyui adds the video group (render is unmappable in rootless)" { python3 - "$REPO_ROOT/dankclaw/compose.yaml" <<'PYEOF' import sys, yaml with open(sys.argv[1]) as fh: data = yaml.safe_load(fh) groups = data["services"]["comfyui"].get("group_add", []) # 'video' is added (it resolves in the rootless namespace). 'render' (gid 990) # is intentionally ABSENT: rootless podman cannot map it into the container # namespace (the subgid range is 100000+), so --group-add render fails to start. # GPU access comes from the /dev/kfd + /dev/dri passthrough, checked host-side # (the run user is already a member of 'render' on the host). assert "video" in groups, f"comfyui group_add must include video; got: {groups}" assert "render" not in groups, f"comfyui group_add must NOT include render (unmappable in rootless); got: {groups}" print("groups OK:", groups) PYEOF }