From 89721fb67a338e1be53eea85d9134ad8fb9bc7c3 Mon Sep 17 00:00:00 2001 From: Michal Vavak Date: Mon, 28 Sep 2026 05:50:33 +0200 Subject: [PATCH 1/3] fix: install custom node dependencies in runtime venv --- .changeset/heavy-donuts-boil.md | 5 ++++ scripts/comfy-node-install.sh | 5 +++- tests/test_comfy_node_install.py | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 .changeset/heavy-donuts-boil.md create mode 100644 tests/test_comfy_node_install.py diff --git a/.changeset/heavy-donuts-boil.md b/.changeset/heavy-donuts-boil.md new file mode 100644 index 000000000..d22177f05 --- /dev/null +++ b/.changeset/heavy-donuts-boil.md @@ -0,0 +1,5 @@ +--- +"worker-comfyui": patch +--- + +install custom node dependencies into the ComfyUI runtime environment diff --git a/scripts/comfy-node-install.sh b/scripts/comfy-node-install.sh index e45e62f71..b6166eac4 100644 --- a/scripts/comfy-node-install.sh +++ b/scripts/comfy-node-install.sh @@ -10,6 +10,9 @@ if [[ $# -eq 0 ]]; then exit 64 # EX_USAGE fi +# ComfyUI runs with /opt/venv/bin/python; install custom-node dependencies there. +export VIRTUAL_ENV=/opt/venv + log=$(mktemp) # run installation – some modes return non-zero even on success, so we @@ -41,4 +44,4 @@ 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 \ No newline at end of file +exit 0 diff --git a/tests/test_comfy_node_install.py b/tests/test_comfy_node_install.py new file mode 100644 index 000000000..7259a32fa --- /dev/null +++ b/tests/test_comfy_node_install.py @@ -0,0 +1,45 @@ +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_custom_node_in_runtime_venv(self): + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + bin_dir = temp_path / "bin" + bin_dir.mkdir() + captured_venv = temp_path / "virtual-env" + 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) + + env = os.environ.copy() + env.pop("VIRTUAL_ENV", None) + env["CAPTURED_VENV"] = str(captured_venv) + env["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(), "/opt/venv") + + +if __name__ == "__main__": + unittest.main() From ade2cf2ea1e33d6c17d685fe64055ad5cb559874 Mon Sep 17 00:00:00 2001 From: Michal Vavak Date: Mon, 28 Sep 2026 10:20:55 +0200 Subject: [PATCH 2/3] fix: mirror custom node deps into worker venv --- scripts/comfy-node-install.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/comfy-node-install.sh b/scripts/comfy-node-install.sh index b6166eac4..109888863 100644 --- a/scripts/comfy-node-install.sh +++ b/scripts/comfy-node-install.sh @@ -10,15 +10,15 @@ if [[ $# -eq 0 ]]; then exit 64 # EX_USAGE fi -# ComfyUI runs with /opt/venv/bin/python; install custom-node dependencies there. -export VIRTUAL_ENV=/opt/venv +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 @@ -38,6 +38,13 @@ 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 From ab37a0b0cf10f80b8d1a5be54be1a0209b2355d6 Mon Sep 17 00:00:00 2001 From: Michal Vavak Date: Mon, 28 Sep 2026 10:21:16 +0200 Subject: [PATCH 3/3] test: verify custom node deps target runtime venv --- tests/test_comfy_node_install.py | 38 +++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/tests/test_comfy_node_install.py b/tests/test_comfy_node_install.py index 7259a32fa..dc4d92160 100644 --- a/tests/test_comfy_node_install.py +++ b/tests/test_comfy_node_install.py @@ -10,24 +10,44 @@ class TestComfyNodeInstall(unittest.TestCase): - def test_installs_custom_node_in_runtime_venv(self): + 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' + '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["CAPTURED_VENV"] = str(captured_venv) - env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}" + 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"], @@ -38,7 +58,15 @@ def test_installs_custom_node_in_runtime_venv(self): ) self.assertEqual(result.returncode, 0, result.stderr) - self.assertEqual(captured_venv.read_text(), "/opt/venv") + 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__":