Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/heavy-donuts-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"worker-comfyui": patch
---

install custom node dependencies into the ComfyUI runtime environment
18 changes: 14 additions & 4 deletions scripts/comfy-node-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ if [[ $# -eq 0 ]]; then
exit 64 # EX_USAGE
fi

custom_nodes_dir="${COMFYUI_CUSTOM_NODES_DIR:-/comfyui/custom_nodes}"
runtime_python="${COMFYUI_RUNTIME_PYTHON:-/opt/venv/bin/python}"

log=$(mktemp)

# run installation – some modes return non-zero even on success, so we
# ignore the exit status and rely on log parsing instead.
# Let comfy-cli use the ComfyUI workspace environment to install the node.
# The worker runtime uses /opt/venv, so mirror node requirements there below.
set +e
comfy node install --mode=remote "$@" 2>&1 | tee "$log"
env -u VIRTUAL_ENV comfy node install --mode=remote "$@" 2>&1 | tee "$log"
cli_status=$?
set -e

Expand All @@ -35,10 +38,17 @@ if [[ -n "$failed_nodes" ]]; then
exit 1
fi

for requirements_file in "$custom_nodes_dir"/*/requirements.txt; do
if [[ ! -f "$requirements_file" ]]; then
continue
fi
uv pip install --python "$runtime_python" -r "$requirements_file"
done

# If we reach here no failed nodes were detected. Warn if CLI exit status
# was non-zero but treat it as success.
if [[ $cli_status -ne 0 ]]; then
echo "Warning: comfy node install exited with status $cli_status but no errors were detected in the log — assuming success." >&2
fi

exit 0
exit 0
73 changes: 73 additions & 0 deletions tests/test_comfy_node_install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import os
from pathlib import Path
import subprocess
import tempfile
import unittest


_REPO_ROOT = Path(__file__).resolve().parents[1]
_INSTALL_SCRIPT = _REPO_ROOT / "scripts" / "comfy-node-install.sh"


class TestComfyNodeInstall(unittest.TestCase):
def test_installs_node_in_workspace_and_mirrors_requirements_to_runtime(self):
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
bin_dir = temp_path / "bin"
bin_dir.mkdir()
nodes_dir = temp_path / "custom_nodes"
node_dir = nodes_dir / "test-node"
node_dir.mkdir(parents=True)
requirements_file = node_dir / "requirements.txt"
requirements_file.write_text("test-dependency\n")

captured_venv = temp_path / "virtual-env"
captured_uv_args = temp_path / "uv-args"
comfy = bin_dir / "comfy"
comfy.write_text(
"#!/usr/bin/env bash\n"
'printf "%s" "${VIRTUAL_ENV-}" > "$CAPTURED_VENV"\n'
'echo "Installed test-node"\n'
)
comfy.chmod(0o755)
uv = bin_dir / "uv"
uv.write_text(
"#!/usr/bin/env bash\n"
'printf "%s\\n" "$*" >> "$CAPTURED_UV_ARGS"\n'
)
uv.chmod(0o755)

env = os.environ.copy()
env.pop("VIRTUAL_ENV", None)
env.update(
{
"CAPTURED_VENV": str(captured_venv),
"CAPTURED_UV_ARGS": str(captured_uv_args),
"COMFYUI_CUSTOM_NODES_DIR": str(nodes_dir),
"COMFYUI_RUNTIME_PYTHON": str(temp_path / "runtime" / "bin" / "python"),
"PATH": f"{bin_dir}{os.pathsep}{env['PATH']}",
}
)

result = subprocess.run(
["bash", str(_INSTALL_SCRIPT), "test-node"],
env=env,
capture_output=True,
text=True,
check=False,
)

self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(captured_venv.read_text(), "")
self.assertEqual(
captured_uv_args.read_text().splitlines(),
[
"pip install --python "
f"{temp_path / 'runtime' / 'bin' / 'python'} "
f"-r {requirements_file}"
],
)


if __name__ == "__main__":
unittest.main()