comfygit-deploy 0.3.7__tar.gz

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.
Files changed (38) hide show
  1. comfygit_deploy-0.3.7/.gitignore +87 -0
  2. comfygit_deploy-0.3.7/PKG-INFO +38 -0
  3. comfygit_deploy-0.3.7/README.md +28 -0
  4. comfygit_deploy-0.3.7/comfygit_deploy/__init__.py +3 -0
  5. comfygit_deploy-0.3.7/comfygit_deploy/cli.py +374 -0
  6. comfygit_deploy-0.3.7/comfygit_deploy/commands/__init__.py +5 -0
  7. comfygit_deploy-0.3.7/comfygit_deploy/commands/custom.py +218 -0
  8. comfygit_deploy-0.3.7/comfygit_deploy/commands/dev.py +356 -0
  9. comfygit_deploy-0.3.7/comfygit_deploy/commands/instances.py +506 -0
  10. comfygit_deploy-0.3.7/comfygit_deploy/commands/runpod.py +203 -0
  11. comfygit_deploy-0.3.7/comfygit_deploy/commands/worker.py +272 -0
  12. comfygit_deploy-0.3.7/comfygit_deploy/config.py +122 -0
  13. comfygit_deploy-0.3.7/comfygit_deploy/providers/__init__.py +11 -0
  14. comfygit_deploy-0.3.7/comfygit_deploy/providers/custom.py +238 -0
  15. comfygit_deploy-0.3.7/comfygit_deploy/providers/runpod.py +549 -0
  16. comfygit_deploy-0.3.7/comfygit_deploy/startup/__init__.py +1 -0
  17. comfygit_deploy-0.3.7/comfygit_deploy/startup/scripts.py +210 -0
  18. comfygit_deploy-0.3.7/comfygit_deploy/worker/__init__.py +12 -0
  19. comfygit_deploy-0.3.7/comfygit_deploy/worker/mdns.py +154 -0
  20. comfygit_deploy-0.3.7/comfygit_deploy/worker/native_manager.py +438 -0
  21. comfygit_deploy-0.3.7/comfygit_deploy/worker/server.py +511 -0
  22. comfygit_deploy-0.3.7/comfygit_deploy/worker/state.py +268 -0
  23. comfygit_deploy-0.3.7/pyproject.toml +31 -0
  24. comfygit_deploy-0.3.7/tests/__init__.py +1 -0
  25. comfygit_deploy-0.3.7/tests/conftest.py +13 -0
  26. comfygit_deploy-0.3.7/tests/test_cli.py +95 -0
  27. comfygit_deploy-0.3.7/tests/test_config.py +95 -0
  28. comfygit_deploy-0.3.7/tests/test_custom_client.py +211 -0
  29. comfygit_deploy-0.3.7/tests/test_logs_streaming.py +327 -0
  30. comfygit_deploy-0.3.7/tests/test_mdns.py +64 -0
  31. comfygit_deploy-0.3.7/tests/test_mdns_scanner.py +202 -0
  32. comfygit_deploy-0.3.7/tests/test_native_manager.py +308 -0
  33. comfygit_deploy-0.3.7/tests/test_runpod_client.py +207 -0
  34. comfygit_deploy-0.3.7/tests/test_startup_script.py +118 -0
  35. comfygit_deploy-0.3.7/tests/test_unified_instances.py +407 -0
  36. comfygit_deploy-0.3.7/tests/test_worker_commands.py +407 -0
  37. comfygit_deploy-0.3.7/tests/test_worker_server.py +225 -0
  38. comfygit_deploy-0.3.7/tests/test_worker_state.py +361 -0
@@ -0,0 +1,87 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ venv/
9
+ ENV/
10
+ .venv
11
+ pip-log.txt
12
+ pip-delete-this-directory.txt
13
+ .pytest_cache/
14
+ *.egg-info/
15
+ dist/
16
+ build/
17
+ *.egg
18
+ .coverage
19
+ htmlcov/
20
+ .tox/
21
+ .mypy_cache/
22
+ .dmypy.json
23
+ dmypy.json
24
+ .pyre/
25
+
26
+ # Node
27
+ node_modules/
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+
32
+ # Environment variables
33
+ .env
34
+ .env.local
35
+ .env.*.local
36
+
37
+ # IDE
38
+ .vscode/
39
+ .idea/
40
+ *.swp
41
+ *.swo
42
+ *~
43
+
44
+ # OS
45
+ .DS_Store
46
+ Thumbs.db
47
+
48
+ # Docker
49
+ .dockerignore
50
+
51
+ # Test environments
52
+ dev/test-environments/
53
+
54
+ # Documentation build
55
+ docs/site/
56
+ docs/comfydock-docs/site/
57
+
58
+ # Logs
59
+ *.log
60
+ logs/
61
+
62
+ # Temporary files
63
+ *.tmp
64
+ temp/
65
+ tmp/
66
+
67
+ # Local configuration
68
+ local_config.json
69
+
70
+ # LLMD files:
71
+ *llm-context.md
72
+ .llmd
73
+
74
+ # Claude:
75
+ .claude/
76
+
77
+ # Docs:
78
+ docs/archive
79
+ docs/contexts
80
+
81
+ # Helper scripts:
82
+ .setup.sh
83
+ .scripts/
84
+
85
+ # Any test environment exports
86
+ *.tar.gz
87
+ .claude/context
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: comfygit-deploy
3
+ Version: 0.3.7
4
+ Summary: ComfyGit Deploy - Remote deployment and worker management CLI
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: aiohttp>=3.9.0
7
+ Requires-Dist: comfygit==0.3.7
8
+ Requires-Dist: zeroconf>=0.131.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # ComfyGit Deploy
12
+
13
+ Remote deployment and worker management CLI for ComfyGit.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install comfygit-deploy
19
+ # or
20
+ uv tool install comfygit-deploy
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```bash
26
+ # Configure RunPod
27
+ cg-deploy runpod config --api-key <your-key>
28
+
29
+ # Deploy to RunPod
30
+ cg-deploy runpod deploy <git-url> --gpu "RTX 4090"
31
+
32
+ # Manage instances
33
+ cg-deploy instances
34
+ cg-deploy stop <instance-id>
35
+ cg-deploy terminate <instance-id>
36
+ ```
37
+
38
+ See the [documentation](../../docs/comfygit-docs/) for full usage information.
@@ -0,0 +1,28 @@
1
+ # ComfyGit Deploy
2
+
3
+ Remote deployment and worker management CLI for ComfyGit.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install comfygit-deploy
9
+ # or
10
+ uv tool install comfygit-deploy
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ # Configure RunPod
17
+ cg-deploy runpod config --api-key <your-key>
18
+
19
+ # Deploy to RunPod
20
+ cg-deploy runpod deploy <git-url> --gpu "RTX 4090"
21
+
22
+ # Manage instances
23
+ cg-deploy instances
24
+ cg-deploy stop <instance-id>
25
+ cg-deploy terminate <instance-id>
26
+ ```
27
+
28
+ See the [documentation](../../docs/comfygit-docs/) for full usage information.
@@ -0,0 +1,3 @@
1
+ """ComfyGit Deploy - Remote deployment and worker management CLI."""
2
+
3
+ __version__ = "0.3.3"
@@ -0,0 +1,374 @@
1
+ """CLI entry point for comfygit-deploy.
2
+
3
+ Provides command-line interface for deploying ComfyUI environments
4
+ to cloud providers (RunPod) and self-hosted workers.
5
+ """
6
+
7
+ import argparse
8
+ import sys
9
+
10
+ from . import __version__
11
+
12
+
13
+ def create_parser() -> argparse.ArgumentParser:
14
+ """Create the argument parser with all subcommands."""
15
+ parser = argparse.ArgumentParser(
16
+ prog="cg-deploy",
17
+ description="ComfyGit Deploy - Remote deployment and worker management",
18
+ )
19
+ parser.add_argument(
20
+ "--version",
21
+ action="version",
22
+ version=f"%(prog)s {__version__}",
23
+ )
24
+
25
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
26
+
27
+ # =========================================================================
28
+ # RunPod commands
29
+ # =========================================================================
30
+ runpod_parser = subparsers.add_parser("runpod", help="RunPod operations")
31
+ runpod_subparsers = runpod_parser.add_subparsers(
32
+ dest="runpod_command", help="RunPod subcommands"
33
+ )
34
+
35
+ # runpod config
36
+ config_parser = runpod_subparsers.add_parser("config", help="Configure RunPod API key")
37
+ config_parser.add_argument("--api-key", help="Set RunPod API key")
38
+ config_parser.add_argument("--show", action="store_true", help="Show current config")
39
+ config_parser.add_argument("--clear", action="store_true", help="Clear stored API key")
40
+
41
+ # runpod gpus
42
+ gpus_parser = runpod_subparsers.add_parser("gpus", help="List available GPUs with pricing")
43
+ gpus_parser.add_argument("--region", help="Filter by region/data center")
44
+
45
+ # runpod volumes
46
+ runpod_subparsers.add_parser("volumes", help="List network volumes")
47
+
48
+ # runpod regions
49
+ runpod_subparsers.add_parser("regions", help="List data centers")
50
+
51
+ # runpod deploy
52
+ deploy_parser = runpod_subparsers.add_parser("deploy", help="Deploy environment to RunPod")
53
+ deploy_parser.add_argument("import_source", help="Git URL or local path to import")
54
+ deploy_parser.add_argument("--gpu", required=True, help="GPU type (e.g., 'RTX 4090')")
55
+ deploy_parser.add_argument("--volume", help="Network volume ID to attach")
56
+ deploy_parser.add_argument("--name", help="Deployment name")
57
+ deploy_parser.add_argument("--branch", "-b", help="Git branch/tag to use")
58
+ deploy_parser.add_argument(
59
+ "--cloud-type",
60
+ choices=["SECURE", "COMMUNITY"],
61
+ default="SECURE",
62
+ help="Cloud type (default: SECURE)",
63
+ )
64
+ deploy_parser.add_argument(
65
+ "--pricing-type",
66
+ choices=["ON_DEMAND", "SPOT"],
67
+ default="ON_DEMAND",
68
+ help="Pricing type (default: ON_DEMAND)",
69
+ )
70
+ deploy_parser.add_argument("--spot-bid", type=float, help="Spot bid price (for SPOT pricing)")
71
+
72
+ # =========================================================================
73
+ # Instance commands
74
+ # =========================================================================
75
+ instances_parser = subparsers.add_parser("instances", help="List all instances")
76
+ instances_parser.add_argument(
77
+ "--provider",
78
+ choices=["runpod", "custom"],
79
+ help="Filter by provider",
80
+ )
81
+ instances_parser.add_argument(
82
+ "--status",
83
+ choices=["running", "stopped"],
84
+ help="Filter by status",
85
+ )
86
+ instances_parser.add_argument("--json", action="store_true", help="Output as JSON")
87
+
88
+ # start
89
+ start_parser = subparsers.add_parser("start", help="Start a stopped instance")
90
+ start_parser.add_argument("instance_id", help="Instance ID to start")
91
+
92
+ # stop
93
+ stop_parser = subparsers.add_parser("stop", help="Stop a running instance")
94
+ stop_parser.add_argument("instance_id", help="Instance ID to stop")
95
+
96
+ # terminate
97
+ terminate_parser = subparsers.add_parser("terminate", help="Terminate an instance")
98
+ terminate_parser.add_argument("instance_id", help="Instance ID to terminate")
99
+ terminate_parser.add_argument("--force", action="store_true", help="Force termination")
100
+ terminate_parser.add_argument("--keep-env", action="store_true", help="Keep environment directory")
101
+
102
+ # logs
103
+ logs_parser = subparsers.add_parser("logs", help="View instance logs")
104
+ logs_parser.add_argument("instance_id", help="Instance ID")
105
+ logs_parser.add_argument("--follow", "-f", action="store_true", help="Follow log output")
106
+ logs_parser.add_argument("--lines", "-n", type=int, default=100, help="Number of lines")
107
+
108
+ # open
109
+ open_parser = subparsers.add_parser("open", help="Open ComfyUI URL in browser")
110
+ open_parser.add_argument("instance_id", help="Instance ID")
111
+
112
+ # wait
113
+ wait_parser = subparsers.add_parser("wait", help="Wait for instance to be ready")
114
+ wait_parser.add_argument("instance_id", help="Instance ID")
115
+ wait_parser.add_argument("--timeout", type=int, default=300, help="Timeout in seconds")
116
+
117
+ # =========================================================================
118
+ # Custom worker commands (Phase 2)
119
+ # =========================================================================
120
+ custom_parser = subparsers.add_parser("custom", help="Custom worker operations")
121
+ custom_subparsers = custom_parser.add_subparsers(
122
+ dest="custom_command", help="Custom worker subcommands"
123
+ )
124
+
125
+ # custom scan
126
+ scan_parser = custom_subparsers.add_parser("scan", help="Scan for workers via mDNS")
127
+ scan_parser.add_argument("--timeout", type=int, default=5, help="Scan timeout")
128
+
129
+ # custom add
130
+ add_parser = custom_subparsers.add_parser("add", help="Add a custom worker")
131
+ add_parser.add_argument("name", help="Worker name")
132
+ add_parser.add_argument("--host", help="Worker host/IP")
133
+ add_parser.add_argument("--port", type=int, default=9090, help="Worker port")
134
+ add_parser.add_argument("--api-key", help="Worker API key")
135
+ add_parser.add_argument(
136
+ "--discovered", action="store_true", help="Add from last scan results"
137
+ )
138
+
139
+ # custom remove
140
+ remove_parser = custom_subparsers.add_parser("remove", help="Remove a custom worker")
141
+ remove_parser.add_argument("name", help="Worker name")
142
+
143
+ # custom list
144
+ custom_subparsers.add_parser("list", help="List registered workers")
145
+
146
+ # custom test
147
+ test_parser = custom_subparsers.add_parser("test", help="Test worker connection")
148
+ test_parser.add_argument("name", help="Worker name")
149
+
150
+ # custom deploy
151
+ custom_deploy_parser = custom_subparsers.add_parser(
152
+ "deploy", help="Deploy to custom worker"
153
+ )
154
+ custom_deploy_parser.add_argument("worker_name", help="Worker to deploy to")
155
+ custom_deploy_parser.add_argument("import_source", help="Git URL or local path")
156
+ custom_deploy_parser.add_argument("--branch", "-b", help="Git branch/tag")
157
+ custom_deploy_parser.add_argument(
158
+ "--mode",
159
+ choices=["docker", "native"],
160
+ default="docker",
161
+ help="Deployment mode",
162
+ )
163
+ custom_deploy_parser.add_argument("--name", help="Environment name")
164
+
165
+ # =========================================================================
166
+ # Worker commands (runs on GPU machine, Phase 2)
167
+ # =========================================================================
168
+ worker_parser = subparsers.add_parser("worker", help="Worker server management")
169
+ worker_subparsers = worker_parser.add_subparsers(
170
+ dest="worker_command", help="Worker subcommands"
171
+ )
172
+
173
+ # worker setup
174
+ setup_parser = worker_subparsers.add_parser("setup", help="One-time worker setup")
175
+ setup_parser.add_argument("--api-key", help="Set worker API key")
176
+ setup_parser.add_argument("--workspace", help="Workspace path")
177
+
178
+ # worker up
179
+ up_parser = worker_subparsers.add_parser("up", help="Start worker server")
180
+ up_parser.add_argument("--port", type=int, default=9090, help="Server port")
181
+ up_parser.add_argument("--host", default="0.0.0.0", help="Bind host")
182
+ up_parser.add_argument(
183
+ "--mode",
184
+ choices=["docker", "native"],
185
+ default="docker",
186
+ help="Instance mode",
187
+ )
188
+ up_parser.add_argument("--broadcast", action="store_true", help="Enable mDNS broadcast")
189
+ up_parser.add_argument("--port-range", default="8200:8210", help="Instance port range")
190
+ up_parser.add_argument("--dev", action="store_true", help="Use saved dev config (from 'dev setup')")
191
+ up_parser.add_argument("--dev-core", metavar="PATH", help="Use local comfygit-core (editable)")
192
+ up_parser.add_argument("--dev-manager", metavar="PATH", help="Use local comfygit-manager")
193
+
194
+ # worker down
195
+ worker_subparsers.add_parser("down", help="Stop worker server")
196
+
197
+ # worker status
198
+ worker_subparsers.add_parser("status", help="Show worker status")
199
+
200
+ # worker regenerate-key
201
+ worker_subparsers.add_parser("regenerate-key", help="Regenerate API key")
202
+
203
+ # =========================================================================
204
+ # Dev commands (development mode setup)
205
+ # =========================================================================
206
+ dev_parser = subparsers.add_parser("dev", help="Development mode setup")
207
+ dev_subparsers = dev_parser.add_subparsers(dest="dev_command", help="Dev subcommands")
208
+
209
+ # dev setup
210
+ dev_setup_parser = dev_subparsers.add_parser(
211
+ "setup", help="Configure local dev paths for core/manager"
212
+ )
213
+ dev_setup_parser.add_argument("--core", metavar="PATH", help="Path to comfygit-core package")
214
+ dev_setup_parser.add_argument("--manager", metavar="PATH", help="Path to comfygit-manager")
215
+ dev_setup_parser.add_argument("--show", action="store_true", help="Show current dev config")
216
+ dev_setup_parser.add_argument("--clear", action="store_true", help="Clear dev config")
217
+
218
+ # dev patch
219
+ dev_patch_parser = dev_subparsers.add_parser(
220
+ "patch", help="Patch existing environments with dev config"
221
+ )
222
+ dev_patch_parser.add_argument("--env", help="Specific environment to patch (default: all)")
223
+
224
+ # dev add-node
225
+ dev_add_node_parser = dev_subparsers.add_parser(
226
+ "add-node", help="Add a dev node to be symlinked into environments"
227
+ )
228
+ dev_add_node_parser.add_argument("name", help="Node directory name (e.g., ComfyUI-Async-API)")
229
+ dev_add_node_parser.add_argument("path", help="Path to the node source directory")
230
+
231
+ # dev remove-node
232
+ dev_remove_node_parser = dev_subparsers.add_parser(
233
+ "remove-node", help="Remove a dev node from config"
234
+ )
235
+ dev_remove_node_parser.add_argument("name", help="Node name to remove")
236
+
237
+ # dev list-nodes
238
+ dev_subparsers.add_parser("list-nodes", help="List configured dev nodes")
239
+
240
+ return parser
241
+
242
+
243
+ def main(args: list[str] | None = None) -> int:
244
+ """Main entry point for cg-deploy CLI.
245
+
246
+ Args:
247
+ args: Command-line arguments (uses sys.argv if None)
248
+
249
+ Returns:
250
+ Exit code (0 for success, non-zero for errors)
251
+ """
252
+ parser = create_parser()
253
+ parsed = parser.parse_args(args)
254
+
255
+ if not parsed.command:
256
+ parser.print_help()
257
+ return 0
258
+
259
+ # Import command handlers
260
+ from .commands import instances as instance_commands
261
+ from .commands import runpod as runpod_commands
262
+
263
+ # Dispatch to command handlers
264
+ try:
265
+ if parsed.command == "runpod":
266
+ if not parsed.runpod_command:
267
+ parser.parse_args(["runpod", "--help"])
268
+ return 0
269
+ handler_map = {
270
+ "config": runpod_commands.handle_config,
271
+ "gpus": runpod_commands.handle_gpus,
272
+ "volumes": runpod_commands.handle_volumes,
273
+ "regions": runpod_commands.handle_regions,
274
+ "deploy": runpod_commands.handle_deploy,
275
+ }
276
+ handler = handler_map.get(parsed.runpod_command)
277
+ if handler:
278
+ return handler(parsed)
279
+ print(f"Unknown runpod command: {parsed.runpod_command}")
280
+ return 1
281
+
282
+ elif parsed.command == "instances":
283
+ return instance_commands.handle_instances(parsed)
284
+
285
+ elif parsed.command == "start":
286
+ return instance_commands.handle_start(parsed)
287
+
288
+ elif parsed.command == "stop":
289
+ return instance_commands.handle_stop(parsed)
290
+
291
+ elif parsed.command == "terminate":
292
+ return instance_commands.handle_terminate(parsed)
293
+
294
+ elif parsed.command == "open":
295
+ return instance_commands.handle_open(parsed)
296
+
297
+ elif parsed.command == "wait":
298
+ return instance_commands.handle_wait(parsed)
299
+
300
+ elif parsed.command == "logs":
301
+ return instance_commands.handle_logs(parsed)
302
+
303
+ elif parsed.command == "worker":
304
+ from .commands import worker as worker_commands
305
+
306
+ if not parsed.worker_command:
307
+ parser.parse_args(["worker", "--help"])
308
+ return 0
309
+ handler_map = {
310
+ "setup": worker_commands.handle_setup,
311
+ "up": worker_commands.handle_up,
312
+ "down": worker_commands.handle_down,
313
+ "status": worker_commands.handle_status,
314
+ "regenerate-key": worker_commands.handle_regenerate_key,
315
+ }
316
+ handler = handler_map.get(parsed.worker_command)
317
+ if handler:
318
+ return handler(parsed)
319
+ print(f"Unknown worker command: {parsed.worker_command}")
320
+ return 1
321
+
322
+ elif parsed.command == "custom":
323
+ from .commands import custom as custom_commands
324
+
325
+ if not parsed.custom_command:
326
+ parser.parse_args(["custom", "--help"])
327
+ return 0
328
+ handler_map = {
329
+ "scan": custom_commands.handle_scan,
330
+ "add": custom_commands.handle_add,
331
+ "remove": custom_commands.handle_remove,
332
+ "list": custom_commands.handle_list,
333
+ "test": custom_commands.handle_test,
334
+ "deploy": custom_commands.handle_deploy,
335
+ }
336
+ handler = handler_map.get(parsed.custom_command)
337
+ if handler:
338
+ return handler(parsed)
339
+ print(f"Unknown custom command: {parsed.custom_command}")
340
+ return 1
341
+
342
+ elif parsed.command == "dev":
343
+ from .commands import dev as dev_commands
344
+
345
+ if not parsed.dev_command:
346
+ parser.parse_args(["dev", "--help"])
347
+ return 0
348
+ handler_map = {
349
+ "setup": dev_commands.handle_setup,
350
+ "patch": dev_commands.handle_patch,
351
+ "add-node": dev_commands.handle_add_node,
352
+ "remove-node": dev_commands.handle_remove_node,
353
+ "list-nodes": dev_commands.handle_list_nodes,
354
+ }
355
+ handler = handler_map.get(parsed.dev_command)
356
+ if handler:
357
+ return handler(parsed)
358
+ print(f"Unknown dev command: {parsed.dev_command}")
359
+ return 1
360
+
361
+ else:
362
+ print(f"Unknown command: {parsed.command}")
363
+ return 1
364
+
365
+ except KeyboardInterrupt:
366
+ print("\nCancelled.")
367
+ return 130
368
+ except Exception as e:
369
+ print(f"Error: {e}")
370
+ return 1
371
+
372
+
373
+ if __name__ == "__main__":
374
+ sys.exit(main())
@@ -0,0 +1,5 @@
1
+ """CLI command implementations."""
2
+
3
+ from . import custom, instances, runpod, worker
4
+
5
+ __all__ = ["custom", "instances", "runpod", "worker"]