comfygit-deploy 0.3.4__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.
- comfygit_deploy/__init__.py +3 -0
- comfygit_deploy/cli.py +374 -0
- comfygit_deploy/commands/__init__.py +5 -0
- comfygit_deploy/commands/custom.py +218 -0
- comfygit_deploy/commands/dev.py +356 -0
- comfygit_deploy/commands/instances.py +506 -0
- comfygit_deploy/commands/runpod.py +203 -0
- comfygit_deploy/commands/worker.py +266 -0
- comfygit_deploy/config.py +122 -0
- comfygit_deploy/providers/__init__.py +11 -0
- comfygit_deploy/providers/custom.py +238 -0
- comfygit_deploy/providers/runpod.py +549 -0
- comfygit_deploy/startup/__init__.py +1 -0
- comfygit_deploy/startup/scripts.py +210 -0
- comfygit_deploy/worker/__init__.py +12 -0
- comfygit_deploy/worker/mdns.py +154 -0
- comfygit_deploy/worker/native_manager.py +438 -0
- comfygit_deploy/worker/server.py +511 -0
- comfygit_deploy/worker/state.py +268 -0
- comfygit_deploy-0.3.4.dist-info/METADATA +38 -0
- comfygit_deploy-0.3.4.dist-info/RECORD +23 -0
- comfygit_deploy-0.3.4.dist-info/WHEEL +4 -0
- comfygit_deploy-0.3.4.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Dev CLI command handlers.
|
|
2
|
+
|
|
3
|
+
Commands for setting up development mode with local package paths.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
DEV_CONFIG_PATH = Path.home() / ".config" / "comfygit" / "deploy" / "dev.json"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class DevNode:
|
|
18
|
+
"""A development node configuration."""
|
|
19
|
+
|
|
20
|
+
name: str
|
|
21
|
+
path: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def load_dev_config() -> dict:
|
|
25
|
+
"""Load dev config from disk."""
|
|
26
|
+
if not DEV_CONFIG_PATH.exists():
|
|
27
|
+
return {}
|
|
28
|
+
try:
|
|
29
|
+
return json.loads(DEV_CONFIG_PATH.read_text())
|
|
30
|
+
except (json.JSONDecodeError, OSError):
|
|
31
|
+
return {}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_dev_nodes() -> list[DevNode]:
|
|
35
|
+
"""Get list of configured dev nodes."""
|
|
36
|
+
config = load_dev_config()
|
|
37
|
+
return [DevNode(name=n["name"], path=n["path"]) for n in config.get("dev_nodes", [])]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def save_dev_config(config: dict) -> None:
|
|
41
|
+
"""Save dev config to disk."""
|
|
42
|
+
DEV_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
DEV_CONFIG_PATH.write_text(json.dumps(config, indent=2))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_workspace_path() -> Path | None:
|
|
47
|
+
"""Get workspace path from env or default."""
|
|
48
|
+
env_home = os.environ.get("COMFYGIT_HOME")
|
|
49
|
+
if env_home:
|
|
50
|
+
return Path(env_home)
|
|
51
|
+
default = Path.home() / "comfygit"
|
|
52
|
+
if default.exists():
|
|
53
|
+
return default
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def handle_setup(args: argparse.Namespace) -> int:
|
|
58
|
+
"""Handle 'dev setup' command."""
|
|
59
|
+
config = load_dev_config()
|
|
60
|
+
|
|
61
|
+
# Show current config
|
|
62
|
+
if args.show:
|
|
63
|
+
if not config:
|
|
64
|
+
print("No dev config set.")
|
|
65
|
+
else:
|
|
66
|
+
print("Dev config:")
|
|
67
|
+
if config.get("core_path"):
|
|
68
|
+
print(f" Core: {config['core_path']}")
|
|
69
|
+
if config.get("manager_path"):
|
|
70
|
+
print(f" Manager: {config['manager_path']}")
|
|
71
|
+
dev_nodes = config.get("dev_nodes", [])
|
|
72
|
+
if dev_nodes:
|
|
73
|
+
print(f" Dev nodes ({len(dev_nodes)}):")
|
|
74
|
+
for node in dev_nodes:
|
|
75
|
+
print(f" - {node['name']}: {node['path']}")
|
|
76
|
+
return 0
|
|
77
|
+
|
|
78
|
+
# Clear config
|
|
79
|
+
if args.clear:
|
|
80
|
+
# Also restore manager symlink to original
|
|
81
|
+
workspace = get_workspace_path()
|
|
82
|
+
if workspace and config.get("manager_path"):
|
|
83
|
+
manager_link = workspace / ".metadata" / "system_nodes" / "comfygit-manager"
|
|
84
|
+
if manager_link.is_symlink():
|
|
85
|
+
manager_link.unlink()
|
|
86
|
+
print(f"Removed dev manager symlink: {manager_link}")
|
|
87
|
+
print("Run 'cg init' or manually clone the manager to restore.")
|
|
88
|
+
|
|
89
|
+
if DEV_CONFIG_PATH.exists():
|
|
90
|
+
DEV_CONFIG_PATH.unlink()
|
|
91
|
+
print("Dev config cleared.")
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
# Validate and set paths
|
|
95
|
+
if args.core:
|
|
96
|
+
core_path = Path(args.core).resolve()
|
|
97
|
+
if not (core_path / "pyproject.toml").exists():
|
|
98
|
+
print(f"Error: Not a valid package path: {core_path}")
|
|
99
|
+
print(" Expected pyproject.toml in the directory.")
|
|
100
|
+
return 1
|
|
101
|
+
config["core_path"] = str(core_path)
|
|
102
|
+
print(f"Core path: {core_path}")
|
|
103
|
+
|
|
104
|
+
if args.manager:
|
|
105
|
+
manager_path = Path(args.manager).resolve()
|
|
106
|
+
if not (manager_path / "__init__.py").exists() and not (manager_path / "server").exists():
|
|
107
|
+
print(f"Error: Not a valid manager path: {manager_path}")
|
|
108
|
+
return 1
|
|
109
|
+
config["manager_path"] = str(manager_path)
|
|
110
|
+
print(f"Manager path: {manager_path}")
|
|
111
|
+
|
|
112
|
+
# Symlink manager to system_nodes
|
|
113
|
+
workspace = get_workspace_path()
|
|
114
|
+
if workspace:
|
|
115
|
+
system_nodes = workspace / ".metadata" / "system_nodes"
|
|
116
|
+
system_nodes.mkdir(parents=True, exist_ok=True)
|
|
117
|
+
manager_link = system_nodes / "comfygit-manager"
|
|
118
|
+
|
|
119
|
+
# Remove existing (whether symlink or directory)
|
|
120
|
+
if manager_link.is_symlink():
|
|
121
|
+
manager_link.unlink()
|
|
122
|
+
elif manager_link.is_dir():
|
|
123
|
+
import shutil
|
|
124
|
+
shutil.rmtree(manager_link)
|
|
125
|
+
|
|
126
|
+
manager_link.symlink_to(manager_path)
|
|
127
|
+
print(f"Symlinked: {manager_link} -> {manager_path}")
|
|
128
|
+
|
|
129
|
+
if not args.core and not args.manager:
|
|
130
|
+
print("Usage: cg-deploy dev setup --core PATH --manager PATH")
|
|
131
|
+
print(" cg-deploy dev setup --show")
|
|
132
|
+
print(" cg-deploy dev setup --clear")
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
save_dev_config(config)
|
|
136
|
+
print()
|
|
137
|
+
print("Dev mode configured!")
|
|
138
|
+
print()
|
|
139
|
+
print("Start worker with dev paths:")
|
|
140
|
+
if config.get("core_path"):
|
|
141
|
+
print(f" cg-deploy worker up --dev-core {config['core_path']}")
|
|
142
|
+
print()
|
|
143
|
+
print("Or set environment variable:")
|
|
144
|
+
if config.get("core_path"):
|
|
145
|
+
print(f" export COMFYGIT_DEV_CORE_PATH={config['core_path']}")
|
|
146
|
+
|
|
147
|
+
return 0
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def handle_patch(args: argparse.Namespace) -> int:
|
|
151
|
+
"""Handle 'dev patch' command - patch existing environments with dev config."""
|
|
152
|
+
config = load_dev_config()
|
|
153
|
+
core_path = config.get("core_path")
|
|
154
|
+
dev_nodes = config.get("dev_nodes", [])
|
|
155
|
+
|
|
156
|
+
if not core_path and not dev_nodes:
|
|
157
|
+
print("No dev config found.")
|
|
158
|
+
print("Run: cg-deploy dev setup --core PATH")
|
|
159
|
+
print(" cg-deploy dev add-node NAME PATH")
|
|
160
|
+
return 1
|
|
161
|
+
|
|
162
|
+
workspace = get_workspace_path()
|
|
163
|
+
if not workspace:
|
|
164
|
+
print("No workspace found. Set COMFYGIT_HOME or run 'cg init'.")
|
|
165
|
+
return 1
|
|
166
|
+
|
|
167
|
+
envs_dir = workspace / "environments"
|
|
168
|
+
if not envs_dir.exists():
|
|
169
|
+
print("No environments found.")
|
|
170
|
+
return 0
|
|
171
|
+
|
|
172
|
+
# Find environments to patch
|
|
173
|
+
if args.env:
|
|
174
|
+
envs = [envs_dir / args.env]
|
|
175
|
+
if not envs[0].exists():
|
|
176
|
+
print(f"Environment not found: {args.env}")
|
|
177
|
+
return 1
|
|
178
|
+
else:
|
|
179
|
+
envs = [e for e in envs_dir.iterdir() if e.is_dir() and (e / ".venv").exists()]
|
|
180
|
+
|
|
181
|
+
if not envs:
|
|
182
|
+
print("No environments with .venv found.")
|
|
183
|
+
return 0
|
|
184
|
+
|
|
185
|
+
print(f"Patching {len(envs)} environment(s):")
|
|
186
|
+
if core_path:
|
|
187
|
+
print(f" - dev core: {core_path}")
|
|
188
|
+
for node in dev_nodes:
|
|
189
|
+
print(f" - dev node: {node['name']} -> {node['path']}")
|
|
190
|
+
print()
|
|
191
|
+
|
|
192
|
+
for env_path in envs:
|
|
193
|
+
env_name = env_path.name
|
|
194
|
+
venv_python = env_path / ".venv" / "bin" / "python"
|
|
195
|
+
|
|
196
|
+
if not venv_python.exists():
|
|
197
|
+
print(f" {env_name}: skipped (no .venv)")
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
success = True
|
|
201
|
+
|
|
202
|
+
# Patch core if configured
|
|
203
|
+
if core_path:
|
|
204
|
+
cmd = ["uv", "pip", "install", "-e", core_path, "--python", str(venv_python)]
|
|
205
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
206
|
+
if result.returncode != 0:
|
|
207
|
+
print(f" {env_name}: core failed - {result.stderr.strip()[:60]}")
|
|
208
|
+
success = False
|
|
209
|
+
|
|
210
|
+
# Apply dev nodes
|
|
211
|
+
for node in dev_nodes:
|
|
212
|
+
node_result = _apply_dev_node_to_env(env_path, node["name"], node["path"], workspace)
|
|
213
|
+
if not node_result:
|
|
214
|
+
print(f" {env_name}: node {node['name']} failed")
|
|
215
|
+
success = False
|
|
216
|
+
|
|
217
|
+
if success:
|
|
218
|
+
print(f" {env_name}: patched")
|
|
219
|
+
|
|
220
|
+
print()
|
|
221
|
+
print("Done. Restart any running ComfyUI instances to apply changes.")
|
|
222
|
+
|
|
223
|
+
return 0
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _apply_dev_node_to_env(env_path: Path, node_name: str, node_path: str, workspace: Path) -> bool:
|
|
227
|
+
"""Apply a dev node to an environment (symlink + track).
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
env_path: Path to the environment
|
|
231
|
+
node_name: Name of the node
|
|
232
|
+
node_path: Path to the dev node source
|
|
233
|
+
workspace: Workspace path
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
True if successful
|
|
237
|
+
"""
|
|
238
|
+
import shutil
|
|
239
|
+
|
|
240
|
+
custom_nodes = env_path / "ComfyUI" / "custom_nodes"
|
|
241
|
+
if not custom_nodes.exists():
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
target = custom_nodes / node_name
|
|
245
|
+
source = Path(node_path)
|
|
246
|
+
|
|
247
|
+
# Create/update symlink
|
|
248
|
+
if target.is_symlink():
|
|
249
|
+
if target.resolve() == source.resolve():
|
|
250
|
+
# Already correct
|
|
251
|
+
pass
|
|
252
|
+
else:
|
|
253
|
+
target.unlink()
|
|
254
|
+
target.symlink_to(source)
|
|
255
|
+
elif target.exists():
|
|
256
|
+
# Regular directory exists - replace with symlink
|
|
257
|
+
shutil.rmtree(target)
|
|
258
|
+
target.symlink_to(source)
|
|
259
|
+
else:
|
|
260
|
+
target.symlink_to(source)
|
|
261
|
+
|
|
262
|
+
# Track with cg node add --dev
|
|
263
|
+
env = os.environ.copy()
|
|
264
|
+
env["COMFYGIT_HOME"] = str(workspace)
|
|
265
|
+
|
|
266
|
+
cmd = ["cg", "-e", env_path.name, "node", "add", node_name, "--dev"]
|
|
267
|
+
result = subprocess.run(cmd, env=env, capture_output=True, text=True)
|
|
268
|
+
|
|
269
|
+
# Success if tracked or already tracked
|
|
270
|
+
return result.returncode == 0 or "already tracked" in result.stderr.lower()
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def handle_add_node(args: argparse.Namespace) -> int:
|
|
274
|
+
"""Handle 'dev add-node' command."""
|
|
275
|
+
config = load_dev_config()
|
|
276
|
+
|
|
277
|
+
node_name = args.name
|
|
278
|
+
node_path = Path(args.path).resolve()
|
|
279
|
+
|
|
280
|
+
# Validate path
|
|
281
|
+
if not node_path.is_dir():
|
|
282
|
+
print(f"Error: Path does not exist: {node_path}")
|
|
283
|
+
return 1
|
|
284
|
+
|
|
285
|
+
# Check for __init__.py or common node indicators
|
|
286
|
+
has_init = (node_path / "__init__.py").exists()
|
|
287
|
+
has_nodes = (node_path / "nodes.py").exists() or (node_path / "nodes").exists()
|
|
288
|
+
if not has_init and not has_nodes:
|
|
289
|
+
print(f"Warning: {node_path} doesn't look like a ComfyUI node (no __init__.py or nodes.py)")
|
|
290
|
+
|
|
291
|
+
# Add to dev_nodes list
|
|
292
|
+
dev_nodes = config.get("dev_nodes", [])
|
|
293
|
+
|
|
294
|
+
# Check if already exists
|
|
295
|
+
existing = next((n for n in dev_nodes if n["name"] == node_name), None)
|
|
296
|
+
if existing:
|
|
297
|
+
existing["path"] = str(node_path)
|
|
298
|
+
print(f"Updated dev node: {node_name} -> {node_path}")
|
|
299
|
+
else:
|
|
300
|
+
dev_nodes.append({"name": node_name, "path": str(node_path)})
|
|
301
|
+
print(f"Added dev node: {node_name} -> {node_path}")
|
|
302
|
+
|
|
303
|
+
config["dev_nodes"] = dev_nodes
|
|
304
|
+
save_dev_config(config)
|
|
305
|
+
|
|
306
|
+
print()
|
|
307
|
+
print("To apply to existing environments:")
|
|
308
|
+
print(" cg-deploy dev patch")
|
|
309
|
+
print()
|
|
310
|
+
print("New instances created with --dev will include this node.")
|
|
311
|
+
|
|
312
|
+
return 0
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def handle_remove_node(args: argparse.Namespace) -> int:
|
|
316
|
+
"""Handle 'dev remove-node' command."""
|
|
317
|
+
config = load_dev_config()
|
|
318
|
+
node_name = args.name
|
|
319
|
+
|
|
320
|
+
dev_nodes = config.get("dev_nodes", [])
|
|
321
|
+
original_len = len(dev_nodes)
|
|
322
|
+
|
|
323
|
+
dev_nodes = [n for n in dev_nodes if n["name"] != node_name]
|
|
324
|
+
|
|
325
|
+
if len(dev_nodes) == original_len:
|
|
326
|
+
print(f"Dev node not found: {node_name}")
|
|
327
|
+
return 1
|
|
328
|
+
|
|
329
|
+
config["dev_nodes"] = dev_nodes
|
|
330
|
+
save_dev_config(config)
|
|
331
|
+
|
|
332
|
+
print(f"Removed dev node: {node_name}")
|
|
333
|
+
print("Note: Existing symlinks in environments are not removed.")
|
|
334
|
+
|
|
335
|
+
return 0
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def handle_list_nodes(args: argparse.Namespace) -> int:
|
|
339
|
+
"""Handle 'dev list-nodes' command."""
|
|
340
|
+
config = load_dev_config()
|
|
341
|
+
dev_nodes = config.get("dev_nodes", [])
|
|
342
|
+
|
|
343
|
+
if not dev_nodes:
|
|
344
|
+
print("No dev nodes configured.")
|
|
345
|
+
print()
|
|
346
|
+
print("Add a dev node:")
|
|
347
|
+
print(" cg-deploy dev add-node NAME PATH")
|
|
348
|
+
return 0
|
|
349
|
+
|
|
350
|
+
print(f"Dev nodes ({len(dev_nodes)}):")
|
|
351
|
+
for node in dev_nodes:
|
|
352
|
+
path = Path(node["path"])
|
|
353
|
+
exists = "✓" if path.exists() else "✗"
|
|
354
|
+
print(f" {exists} {node['name']}: {node['path']}")
|
|
355
|
+
|
|
356
|
+
return 0
|