Files
ComfyUI/custom_nodes/rgthree-comfy/__build__.py
jaidaken f09734b0ee
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Add custom nodes, Civitai loras (LFS), and vast.ai setup script
Includes 30 custom nodes committed directly, 7 Civitai-exclusive
loras stored via Git LFS, and a setup script that installs all
dependencies and downloads HuggingFace-hosted models on vast.ai.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 00:56:42 +00:00

152 lines
6.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
import subprocess
import os
from shutil import rmtree, copytree, ignore_patterns
from glob import glob
import time
import re
import argparse
from py.log import COLORS
from py.config import RGTHREE_CONFIG
step_msg = ''
step_start = 0
step_infos = []
def log_step(msg=None, status=None):
""" Logs a step keeping track of timing and initial msg. """
global step_msg # pylint: disable=W0601
global step_start # pylint: disable=W0601
global step_infos # pylint: disable=W0601
if msg:
tag = f'{COLORS["YELLOW"]}[ Notice ]' if status == 'Notice' else f'{COLORS["RESET"]}[Starting]'
step_msg = f'{tag}{COLORS["RESET"]} {msg}...'
step_start = time.time()
step_infos = []
print(step_msg, end="\r")
elif status:
if status != 'Error':
warnings = [w for w in step_infos if w["type"] == 'warn']
status = "Warn" if warnings else status
step_time = round(time.time() - step_start, 3)
if status == 'Error':
status_msg = f'{COLORS["RED"]} {status}{COLORS["RESET"]}'
elif status == 'Warn':
status_msg = f'{COLORS["YELLOW"]}! {status}{COLORS["RESET"]}'
else:
status_msg = f'{COLORS["BRIGHT_GREEN"]}🗸 {status}{COLORS["RESET"]}'
print(f'{step_msg.ljust(64, ".")} {status_msg} ({step_time}s)')
for info in step_infos:
print(info["msg"])
def log_step_info(msg:str, status='info'):
global step_infos # pylint: disable=W0601
step_infos.append({"msg": f' - {msg}', "type": status})
def build(without_tests = True, fix = False):
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
DIR_SRC_WEB = os.path.abspath(f'{THIS_DIR}/src_web/')
DIR_WEB = os.path.abspath(f'{THIS_DIR}/web/')
DIR_WEB_COMFYUI = os.path.abspath(f'{DIR_WEB}/comfyui/')
if fix:
tss = glob(os.path.join(DIR_SRC_WEB, "**", "*.ts"), recursive=True)
log_step(msg=f'Fixing {len(tss)} ts files')
for ts in tss:
with open(ts, 'r', encoding="utf-8") as f:
content = f.read()
# (\s*from\s*['"](?!.*[.]js['"]).*?)(['"];) in vscode.
content, n = re.subn(r'(\s*from [\'"](?!.*[.]js[\'"]).*?)([\'"];)', '\\1.js\\2', content)
if n > 0:
filename = os.path.basename(ts)
log_step_info(
f'{filename} has {n} import{"s" if n > 1 else ""} that do not end in ".js"', 'warn')
with open(ts, 'w', encoding="utf-8") as f:
f.write(content)
log_step(status="Done")
log_step(msg='Copying web directory')
rmtree(DIR_WEB)
copytree(DIR_SRC_WEB, DIR_WEB, ignore=ignore_patterns("typings*", "*.ts", "*.scss"))
log_step(status="Done")
ts_version_result = subprocess.run(["node", "./node_modules/typescript/bin/tsc", "-v"],
capture_output=True,
text=True,
check=True)
ts_version = re.sub(r'^.*Version\s*([\d\.]+).*', 'v\\1', ts_version_result.stdout, flags=re.DOTALL)
log_step(msg=f'TypeScript ({ts_version})')
checked = subprocess.run(["node", "./node_modules/typescript/bin/tsc"], check=True)
log_step(status="Done")
if not without_tests:
log_step(msg='Removing directories (KEEPING TESTING)', status="Notice")
else:
log_step(msg='Removing unneeded directories')
test_path = os.path.join(DIR_WEB, 'comfyui', 'tests')
if os.path.exists(test_path):
rmtree(test_path)
rmtree(os.path.join(DIR_WEB, 'comfyui', 'testing'))
# Always remove the dummy scripts_comfy directory
rmtree(os.path.join(DIR_WEB, 'scripts_comfy'))
log_step(status="Done")
scsss = glob(os.path.join(DIR_SRC_WEB, "**", "*.scss"), recursive=True)
log_step(msg=f'SASS for {len(scsss)} files')
scsss = [i.replace(THIS_DIR, '.') for i in scsss]
cmds = ["node", "./node_modules/sass/sass"]
for scss in scsss:
out = scss.replace('src_web', 'web').replace('.scss', '.css')
cmds.append(f'{scss}:{out}')
cmds.append('--no-source-map')
checked = subprocess.run(cmds, check=True)
log_step(status="Done")
# Handle the common directories. Because ComfyUI loads under /extensions/rgthree-comfy we can't
# easily share sources outside of the `DIR_WEB_COMFYUI` _and_ allow typescript to resolve them in
# src view, so we set the path in the tsconfig to map an import of "rgthree/common" to the
# "src_web/common" directory, but then need to rewrite the comfyui JS files to load from
# "../../rgthree/common" (which we map correctly in rgthree_server.py).
log_step(msg='Cleaning Imports')
js_files = glob(os.path.join(DIR_WEB, '**', '*.js'), recursive=True)
for file in js_files:
rel_path = file.replace(f'{DIR_WEB}/', "")
with open(file, 'r', encoding="utf-8") as f:
filedata = f.read()
num = rel_path.count(os.sep)
if rel_path.startswith('comfyui'):
filedata = re.sub(r'(from\s+["\'])rgthree/', f'\\1{"../" * (num + 1)}rgthree/', filedata)
filedata = re.sub(r'(from\s+["\'])scripts/', f'\\1{"../" * (num + 1)}scripts/', filedata)
# Dynamic Imports
filedata = re.sub(r'(import\(["\'])rgthree/', f'\\1{"../" * (num + 1)}rgthree/', filedata)
else:
filedata = re.sub(r'(from\s+["\'])rgthree/', f'\\1{"../" * num}', filedata)
filedata = re.sub(r'(from\s+["\'])scripts/', f'\\1{"../" * (num + 1)}scripts/', filedata)
# Dynamic Imports
filedata = re.sub(r'(import\(["\'])rgthree/', f'\\1{"../" * num}', filedata)
filedata, n = re.subn(r'(\s*from [\'"](?!.*[.]js[\'"]).*?)([\'"];)', '\\1.js\\2', filedata)
if n > 0:
filename = file.split('rgthree-comfy')[1]
log_step_info(
f'{filename} has {n} import{"s" if n > 1 else ""} that do not end in ".js"', 'warn')
with open(file, 'w', encoding="utf-8") as f:
f.write(filedata)
log_step(status="Done")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--no-tests", default=False, action="store_true")
parser.add_argument("-f", "--fix", default=False, action="store_true")
args = parser.parse_args()
start = time.time()
build(without_tests=args.no_tests, fix=args.fix)
print(f'Finished all in {round(time.time() - start, 3)}s')