comfy-env 0.0.64__py3-none-any.whl → 0.0.66__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- comfy_env/__init__.py +70 -122
- comfy_env/cli.py +78 -7
- comfy_env/config/__init__.py +19 -0
- comfy_env/config/parser.py +151 -0
- comfy_env/config/types.py +64 -0
- comfy_env/install.py +83 -361
- comfy_env/isolation/__init__.py +9 -0
- comfy_env/isolation/wrap.py +351 -0
- comfy_env/nodes.py +2 -2
- comfy_env/pixi/__init__.py +48 -0
- comfy_env/pixi/core.py +356 -0
- comfy_env/{resolver.py → pixi/resolver.py} +1 -14
- comfy_env/prestartup.py +60 -0
- comfy_env/templates/comfy-env-instructions.txt +30 -87
- comfy_env/templates/comfy-env.toml +68 -136
- comfy_env/workers/__init__.py +21 -32
- comfy_env/workers/base.py +1 -1
- comfy_env/workers/{torch_mp.py → mp.py} +47 -14
- comfy_env/workers/{venv.py → subprocess.py} +405 -441
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/METADATA +2 -1
- comfy_env-0.0.66.dist-info/RECORD +34 -0
- comfy_env/decorator.py +0 -700
- comfy_env/env/__init__.py +0 -47
- comfy_env/env/config.py +0 -201
- comfy_env/env/config_file.py +0 -740
- comfy_env/env/manager.py +0 -636
- comfy_env/env/security.py +0 -267
- comfy_env/ipc/__init__.py +0 -55
- comfy_env/ipc/bridge.py +0 -476
- comfy_env/ipc/protocol.py +0 -265
- comfy_env/ipc/tensor.py +0 -371
- comfy_env/ipc/torch_bridge.py +0 -401
- comfy_env/ipc/transport.py +0 -318
- comfy_env/ipc/worker.py +0 -221
- comfy_env/isolation.py +0 -310
- comfy_env/pixi.py +0 -760
- comfy_env/stub_imports.py +0 -270
- comfy_env/stubs/__init__.py +0 -1
- comfy_env/stubs/comfy/__init__.py +0 -6
- comfy_env/stubs/comfy/model_management.py +0 -58
- comfy_env/stubs/comfy/utils.py +0 -29
- comfy_env/stubs/folder_paths.py +0 -71
- comfy_env/workers/pool.py +0 -241
- comfy_env-0.0.64.dist-info/RECORD +0 -48
- /comfy_env/{env/cuda_gpu_detection.py → pixi/cuda_detection.py} +0 -0
- /comfy_env/{env → pixi}/platform/__init__.py +0 -0
- /comfy_env/{env → pixi}/platform/base.py +0 -0
- /comfy_env/{env → pixi}/platform/darwin.py +0 -0
- /comfy_env/{env → pixi}/platform/linux.py +0 -0
- /comfy_env/{env → pixi}/platform/windows.py +0 -0
- /comfy_env/{registry.py → pixi/registry.py} +0 -0
- /comfy_env/{wheel_sources.yml → pixi/wheel_sources.yml} +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/WHEEL +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/entry_points.txt +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/licenses/LICENSE +0 -0
comfy_env/install.py
CHANGED
|
@@ -1,137 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Installation API for comfy-env.
|
|
3
3
|
|
|
4
|
-
This module provides the main `install()` function that handles:
|
|
5
|
-
- Named environments [envname] -> pixi (isolated Python environment)
|
|
6
|
-
- Local packages [local] -> uv/pip (in-place to current Python)
|
|
7
|
-
|
|
8
4
|
Example:
|
|
9
5
|
from comfy_env import install
|
|
10
|
-
|
|
11
|
-
# Auto-discovers config and installs
|
|
12
|
-
install()
|
|
13
|
-
|
|
14
|
-
# With explicit config path
|
|
15
|
-
install(config="comfy-env.toml")
|
|
6
|
+
install() # Auto-discovers comfy-env.toml and installs
|
|
16
7
|
"""
|
|
17
8
|
|
|
18
9
|
import inspect
|
|
19
|
-
import shutil
|
|
20
|
-
import subprocess
|
|
21
|
-
import sys
|
|
22
10
|
from pathlib import Path
|
|
23
|
-
from typing import Callable,
|
|
24
|
-
|
|
25
|
-
from .env.config import IsolatedEnv, LocalConfig, NodeReq, SystemConfig
|
|
26
|
-
from .env.config_file import load_config, discover_config
|
|
27
|
-
from .errors import CUDANotFoundError, InstallError
|
|
28
|
-
from .pixi import pixi_install
|
|
29
|
-
from .registry import PACKAGE_REGISTRY, get_cuda_short2
|
|
30
|
-
from .resolver import RuntimeEnv, parse_wheel_requirement
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def _install_system_packages(
|
|
34
|
-
system_config: SystemConfig,
|
|
35
|
-
log: Callable[[str], None],
|
|
36
|
-
dry_run: bool = False,
|
|
37
|
-
) -> bool:
|
|
38
|
-
"""
|
|
39
|
-
Install system-level packages (apt, brew, etc.).
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
system_config: SystemConfig with package lists per OS.
|
|
43
|
-
log: Logging callback.
|
|
44
|
-
dry_run: If True, show what would be installed without installing.
|
|
11
|
+
from typing import Callable, List, Optional, Set, Union
|
|
45
12
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"""
|
|
49
|
-
platform = sys.platform
|
|
50
|
-
|
|
51
|
-
if platform.startswith("linux"):
|
|
52
|
-
packages = system_config.linux
|
|
53
|
-
if not packages:
|
|
54
|
-
return True
|
|
55
|
-
|
|
56
|
-
log(f"Installing {len(packages)} system package(s) via apt...")
|
|
57
|
-
|
|
58
|
-
if dry_run:
|
|
59
|
-
log(f" Would install: {', '.join(packages)}")
|
|
60
|
-
return True
|
|
61
|
-
|
|
62
|
-
if not shutil.which("apt-get"):
|
|
63
|
-
log(" Warning: apt-get not found. Cannot install system packages.")
|
|
64
|
-
log(f" Please install manually: {', '.join(packages)}")
|
|
65
|
-
return True
|
|
66
|
-
|
|
67
|
-
sudo_available = shutil.which("sudo") is not None
|
|
68
|
-
|
|
69
|
-
try:
|
|
70
|
-
if sudo_available:
|
|
71
|
-
log(" Running apt-get update...")
|
|
72
|
-
subprocess.run(["sudo", "apt-get", "update"], capture_output=True, text=True)
|
|
73
|
-
|
|
74
|
-
log(f" Installing: {', '.join(packages)}")
|
|
75
|
-
install_result = subprocess.run(
|
|
76
|
-
["sudo", "apt-get", "install", "-y"] + packages,
|
|
77
|
-
capture_output=True,
|
|
78
|
-
text=True,
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
if install_result.returncode != 0:
|
|
82
|
-
log(f" Warning: apt-get install failed: {install_result.stderr.strip()}")
|
|
83
|
-
log(f" Please install manually: sudo apt-get install {' '.join(packages)}")
|
|
84
|
-
else:
|
|
85
|
-
log(" System packages installed successfully.")
|
|
86
|
-
else:
|
|
87
|
-
log(" Warning: sudo not available.")
|
|
88
|
-
log(f" Please install manually: sudo apt-get install {' '.join(packages)}")
|
|
89
|
-
|
|
90
|
-
except Exception as e:
|
|
91
|
-
log(f" Warning: Failed to install system packages: {e}")
|
|
92
|
-
log(f" Please install manually: sudo apt-get install {' '.join(packages)}")
|
|
93
|
-
|
|
94
|
-
return True
|
|
95
|
-
|
|
96
|
-
elif platform == "darwin":
|
|
97
|
-
packages = system_config.darwin
|
|
98
|
-
if packages:
|
|
99
|
-
log(f"System packages for macOS: {', '.join(packages)}")
|
|
100
|
-
log(f" Please install manually: brew install {' '.join(packages)}")
|
|
101
|
-
return True
|
|
102
|
-
|
|
103
|
-
elif platform == "win32":
|
|
104
|
-
packages = system_config.windows
|
|
105
|
-
if packages:
|
|
106
|
-
log(f"System packages for Windows: {', '.join(packages)}")
|
|
107
|
-
log(" Please install manually.")
|
|
108
|
-
return True
|
|
109
|
-
|
|
110
|
-
return True
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def _install_node_dependencies(
|
|
114
|
-
node_reqs: List[NodeReq],
|
|
115
|
-
node_dir: Path,
|
|
116
|
-
log: Callable[[str], None],
|
|
117
|
-
dry_run: bool = False,
|
|
118
|
-
) -> bool:
|
|
119
|
-
"""Install node dependencies (other ComfyUI custom nodes)."""
|
|
120
|
-
from .nodes import install_node_deps
|
|
121
|
-
|
|
122
|
-
custom_nodes_dir = node_dir.parent
|
|
123
|
-
log(f"\nInstalling {len(node_reqs)} node dependencies...")
|
|
124
|
-
|
|
125
|
-
if dry_run:
|
|
126
|
-
for req in node_reqs:
|
|
127
|
-
node_path = custom_nodes_dir / req.name
|
|
128
|
-
status = "exists" if node_path.exists() else "would clone"
|
|
129
|
-
log(f" {req.name}: {status}")
|
|
130
|
-
return True
|
|
131
|
-
|
|
132
|
-
visited: Set[str] = {node_dir.name}
|
|
133
|
-
install_node_deps(node_reqs, custom_nodes_dir, log, visited)
|
|
134
|
-
return True
|
|
13
|
+
from .config.types import ComfyEnvConfig, NodeReq
|
|
14
|
+
from .config.parser import load_config, discover_config
|
|
135
15
|
|
|
136
16
|
|
|
137
17
|
def install(
|
|
@@ -143,10 +23,6 @@ def install(
|
|
|
143
23
|
"""
|
|
144
24
|
Install dependencies from comfy-env.toml.
|
|
145
25
|
|
|
146
|
-
Example:
|
|
147
|
-
from comfy_env import install
|
|
148
|
-
install()
|
|
149
|
-
|
|
150
26
|
Args:
|
|
151
27
|
config: Optional path to comfy-env.toml. Auto-discovered if not provided.
|
|
152
28
|
node_dir: Optional node directory. Auto-discovered from caller if not provided.
|
|
@@ -164,270 +40,118 @@ def install(
|
|
|
164
40
|
|
|
165
41
|
log = log_callback or print
|
|
166
42
|
|
|
167
|
-
|
|
168
|
-
if
|
|
43
|
+
# Load config
|
|
44
|
+
if config is not None:
|
|
45
|
+
config_path = Path(config)
|
|
46
|
+
if not config_path.is_absolute():
|
|
47
|
+
config_path = node_dir / config_path
|
|
48
|
+
cfg = load_config(config_path)
|
|
49
|
+
else:
|
|
50
|
+
cfg = discover_config(node_dir)
|
|
51
|
+
|
|
52
|
+
if cfg is None:
|
|
169
53
|
raise FileNotFoundError(
|
|
170
54
|
f"No comfy-env.toml found in {node_dir}. "
|
|
171
55
|
"Create comfy-env.toml to define dependencies."
|
|
172
56
|
)
|
|
173
57
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if full_config.has_system:
|
|
178
|
-
_install_system_packages(full_config.system, log, dry_run)
|
|
179
|
-
|
|
180
|
-
env_config = full_config.default_env
|
|
181
|
-
|
|
182
|
-
# Get user wheel_sources overrides
|
|
183
|
-
user_wheel_sources = full_config.wheel_sources if hasattr(full_config, 'wheel_sources') else {}
|
|
184
|
-
|
|
185
|
-
if env_config:
|
|
186
|
-
# Named environment -> always pixi
|
|
187
|
-
log(f"Found environment: {env_config.name}")
|
|
188
|
-
python_ver = env_config.python or "3.11" # Default to 3.11 if not specified
|
|
189
|
-
if not env_config.python:
|
|
190
|
-
log(f" No Python version specified, defaulting to {python_ver}")
|
|
191
|
-
env_config = IsolatedEnv(
|
|
192
|
-
name=env_config.name,
|
|
193
|
-
python=python_ver,
|
|
194
|
-
cuda=env_config.cuda,
|
|
195
|
-
pytorch=env_config.pytorch,
|
|
196
|
-
requirements=env_config.requirements,
|
|
197
|
-
no_deps_requirements=env_config.no_deps_requirements,
|
|
198
|
-
linux_requirements=env_config.linux_requirements,
|
|
199
|
-
darwin_requirements=env_config.darwin_requirements,
|
|
200
|
-
windows_requirements=env_config.windows_requirements,
|
|
201
|
-
conda=env_config.conda,
|
|
202
|
-
isolated=env_config.isolated,
|
|
203
|
-
)
|
|
204
|
-
log(f" Using pixi backend (Python {python_ver})")
|
|
205
|
-
return pixi_install(env_config, node_dir, log, dry_run)
|
|
206
|
-
elif full_config.has_local:
|
|
207
|
-
# [local] section -> uv in-place install
|
|
208
|
-
return _install_local(full_config.local, node_dir, log, dry_run, user_wheel_sources)
|
|
209
|
-
else:
|
|
210
|
-
log("No packages to install")
|
|
211
|
-
return True
|
|
58
|
+
# Install node dependencies first
|
|
59
|
+
if cfg.node_reqs:
|
|
60
|
+
_install_node_dependencies(cfg.node_reqs, node_dir, log, dry_run)
|
|
212
61
|
|
|
62
|
+
# Install everything via pixi
|
|
63
|
+
_install_via_pixi(cfg, node_dir, log, dry_run)
|
|
213
64
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
config_path = node_dir / config_path
|
|
220
|
-
return load_config(config_path, node_dir)
|
|
221
|
-
return discover_config(node_dir)
|
|
65
|
+
# Auto-discover and install isolated subdirectory environments
|
|
66
|
+
_install_isolated_subdirs(node_dir, log, dry_run)
|
|
67
|
+
|
|
68
|
+
log("\nInstallation complete!")
|
|
69
|
+
return True
|
|
222
70
|
|
|
223
71
|
|
|
224
|
-
def
|
|
225
|
-
|
|
72
|
+
def _install_node_dependencies(
|
|
73
|
+
node_reqs: List[NodeReq],
|
|
226
74
|
node_dir: Path,
|
|
227
75
|
log: Callable[[str], None],
|
|
228
76
|
dry_run: bool,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
log("Installing local packages into host environment")
|
|
233
|
-
|
|
234
|
-
if sys.platform == "win32":
|
|
235
|
-
log("Installing MSVC runtime for Windows...")
|
|
236
|
-
if not dry_run:
|
|
237
|
-
_pip_install(["msvc-runtime"], no_deps=False, log=log)
|
|
238
|
-
|
|
239
|
-
env = RuntimeEnv.detect()
|
|
240
|
-
log(f"Detected environment: {env}")
|
|
241
|
-
|
|
242
|
-
if not env.cuda_version and local_config.cuda_packages:
|
|
243
|
-
raise CUDANotFoundError(package=", ".join(local_config.cuda_packages.keys()))
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Install node dependencies (other ComfyUI custom nodes)."""
|
|
79
|
+
from .nodes import install_node_deps
|
|
244
80
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if ver and ver != "*":
|
|
248
|
-
cuda_packages.append(f"{pkg}=={ver}")
|
|
249
|
-
else:
|
|
250
|
-
cuda_packages.append(pkg)
|
|
81
|
+
custom_nodes_dir = node_dir.parent
|
|
82
|
+
log(f"\nInstalling {len(node_reqs)} node dependencies...")
|
|
251
83
|
|
|
252
84
|
if dry_run:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
for pkg in local_config.requirements:
|
|
259
|
-
log(f" {pkg}")
|
|
260
|
-
return True
|
|
261
|
-
|
|
262
|
-
if cuda_packages:
|
|
263
|
-
log(f"\nInstalling {len(cuda_packages)} CUDA packages...")
|
|
264
|
-
for req in cuda_packages:
|
|
265
|
-
package, version = parse_wheel_requirement(req)
|
|
266
|
-
_install_cuda_package(package, version, env, user_wheel_sources, log)
|
|
267
|
-
|
|
268
|
-
if local_config.requirements:
|
|
269
|
-
log(f"\nInstalling {len(local_config.requirements)} regular packages...")
|
|
270
|
-
_pip_install(local_config.requirements, no_deps=False, log=log)
|
|
271
|
-
|
|
272
|
-
log("\nLocal installation complete!")
|
|
273
|
-
return True
|
|
85
|
+
for req in node_reqs:
|
|
86
|
+
node_path = custom_nodes_dir / req.name
|
|
87
|
+
status = "exists" if node_path.exists() else "would clone"
|
|
88
|
+
log(f" {req.name}: {status}")
|
|
89
|
+
return
|
|
274
90
|
|
|
91
|
+
visited: Set[str] = {node_dir.name}
|
|
92
|
+
install_node_deps(node_reqs, custom_nodes_dir, log, visited)
|
|
275
93
|
|
|
276
|
-
def _resolve_wheel_url(
|
|
277
|
-
package: str,
|
|
278
|
-
version: Optional[str],
|
|
279
|
-
env: RuntimeEnv,
|
|
280
|
-
user_wheel_sources: Dict[str, str],
|
|
281
|
-
) -> str:
|
|
282
|
-
"""
|
|
283
|
-
Resolve wheel URL for a CUDA package.
|
|
284
94
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
3. Error if not found
|
|
289
|
-
"""
|
|
290
|
-
pkg_lower = package.lower()
|
|
291
|
-
vars_dict = _build_template_vars(env, version)
|
|
292
|
-
|
|
293
|
-
# 1. Check user overrides first
|
|
294
|
-
if pkg_lower in user_wheel_sources:
|
|
295
|
-
template = user_wheel_sources[pkg_lower]
|
|
296
|
-
return _substitute_template(template, vars_dict)
|
|
297
|
-
|
|
298
|
-
# 2. Check built-in registry
|
|
299
|
-
if pkg_lower in PACKAGE_REGISTRY:
|
|
300
|
-
config = PACKAGE_REGISTRY[pkg_lower]
|
|
301
|
-
|
|
302
|
-
# wheel_template: direct URL
|
|
303
|
-
if "wheel_template" in config:
|
|
304
|
-
template = config["wheel_template"]
|
|
305
|
-
|
|
306
|
-
# Only require version if template uses {version}
|
|
307
|
-
if "{version}" in template:
|
|
308
|
-
effective_version = version or config.get("default_version")
|
|
309
|
-
if not effective_version:
|
|
310
|
-
raise InstallError(f"Package {package} requires version (no default in registry)")
|
|
311
|
-
vars_dict["version"] = effective_version
|
|
312
|
-
|
|
313
|
-
return _substitute_template(template, vars_dict)
|
|
314
|
-
|
|
315
|
-
# find_links: pip resolves wheel from index (e.g., miropsota's detectron2)
|
|
316
|
-
if "find_links" in config:
|
|
317
|
-
find_links_url = config["find_links"]
|
|
318
|
-
effective_version = version or config.get("default_version")
|
|
319
|
-
if effective_version:
|
|
320
|
-
return f"find_links:{find_links_url}:{package}=={effective_version}"
|
|
321
|
-
else:
|
|
322
|
-
return f"find_links:{find_links_url}:{package}"
|
|
323
|
-
|
|
324
|
-
# package_name: PyPI variant (e.g., spconv-cu124)
|
|
325
|
-
if "package_name" in config:
|
|
326
|
-
pkg_name = _substitute_template(config["package_name"], vars_dict)
|
|
327
|
-
return f"pypi:{pkg_name}" # Special marker for PyPI install
|
|
328
|
-
|
|
329
|
-
raise InstallError(
|
|
330
|
-
f"Package {package} not found in registry or user wheel_sources.\n"
|
|
331
|
-
f"Add it to [wheel_sources] in your comfy-env.toml:\n\n"
|
|
332
|
-
f"[wheel_sources]\n"
|
|
333
|
-
f'{package} = "https://example.com/{package}-{{version}}+cu{{cuda_short}}-{{py_tag}}-{{platform}}.whl"'
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def _install_cuda_package(
|
|
338
|
-
package: str,
|
|
339
|
-
version: Optional[str],
|
|
340
|
-
env: RuntimeEnv,
|
|
341
|
-
user_wheel_sources: Dict[str, str],
|
|
95
|
+
def _install_via_pixi(
|
|
96
|
+
cfg: ComfyEnvConfig,
|
|
97
|
+
node_dir: Path,
|
|
342
98
|
log: Callable[[str], None],
|
|
99
|
+
dry_run: bool,
|
|
343
100
|
) -> None:
|
|
344
|
-
"""
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
Uses wheel_template for direct URL or package_name for PyPI variants.
|
|
348
|
-
"""
|
|
349
|
-
url_or_marker = _resolve_wheel_url(package, version, env, user_wheel_sources)
|
|
350
|
-
|
|
351
|
-
if url_or_marker.startswith("pypi:"):
|
|
352
|
-
# PyPI variant package (e.g., spconv-cu124)
|
|
353
|
-
pkg_name = url_or_marker[5:] # Strip "pypi:" prefix
|
|
354
|
-
pkg_spec = f"{pkg_name}=={version}" if version else pkg_name
|
|
355
|
-
log(f" Installing {package} as {pkg_spec} from PyPI...")
|
|
356
|
-
_pip_install([pkg_spec], no_deps=False, log=log)
|
|
357
|
-
elif url_or_marker.startswith("find_links:"):
|
|
358
|
-
# find_links: pip resolves from index URL
|
|
359
|
-
_, find_links_url, pkg_spec = url_or_marker.split(":", 2)
|
|
360
|
-
log(f" Installing {pkg_spec} from {find_links_url}...")
|
|
361
|
-
_pip_install([pkg_spec, "--find-links", find_links_url], no_deps=True, log=log)
|
|
362
|
-
else:
|
|
363
|
-
# Direct wheel URL
|
|
364
|
-
log(f" Installing {package}...")
|
|
365
|
-
log(f" URL: {url_or_marker}")
|
|
366
|
-
_pip_install([url_or_marker], no_deps=True, log=log)
|
|
367
|
-
|
|
101
|
+
"""Install all packages via pixi."""
|
|
102
|
+
from .pixi import pixi_install
|
|
368
103
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
vars_dict = env.as_dict()
|
|
104
|
+
# Count what we're installing
|
|
105
|
+
cuda_count = len(cfg.cuda_packages)
|
|
372
106
|
|
|
373
|
-
|
|
374
|
-
|
|
107
|
+
# Count from passthrough (pixi-native format)
|
|
108
|
+
deps = cfg.pixi_passthrough.get("dependencies", {})
|
|
109
|
+
pypi_deps = cfg.pixi_passthrough.get("pypi-dependencies", {})
|
|
375
110
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
111
|
+
if cuda_count == 0 and not deps and not pypi_deps:
|
|
112
|
+
log("No packages to install")
|
|
113
|
+
return
|
|
379
114
|
|
|
380
|
-
|
|
115
|
+
log(f"\nInstalling via pixi:")
|
|
116
|
+
if cuda_count:
|
|
117
|
+
log(f" CUDA packages: {', '.join(cfg.cuda_packages)}")
|
|
118
|
+
if deps:
|
|
119
|
+
log(f" Conda packages: {len(deps)}")
|
|
120
|
+
if pypi_deps:
|
|
121
|
+
log(f" PyPI packages: {len(pypi_deps)}")
|
|
381
122
|
|
|
123
|
+
if dry_run:
|
|
124
|
+
log("\n(dry run - no changes made)")
|
|
125
|
+
return
|
|
382
126
|
|
|
383
|
-
|
|
384
|
-
"""Substitute {var} placeholders in template with values from vars_dict."""
|
|
385
|
-
result = template
|
|
386
|
-
for key, value in vars_dict.items():
|
|
387
|
-
if value is not None:
|
|
388
|
-
result = result.replace(f"{{{key}}}", str(value))
|
|
389
|
-
return result
|
|
127
|
+
pixi_install(cfg, node_dir, log)
|
|
390
128
|
|
|
391
129
|
|
|
392
|
-
def
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
130
|
+
def _install_isolated_subdirs(
|
|
131
|
+
node_dir: Path,
|
|
132
|
+
log: Callable[[str], None],
|
|
133
|
+
dry_run: bool,
|
|
396
134
|
) -> None:
|
|
397
|
-
"""
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
#
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
args.append("--no-deps")
|
|
406
|
-
args.extend(packages)
|
|
407
|
-
|
|
408
|
-
log(f"Running: {' '.join(args[:3])}... ({len(packages)} packages)")
|
|
409
|
-
|
|
410
|
-
result = subprocess.run(args, capture_output=True, text=True)
|
|
411
|
-
|
|
412
|
-
if result.returncode != 0:
|
|
413
|
-
raise InstallError(
|
|
414
|
-
f"Failed to install packages",
|
|
415
|
-
exit_code=result.returncode,
|
|
416
|
-
stderr=result.stderr,
|
|
417
|
-
)
|
|
135
|
+
"""Find and install comfy-env.toml in subdirectories."""
|
|
136
|
+
from .pixi import pixi_install
|
|
137
|
+
from .config.parser import CONFIG_FILE_NAME
|
|
138
|
+
|
|
139
|
+
# Find all comfy-env.toml files in subdirectories (not root)
|
|
140
|
+
for config_file in node_dir.rglob(CONFIG_FILE_NAME):
|
|
141
|
+
if config_file.parent == node_dir:
|
|
142
|
+
continue # Skip root (already installed)
|
|
418
143
|
|
|
144
|
+
sub_dir = config_file.parent
|
|
145
|
+
relative = sub_dir.relative_to(node_dir)
|
|
419
146
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
uv_path = shutil.which("uv")
|
|
423
|
-
if uv_path:
|
|
424
|
-
return [uv_path, "pip"]
|
|
425
|
-
return [sys.executable, "-m", "pip"]
|
|
147
|
+
log(f"\n[isolated] Installing: {relative}")
|
|
148
|
+
sub_cfg = load_config(config_file)
|
|
426
149
|
|
|
150
|
+
if dry_run:
|
|
151
|
+
log(f" (dry run)")
|
|
152
|
+
continue
|
|
427
153
|
|
|
428
|
-
|
|
429
|
-
"""Check if we're using uv for pip commands."""
|
|
430
|
-
return shutil.which("uv") is not None
|
|
154
|
+
pixi_install(sub_cfg, sub_dir, log, create_env_link=True)
|
|
431
155
|
|
|
432
156
|
|
|
433
157
|
def verify_installation(
|
|
@@ -445,5 +169,3 @@ def verify_installation(
|
|
|
445
169
|
log(f" {package}: FAILED ({e})")
|
|
446
170
|
all_ok = False
|
|
447
171
|
return all_ok
|
|
448
|
-
|
|
449
|
-
|