Skip to content

Commit 9f94c9f

Browse files
snomiaoclaude
andcommitted
[feat] Merge ComfyUI_devtools into ComfyUI_frontend
- Move devtools Python files to src/devtools/ - Update CI workflows to use integrated devtools - Update browser test documentation - Eliminate external repository dependency Fixes #4683 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 1e9de37 commit 9f94c9f

File tree

7 files changed

+816
-12
lines changed

7 files changed

+816
-12
lines changed

.github/workflows/i18n-custom-nodes.yaml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,10 @@ jobs:
3232
with:
3333
repository: Comfy-Org/ComfyUI_frontend
3434
path: ComfyUI_frontend
35-
- name: Checkout ComfyUI_devtools
36-
uses: actions/checkout@v4
37-
with:
38-
repository: Comfy-Org/ComfyUI_devtools
39-
path: ComfyUI/custom_nodes/ComfyUI_devtools
35+
- name: Copy ComfyUI_devtools from frontend repo
36+
run: |
37+
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
38+
cp -r ComfyUI_frontend/src/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
4039
- name: Checkout custom node repository
4140
uses: actions/checkout@v4
4241
with:

.github/workflows/test-ui.yaml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,10 @@ jobs:
3030
repository: 'Comfy-Org/ComfyUI_frontend'
3131
path: 'ComfyUI_frontend'
3232

33-
- name: Checkout ComfyUI_devtools
34-
uses: actions/checkout@v4
35-
with:
36-
repository: 'Comfy-Org/ComfyUI_devtools'
37-
path: 'ComfyUI/custom_nodes/ComfyUI_devtools'
38-
ref: 'd05fd48dd787a4192e16802d4244cfcc0e2f9684'
33+
- name: Copy ComfyUI_devtools from frontend repo
34+
run: |
35+
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
36+
cp -r ComfyUI_frontend/src/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
3937
4038
- name: Install pnpm
4139
uses: pnpm/action-setup@v4

browser_tests/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ Without this flag, parallel tests will conflict and fail randomly.
1616

1717
### ComfyUI devtools
1818

19-
Clone <https://github.com/Comfy-Org/ComfyUI_devtools> to your `custom_nodes` directory.
19+
ComfyUI_devtools is now included in this repository under `src/devtools/`. During CI/CD, these files are automatically copied to the `custom_nodes` directory.
2020
_ComfyUI_devtools adds additional API endpoints and nodes to ComfyUI for browser testing._
2121

22+
For local development, copy the devtools files to your ComfyUI installation:
23+
```bash
24+
cp -r src/devtools/* /path/to/your/ComfyUI/custom_nodes/ComfyUI_devtools/
25+
```
26+
2227
### Node.js & Playwright Prerequisites
2328

2429
Ensure you have Node.js v20 or v22 installed. Then, set up the Chromium test driver:

src/devtools/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# ComfyUI DevTools
2+
3+
This directory contains development tools and test utilities for ComfyUI, previously maintained as a separate repository at `https://github.com/Comfy-Org/ComfyUI_devtools`.
4+
5+
## Contents
6+
7+
- `__init__.py` - Server endpoints for development tools (`/api/devtools/*`)
8+
- `dev_nodes.py` - Development and testing nodes for ComfyUI
9+
- `fake_model.safetensors` - Test fixture for model loading tests
10+
11+
## Purpose
12+
13+
These tools provide:
14+
- Test endpoints for browser automation
15+
- Development nodes for testing various UI features
16+
- Mock data for consistent testing environments
17+
18+
## Usage
19+
20+
During CI/CD, these files are automatically copied to the ComfyUI `custom_nodes` directory. For local development, copy these files to your ComfyUI installation:
21+
22+
```bash
23+
cp -r src/devtools/* /path/to/your/ComfyUI/custom_nodes/ComfyUI_devtools/
24+
```
25+
26+
## Migration
27+
28+
This directory was created as part of issue #4683 to merge the ComfyUI_devtools repository into the main frontend repository, eliminating the need for separate versioning and simplifying the development workflow.

src/devtools/__init__.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from __future__ import annotations
2+
from .dev_nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
3+
4+
import os
5+
import shutil
6+
import json
7+
from typing import Union
8+
9+
import server
10+
from aiohttp import web
11+
from aiohttp.web_request import Request
12+
import folder_paths
13+
from folder_paths import models_dir
14+
15+
16+
@server.PromptServer.instance.routes.get("/devtools/fake_model.safetensors")
17+
async def fake_model(request: Request):
18+
file_path = os.path.join(os.path.dirname(__file__), "fake_model.safetensors")
19+
return web.FileResponse(file_path)
20+
21+
22+
@server.PromptServer.instance.routes.get("/devtools/cleanup_fake_model")
23+
async def cleanup_fake_model(request: Request):
24+
model_folder = request.query.get("model_folder", "clip")
25+
model_path = os.path.join(models_dir, model_folder, "fake_model.safetensors")
26+
if os.path.exists(model_path):
27+
os.remove(model_path)
28+
return web.Response(status=200, text="Fake model cleaned up")
29+
30+
31+
TreeType = dict[str, Union[str, "TreeType"]]
32+
33+
34+
def write_tree_structure(tree: TreeType, base_path: str):
35+
# Remove existing files and folders in users/workflows
36+
if os.path.exists(base_path):
37+
shutil.rmtree(base_path)
38+
39+
# Recreate the base directory
40+
os.makedirs(base_path, exist_ok=True)
41+
42+
def write_recursive(current_tree: TreeType, current_path: str):
43+
for key, value in current_tree.items():
44+
new_path = os.path.join(current_path, key)
45+
if isinstance(value, dict):
46+
# If it's a dictionary, create a new directory and recurse
47+
os.makedirs(new_path, exist_ok=True)
48+
write_recursive(value, new_path)
49+
else:
50+
# If it's a string, write the content to a file
51+
with open(new_path, "w") as f:
52+
f.write(value)
53+
54+
write_recursive(tree, base_path)
55+
56+
57+
@server.PromptServer.instance.routes.post("/devtools/setup_folder_structure")
58+
async def setup_folder_structure(request: Request):
59+
try:
60+
data = await request.json()
61+
tree_structure = data.get("tree_structure")
62+
base_path = os.path.join(
63+
folder_paths.base_path, data.get("base_path", "users/workflows")
64+
)
65+
66+
if not isinstance(tree_structure, dict):
67+
return web.Response(status=400, text="Invalid tree structure")
68+
69+
write_tree_structure(tree_structure, base_path)
70+
return web.Response(status=200, text=f"Folder structure created at {base_path}")
71+
except json.JSONDecodeError:
72+
return web.Response(status=400, text="Invalid JSON data")
73+
except Exception as e:
74+
return web.Response(status=500, text=f"Error: {str(e)}")
75+
76+
77+
@server.PromptServer.instance.routes.post("/devtools/set_settings")
78+
async def set_settings(request: Request):
79+
"""Directly set the settings for the user specified via `Comfy.userId`,
80+
instead of merging with the existing settings."""
81+
try:
82+
settings: dict[str, str | bool | int | float] = await request.json()
83+
user_root = folder_paths.get_user_directory()
84+
try:
85+
user_id: str = settings.pop("Comfy.userId")
86+
except KeyError:
87+
user_id = "default"
88+
settings_file_path = os.path.join(user_root, user_id, "comfy.settings.json")
89+
90+
# Ensure the directory structure exists
91+
os.makedirs(os.path.dirname(settings_file_path), exist_ok=True)
92+
93+
with open(settings_file_path, "w") as f:
94+
f.write(json.dumps(settings, indent=4))
95+
return web.Response(status=200)
96+
except Exception as e:
97+
return web.Response(status=500, text=f"Error: {str(e)}")
98+
99+
100+
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]

0 commit comments

Comments
 (0)