Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a571785
fix: use Comfy3D forks for geometry addons
michaelgold Jul 20, 2026
f035e4f
feat: install Auto-Rig Pro from build secret
michaelgold Jul 20, 2026
640b8b1
fix: avoid stale NVIDIA graphics mounts
michaelgold Jul 20, 2026
63596d4
fix: preserve builder cache for nvidia capability change
michaelgold Jul 20, 2026
f84d4e7
fix: move Auto-Rig Pro bundle into UniRig
michaelgold Jul 20, 2026
c11110d
fix: pin UniRig addon revision
michaelgold Jul 20, 2026
8d52b71
fix: harden ComfyUI version check
michaelgold Jul 20, 2026
4993afa
fix: capture ComfyUI stderr in startup check
michaelgold Jul 20, 2026
f46489a
fix: test Remesher workflow node coverage
michaelgold Jul 20, 2026
82937ec
test: run Remesher avatar smoke in Docker CI
michaelgold Jul 20, 2026
80896e6
test: cache Remesher smoke models in CI
michaelgold Jul 20, 2026
7851d7c
test: pin Remesher CLI smoke version
michaelgold Jul 20, 2026
0ef6ce7
test: cache DINOv3 for Remesher smoke
michaelgold Jul 20, 2026
0ef1dd1
fix: include compiler for Triton runtime JIT
michaelgold Jul 20, 2026
c448710
test: cache MIA models for rigging smoke
michaelgold Jul 20, 2026
3dcd9aa
test: free ComfyUI memory between smoke stages
michaelgold Jul 20, 2026
7cff00c
test: use auto precision for MIA smoke
michaelgold Jul 21, 2026
9767993
test: run MIA smoke with fp16 precision
michaelgold Jul 21, 2026
019920c
test: run MIA smoke with sdpa attention
michaelgold Jul 21, 2026
d3a7082
test: cap MIA smoke mesh face count
michaelgold Jul 21, 2026
8e423f8
test: disable MIA FBX texture embedding in smoke
michaelgold Jul 21, 2026
e3437ba
ci: clean self-hosted runner disk before build
michaelgold Jul 21, 2026
6ed64b7
ci: reuse cached Comfy3D images for smoke
michaelgold Jul 21, 2026
f148612
test: use direct GLB MIA rigging smoke
michaelgold Jul 21, 2026
824d1f3
test: use isolated MIA GLB export worker
michaelgold Jul 21, 2026
6ec3e89
test: use scale-baked MIA GLB export
michaelgold Jul 21, 2026
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
127 changes: 122 additions & 5 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Report self-hosted runner disk
run: |
df -h / /home "$RUNNER_WORKSPACE" || true
docker system df || true
- name: Build Docker image
run: |
if [[ -z "${{ needs.check-comfyui-version.outputs.version_to_use }}" ]]; then
Expand All @@ -99,6 +103,12 @@ jobs:
echo "final_update_version is empty" >&2
exit 1
fi
IMAGE="michaelgold/comfy3d:${{ needs.check-comfyui-version.outputs.final_update_version }}-${{ matrix.cuda.tag_suffix }}"
if docker image inspect "$IMAGE" >/dev/null 2>&1; then
echo "Using cached local Docker image: $IMAGE"
exit 0
fi
echo "Cached Docker image not found; building: $IMAGE"
docker build \
--build-arg VERSION=${{ needs.check-comfyui-version.outputs.version_to_use }} \
--build-arg UPDATE_VERSION=${{ needs.check-comfyui-version.outputs.final_update_version }} \
Expand All @@ -108,23 +118,130 @@ jobs:
--build-arg CUMM_CUDA_VERSION=${{ matrix.cuda.cumm_cuda_version }} \
--build-arg CUDA_PACKAGE_VERSION=${{ matrix.cuda.cuda_package_version }} \
--build-arg CUDA_ARCH_LIST="${{ matrix.cuda.cuda_arch_list }}" \
-t michaelgold/comfy3d:${{ needs.check-comfyui-version.outputs.final_update_version }}-${{ matrix.cuda.tag_suffix }} \
-t "$IMAGE" \
-f Docker/Dockerfile .
- name: Test container
run: |
cleanup() {
status=$?
if [ "$status" -ne 0 ]; then
echo "::group::comfy-test diagnostics"
docker ps -a --filter name=comfy-test --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" || true
docker logs --tail 500 comfy-test || true
nvidia-smi || true
echo "::endgroup::"
fi
docker rm -f comfy-test || true
exit "$status"
}
trap cleanup EXIT
free_comfy_memory() {
curl -sS -X POST http://127.0.0.1:8234/free \
-H 'Content-Type: application/json' \
-d '{"unload_models": true, "free_memory": true}' || true
sleep 5
}
# Remove existing container if it exists
docker rm -f comfy-test || true
docker run -d -p 8234:8188 --name comfy-test --runtime=nvidia --gpus all michaelgold/comfy3d:${{ needs.check-comfyui-version.outputs.final_update_version }}-${{ matrix.cuda.tag_suffix }}
rm -rf /tmp/comfy3d-unirig-override
git clone --depth 1 https://github.com/michaelgold/ComfyUI-UniRig.git /tmp/comfy3d-unirig-override
git -C /tmp/comfy3d-unirig-override fetch --depth 1 origin c68895bd9f4ad44451bec335be97f0b98ccddabd
git -C /tmp/comfy3d-unirig-override checkout c68895bd9f4ad44451bec335be97f0b98ccddabd
mkdir -p "$HOME/.cache/comfy3d-models"
docker run -d -p 8234:8188 --name comfy-test --runtime=nvidia --gpus all \
-v "$HOME/.cache/comfy3d-models:/app/comfy/models" \
-v "/tmp/comfy3d-unirig-override:/app/comfy/custom_nodes/ComfyUI-UniRig:ro" \
michaelgold/comfy3d:${{ needs.check-comfyui-version.outputs.final_update_version }}-${{ matrix.cuda.tag_suffix }}
sleep 150
docker logs comfy-test > logs.txt
docker logs comfy-test > logs.txt 2>&1
cat logs.txt
if grep -q "0.0.0.0" logs.txt; then
echo "✅ ComfyUI server started"
else
echo "❌ ComfyUI server did not start" && exit 1
fi
docker stop comfy-test
docker rm comfy-test
rm -rf /tmp/remesher-workflows /tmp/remesher-smoke
git clone --depth 1 https://github.com/remesher/remesher.git /tmp/remesher-workflows
git -C /tmp/remesher-workflows fetch --depth 1 origin 2d37cbbe63151327965fbd0db569d1e63c7bfb64
git -C /tmp/remesher-workflows checkout 2d37cbbe63151327965fbd0db569d1e63c7bfb64
python utils/check_remesher_workflows.py /tmp/remesher-workflows/examples --server http://127.0.0.1:8234
if [[ "${{ matrix.cuda.tag_suffix }}" == "cuda13.0" ]]; then
cd /tmp/remesher-workflows
printf '{"server_url":"http://127.0.0.1:8234"}\n' > config.json
mkdir -p /tmp/remesher-smoke/images /tmp/remesher-smoke/glb /tmp/remesher-smoke/rigged
python3 -m venv /tmp/remesher-smoke-venv
/tmp/remesher-smoke-venv/bin/python -m pip install --upgrade pip
/tmp/remesher-smoke-venv/bin/python -m pip install /tmp/remesher-workflows
docker exec -e HF_TOKEN="${{ secrets.HF_TOKEN }}" comfy-test bash -lc '
set -euo pipefail
download_model() {
url="$1"
dest="$2"
mkdir -p "$(dirname "$dest")"
if [ -s "$dest" ]; then
echo "Model already present: $dest"
return 0
fi
echo "Downloading model: $url -> $dest"
tmp="${dest}.tmp"
rm -f "$tmp"
auth_args=()
if [ -n "${HF_TOKEN:-}" ]; then
auth_args=(-H "Authorization: Bearer ${HF_TOKEN}")
fi
curl -L --fail --retry 5 --retry-delay 10 --connect-timeout 30 "${auth_args[@]}" -o "$tmp" "$url"
test -s "$tmp"
mv "$tmp" "$dest"
}
download_model "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_2512_fp8_e4m3fn.safetensors" "/app/comfy/models/diffusion_models/qwen_image_2512_fp8_e4m3fn.safetensors"
download_model "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors" "/app/comfy/models/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors"
download_model "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/vae/qwen_image_vae.safetensors" "/app/comfy/models/vae/qwen_image_vae.safetensors"
download_model "https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V1.0.safetensors" "/app/comfy/models/loras/Qwen-Image-Lightning-4steps-V1.0.safetensors"
download_model "https://huggingface.co/facebook/dinov3-vitl16-pretrain-lvd1689m/resolve/main/config.json" "/app/comfy/models/facebook/dinov3-vitl16-pretrain-lvd1689m/config.json"
download_model "https://huggingface.co/facebook/dinov3-vitl16-pretrain-lvd1689m/resolve/main/preprocessor_config.json" "/app/comfy/models/facebook/dinov3-vitl16-pretrain-lvd1689m/preprocessor_config.json"
download_model "https://huggingface.co/facebook/dinov3-vitl16-pretrain-lvd1689m/resolve/main/model.safetensors" "/app/comfy/models/facebook/dinov3-vitl16-pretrain-lvd1689m/model.safetensors"
download_model "https://huggingface.co/jasongzy/Make-It-Animatable/resolve/main/output/best/new/bw.pth" "/app/comfy/models/mia/bw.pth"
download_model "https://huggingface.co/jasongzy/Make-It-Animatable/resolve/main/output/best/new/bw_normal.pth" "/app/comfy/models/mia/bw_normal.pth"
download_model "https://huggingface.co/jasongzy/Make-It-Animatable/resolve/main/output/best/new/joints.pth" "/app/comfy/models/mia/joints.pth"
download_model "https://huggingface.co/jasongzy/Make-It-Animatable/resolve/main/output/best/new/joints_coarse.pth" "/app/comfy/models/mia/joints_coarse.pth"
download_model "https://huggingface.co/jasongzy/Make-It-Animatable/resolve/main/output/best/new/pose.pth" "/app/comfy/models/mia/pose.pth"
'
/tmp/remesher-smoke-venv/bin/comfy-prompt-cli text-to-image \
--prompt "cute 3d robot character in a clean t-pose, arms straight out horizontally, front view, centered, full body, neutral background, toy-like rounded shapes" \
--seed 12345 \
--filename-prefix ci_smoke_robot_tpose \
--timeout 3600 \
--out-dir /tmp/remesher-smoke/images
SMOKE_IMAGE="$(find /tmp/remesher-smoke/images -type f \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.webp' \) | sort | head -n 1)"
test -s "$SMOKE_IMAGE"
echo "Remesher smoke image: $SMOKE_IMAGE"
free_comfy_memory
/tmp/remesher-smoke-venv/bin/comfy-prompt-cli image-to-glb \
--image "$SMOKE_IMAGE" \
--mesh-seed 23456 \
--target-face-num 50000 \
--filename-prefix ci_smoke_robot_glb \
--texture-seed 34567 \
--timeout 3600 \
--out-dir /tmp/remesher-smoke/glb
SMOKE_GLB="$(find /tmp/remesher-smoke/glb -type f -iname '*.glb' | sort | head -n 1)"
test -s "$SMOKE_GLB"
echo "Remesher smoke GLB: $SMOKE_GLB"
free_comfy_memory
/tmp/remesher-smoke-venv/bin/comfy-prompt-cli rig-glb \
--mesh "$SMOKE_GLB" \
--glb-name ci_smoke_robot_rigged \
--precision fp16 \
--attn-backend sdpa \
--target-face-count 50000 \
--no-embed-textures \
--timeout 3600 \
--out-dir /tmp/remesher-smoke/rigged
SMOKE_RIGGED_GLB="$(find /tmp/remesher-smoke/rigged -type f -iname '*.glb' | sort | head -n 1)"
test -s "$SMOKE_RIGGED_GLB"
echo "Remesher smoke rigged GLB: $SMOKE_RIGGED_GLB"
cd "$GITHUB_WORKSPACE"
fi

push:
needs: [check-comfyui-version, build-and-test]
Expand Down
9 changes: 5 additions & 4 deletions Docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,9 @@ RUN python /app/utils/node_install.py https://github.com/kijai/ComfyUI-Hunyuan3D
RUN python /app/utils/node_install.py https://github.com/calcuis/gguf --version e802c7831507af49eefa40324eb78378eec4f3cf


RUN python /app/utils/node_install.py https://github.com/PozzettiAndrea/ComfyUI-UniRig --version 30503fb0bf591f4c4d11f1e61580bd66ecdda8cd --no-build-isolation
RUN python /app/utils/node_install.py https://github.com/michaelgold/ComfyUI-UniRig --version c68895bd9f4ad44451bec335be97f0b98ccddabd --no-build-isolation

RUN python /app/utils/node_install.py https://github.com/PozzettiAndrea/ComfyUI-GeometryPack --version 3cb49fbd7ba911c10a72fc767926a029f97798ab --no-build-isolation
RUN python /app/utils/node_install.py https://github.com/michaelgold/ComfyUI-GeometryPack --version 8229aa65ec026b4b87802e4ac3b28022c8dd628b --no-build-isolation

RUN python /app/utils/node_install.py https://github.com/visualbruno/ComfyUI-Trellis2 --version 3c7e46b265c1e28f523889ad24d65708cdeee7f8 --no-build-isolation

Expand Down Expand Up @@ -340,7 +340,6 @@ RUN uv pip install diffusers==0.34.0 transformers==4.56.0
RUN uv pip install --extra-index-url https://michaelgold.github.io/buildbpy/ bpy==5.0.1



# ---------- Stage 5: final runtime ----------
ARG CUDA_PACKAGE_VERSION
FROM nvidia/cuda:${CUDA_RUNTIME_TAG} AS final
Expand All @@ -349,6 +348,7 @@ ARG CUDA_PACKAGE_VERSION
RUN apt-get update && apt-get install -y --allow-change-held-packages --no-install-recommends \
libgl1 libegl1 libopengl0 libglu1-mesa \
libcusparse-${CUDA_PACKAGE_VERSION} libcublas-${CUDA_PACKAGE_VERSION} libcusolver-${CUDA_PACKAGE_VERSION} \
gcc g++ \
wget curl git \
libglib2.0-0 libglib2.0-dev libgthread-2.0-0 \
libtbbmalloc2 \
Expand Down Expand Up @@ -381,11 +381,12 @@ ENV PATH="$VIRTUAL_ENV/bin:$PATH"
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility,graphics
ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility
ENV PYOPENGL_PLATFORM=egl
ENV FORCE_CUDA="1"
ENV CUDA_HOME="/usr/local/cuda"
ENV LD_LIBRARY_PATH="/usr/local/lib:/usr/lib/x86_64-linux-gnu:/usr/local/cuda/lib64:/usr/local/cuda/targets/x86_64-linux/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"

# Prevent spconv from switching algorithms at runtime

ARG CUDA_ARCH_LIST
Expand Down
82 changes: 60 additions & 22 deletions utils/check_comfyui_latest.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,100 @@
import requests
import json
import os
import sys
import urllib.error
import urllib.request


GITHUB_API = "https://api.github.com/repos/comfyanonymous/ComfyUI"


def github_json(path):
token = os.getenv("GITHUB_TOKEN")
headers = {
"Accept": "application/vnd.github+json",
"User-Agent": "comfy3d-check-comfyui-latest",
"X-GitHub-Api-Version": "2022-11-28",
}
if token:
headers["Authorization"] = f"Bearer {token}"

request = urllib.request.Request(f"{GITHUB_API}/{path}", headers=headers)
try:
with urllib.request.urlopen(request, timeout=30) as response:
return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"GitHub API request failed for {path}: HTTP {exc.code}: {body}") from exc
except urllib.error.URLError as exc:
raise RuntimeError(f"GitHub API request failed for {path}: {exc}") from exc


def get_latest_tag():
response = requests.get('https://api.github.com/repos/comfyanonymous/ComfyUI/tags')
tags = response.json()
return tags[0]['name'] if tags else None
tags = github_json("tags")
if not isinstance(tags, list):
raise RuntimeError(f"GitHub tags response was not a list: {tags!r}")
return tags[0].get("name") if tags else None


def get_latest_commit():
response = requests.get('https://api.github.com/repos/comfyanonymous/ComfyUI/commits/master')
commit = response.json()
return commit['sha'] if commit else None
commit = github_json("commits/master")
if not isinstance(commit, dict):
raise RuntimeError(f"GitHub commit response was not an object: {commit!r}")
return commit.get("sha")


def read_version_info(file_path):
if os.path.exists(file_path):
with open(file_path, 'r') as file:
with open(file_path, "r") as file:
return json.load(file)
return {"base_version": "", "update_version": "", "previous_commit": ""}


def write_version_info(file_path, base_version, update_version, commit):
with open(file_path, 'w') as file:
with open(file_path, "w") as file:
json.dump({
"base_version": base_version,
"update_version": update_version,
"previous_commit": commit
"base_version": base_version,
"update_version": update_version,
"previous_commit": commit,
}, file)


def main():
file_path = 'utils/comfyui_version_info.json'
file_path = "utils/comfyui_version_info.json"
version_info = read_version_info(file_path)

latest_tag = get_latest_tag()
latest_commit = get_latest_commit()

# Check if we have a new tag (update_version changes)
new_tag = latest_tag != version_info.get('update_version')
new_commit = latest_commit != version_info.get('previous_commit')
new_tag = latest_tag != version_info.get("update_version")
new_commit = latest_commit != version_info.get("previous_commit")

if new_tag or new_commit:
# Keep the existing base_version unless it's empty
base_version = version_info.get('base_version') or latest_tag
base_version = version_info.get("base_version") or latest_tag
write_version_info(file_path, base_version, latest_tag, latest_commit)

# Write outputs to $GITHUB_OUTPUT
github_output = os.getenv('GITHUB_OUTPUT')
github_output = os.getenv("GITHUB_OUTPUT")
if github_output:
# Resolve versions from GitHub data (tag preferred, then commit)
git_version = latest_tag or latest_commit or ''
base_version = version_info.get('base_version') or git_version
git_version = latest_tag or latest_commit or ""
base_version = version_info.get("base_version") or git_version
update_version = git_version
with open(github_output, 'a') as output_file:

with open(github_output, "a") as output_file:
output_file.write(f"new_tag={str(new_tag).lower()}\n")
output_file.write(f"new_commit={str(new_commit).lower()}\n")
output_file.write(f"latest_tag={latest_tag or ''}\n")
output_file.write(f"latest_commit={latest_commit or ''}\n")
output_file.write(f"base_version={base_version}\n")
output_file.write(f"update_version={update_version}\n")


if __name__ == "__main__":
main()
try:
main()
except Exception as exc:
print(f"check_comfyui_latest.py failed: {exc}", file=sys.stderr)
raise
Loading
Loading