comfygit 0.3.8.dev1__tar.gz → 0.3.10__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.
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/PKG-INFO +2 -2
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/cli.py +36 -1
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/cli_utils.py +1 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/env_commands.py +104 -16
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/global_commands.py +23 -48
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/pyproject.toml +2 -2
- comfygit-0.3.10/tests/test_manager_commands.py +319 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_torch_backend_cli.py +18 -18
- comfygit-0.3.8.dev1/tests/test_init_system_nodes.py +0 -126
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/.github/workflows/publish_pypi.yml +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/.gitignore +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/.python-version +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/CLAUDE.md +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/LICENSE.txt +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/README.md +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/__init__.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/__main__.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/completers.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/completion_commands.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/formatters/__init__.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/formatters/error_formatter.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/interactive/__init__.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/logging/compressed_handler.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/logging/environment_logger.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/logging/log_compressor.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/logging/logging_config.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/resolution_strategies.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/strategies/__init__.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/strategies/conflict_resolver.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/strategies/interactive.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/strategies/rollback.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/utils/__init__.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/utils/civitai_errors.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/utils/orchestrator.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/utils/pagination.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/comfygit_cli/utils/progress.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/docs/architecture.md +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/README_REGISTRY.md +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/augment_mappings.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/build_global_mappings.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/build_registry_cache.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/extract_builtin_nodes.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/extract_node_modules.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/get_hash.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/global-node-mappings.md +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/registry.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/registry_client.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/scripts/test_concurrent_api.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/conftest.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_batch_node_add.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_batch_node_remove.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_branch_commands.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_completers.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_completion_commands.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_conflict_resolver.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_detached_head_display.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_error_formatter.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_log_command.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_logging_structure.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_manifest_command.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_pagination.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_preview_display.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_py_commands.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_py_remove_group_commands.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_status_disabled_nodes_display.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_status_displays_uninstalled_nodes.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_status_path_sync_display.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_status_real_bug_scenario.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_status_suggestions.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_status_uninstalled_reporting.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_update_workflow_model_paths.py +0 -0
- {comfygit-0.3.8.dev1 → comfygit-0.3.10}/tests/test_workflow_model_importance.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: comfygit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.10
|
|
4
4
|
Summary: ComfyGit - Git-based environment management for ComfyUI
|
|
5
5
|
License-File: LICENSE.txt
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Requires-Dist: argcomplete>=3.5.0
|
|
8
|
-
Requires-Dist: comfygit-core==0.3.
|
|
8
|
+
Requires-Dist: comfygit-core==0.3.10
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
|
|
11
11
|
# ComfyGit CLI
|
|
@@ -165,7 +165,6 @@ def _add_global_commands(subparsers: argparse._SubParsersAction) -> None:
|
|
|
165
165
|
init_parser.add_argument("path", type=Path, nargs="?", help="Workspace directory (default: ~/comfygit)")
|
|
166
166
|
init_parser.add_argument("--models-dir", type=Path, help="Path to existing models directory to index")
|
|
167
167
|
init_parser.add_argument("--yes", "-y", action="store_true", help="Use all defaults, no interactive prompts")
|
|
168
|
-
init_parser.add_argument("--bare", action="store_true", help="Create workspace without system nodes (comfygit-manager)")
|
|
169
168
|
init_parser.set_defaults(func=global_cmds.init)
|
|
170
169
|
|
|
171
170
|
# list - List all environments
|
|
@@ -349,6 +348,26 @@ def _add_global_commands(subparsers: argparse._SubParsersAction) -> None:
|
|
|
349
348
|
orch_logs_parser.add_argument("-n", "--lines", type=int, default=50, help="Number of lines to show (default: 50)")
|
|
350
349
|
orch_logs_parser.set_defaults(func=global_cmds.orch_logs)
|
|
351
350
|
|
|
351
|
+
# Workspace management subcommands
|
|
352
|
+
workspace_parser = subparsers.add_parser("workspace", help="Workspace operations")
|
|
353
|
+
workspace_subparsers = workspace_parser.add_subparsers(
|
|
354
|
+
dest="workspace_command",
|
|
355
|
+
help="Workspace commands"
|
|
356
|
+
)
|
|
357
|
+
workspace_parser.set_defaults(func=_make_help_func(workspace_parser))
|
|
358
|
+
|
|
359
|
+
# workspace cleanup
|
|
360
|
+
workspace_cleanup_parser = workspace_subparsers.add_parser(
|
|
361
|
+
"cleanup",
|
|
362
|
+
help="Remove legacy workspace artifacts"
|
|
363
|
+
)
|
|
364
|
+
workspace_cleanup_parser.add_argument(
|
|
365
|
+
"--force",
|
|
366
|
+
action="store_true",
|
|
367
|
+
help="Skip verification and force cleanup"
|
|
368
|
+
)
|
|
369
|
+
workspace_cleanup_parser.set_defaults(func=global_cmds.workspace_cleanup)
|
|
370
|
+
|
|
352
371
|
|
|
353
372
|
def _add_env_commands(subparsers: argparse._SubParsersAction) -> None:
|
|
354
373
|
"""Add environment-specific commands."""
|
|
@@ -796,5 +815,21 @@ def _add_env_commands(subparsers: argparse._SubParsersAction) -> None:
|
|
|
796
815
|
)
|
|
797
816
|
py_uv_parser.set_defaults(func=env_cmds.py_uv)
|
|
798
817
|
|
|
818
|
+
# Manager subcommands (per-environment comfygit-manager)
|
|
819
|
+
manager_parser = subparsers.add_parser("manager", help="Manage comfygit-manager installation")
|
|
820
|
+
manager_subparsers = manager_parser.add_subparsers(dest="manager_command", help="Manager commands")
|
|
821
|
+
manager_parser.set_defaults(func=_make_help_func(manager_parser))
|
|
822
|
+
|
|
823
|
+
# manager status
|
|
824
|
+
manager_status_parser = manager_subparsers.add_parser("status", help="Show manager version and update availability")
|
|
825
|
+
manager_status_parser.set_defaults(func=env_cmds.manager_status)
|
|
826
|
+
|
|
827
|
+
# manager update
|
|
828
|
+
manager_update_parser = manager_subparsers.add_parser("update", help="Update or migrate comfygit-manager")
|
|
829
|
+
manager_update_parser.add_argument("--version", help="Target version (default: latest)")
|
|
830
|
+
manager_update_parser.add_argument("-y", "--yes", action="store_true", help="Skip confirmation prompts")
|
|
831
|
+
manager_update_parser.set_defaults(func=env_cmds.manager_update)
|
|
832
|
+
|
|
833
|
+
|
|
799
834
|
if __name__ == "__main__":
|
|
800
835
|
main()
|
|
@@ -88,6 +88,13 @@ class EnvironmentCommands:
|
|
|
88
88
|
sys.exit(1)
|
|
89
89
|
return active
|
|
90
90
|
|
|
91
|
+
def _get_python_version(self, env: Environment) -> str:
|
|
92
|
+
"""Get Python version from environment."""
|
|
93
|
+
python_version_file = env.cec_path / ".python-version"
|
|
94
|
+
if python_version_file.exists():
|
|
95
|
+
return python_version_file.read_text(encoding="utf-8").strip()
|
|
96
|
+
return "3.12"
|
|
97
|
+
|
|
91
98
|
def _get_or_probe_backend(
|
|
92
99
|
self, env: Environment, override: str | None = None
|
|
93
100
|
) -> tuple[str, bool]:
|
|
@@ -104,29 +111,34 @@ class EnvironmentCommands:
|
|
|
104
111
|
if override:
|
|
105
112
|
return override, False
|
|
106
113
|
|
|
107
|
-
|
|
108
|
-
return env.pytorch_manager.get_backend(), False
|
|
114
|
+
had_backend = env.pytorch_manager.has_backend()
|
|
109
115
|
|
|
110
|
-
# No backend configured - probe and set it
|
|
111
|
-
print("⚠️ No PyTorch backend configured. Auto-detecting...")
|
|
112
116
|
try:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
)
|
|
124
|
-
return backend, True
|
|
117
|
+
python_version = self._get_python_version(env)
|
|
118
|
+
backend = env.pytorch_manager.ensure_backend(python_version)
|
|
119
|
+
|
|
120
|
+
was_probed = not had_backend
|
|
121
|
+
if was_probed:
|
|
122
|
+
print("⚠️ No PyTorch backend configured. Auto-detecting...")
|
|
123
|
+
print(f"✓ Backend detected and saved: {backend}")
|
|
124
|
+
print(" To change: cg env-config torch-backend set <backend>")
|
|
125
|
+
|
|
126
|
+
return backend, was_probed
|
|
125
127
|
except Exception as e:
|
|
126
128
|
print(f"✗ Error probing PyTorch backend: {e}")
|
|
127
129
|
print(" Try setting it explicitly: cg env-config torch-backend set <backend>")
|
|
128
130
|
sys.exit(1)
|
|
129
131
|
|
|
132
|
+
def _show_legacy_manager_notice(self, env: Environment) -> None:
|
|
133
|
+
"""Show legacy manager notice if environment uses symlinked manager."""
|
|
134
|
+
try:
|
|
135
|
+
status = env.get_manager_status()
|
|
136
|
+
if status.is_legacy:
|
|
137
|
+
print("")
|
|
138
|
+
print("Legacy manager detected. Run 'cg manager update' to migrate.")
|
|
139
|
+
except Exception:
|
|
140
|
+
pass # Silently fail - notice is informational only
|
|
141
|
+
|
|
130
142
|
def _format_size(self, size_bytes: int) -> str:
|
|
131
143
|
"""Format bytes as human-readable size."""
|
|
132
144
|
for unit in ("B", "KB", "MB", "GB"):
|
|
@@ -611,6 +623,9 @@ class EnvironmentCommands:
|
|
|
611
623
|
|
|
612
624
|
print("\n✓ No workflows")
|
|
613
625
|
print("✓ No uncommitted changes")
|
|
626
|
+
|
|
627
|
+
# Show legacy manager notice even in clean state
|
|
628
|
+
self._show_legacy_manager_notice(env)
|
|
614
629
|
return
|
|
615
630
|
|
|
616
631
|
# Show environment name with branch
|
|
@@ -761,6 +776,9 @@ class EnvironmentCommands:
|
|
|
761
776
|
# Suggested actions - smart and contextual
|
|
762
777
|
self._show_smart_suggestions(status)
|
|
763
778
|
|
|
779
|
+
# Show legacy manager notice if applicable
|
|
780
|
+
self._show_legacy_manager_notice(env)
|
|
781
|
+
|
|
764
782
|
# Removed: _has_uninstalled_packages - this logic is now in core's WorkflowAnalysisStatus
|
|
765
783
|
|
|
766
784
|
def _print_workflow_issues(self, wf_analysis: WorkflowAnalysisStatus, verbose: bool = False) -> None:
|
|
@@ -2950,3 +2968,73 @@ class EnvironmentCommands:
|
|
|
2950
2968
|
print(f" {m.actual_category}/{m.name} → {expected}/")
|
|
2951
2969
|
else:
|
|
2952
2970
|
print("✓ No changes needed - all dependencies already resolved")
|
|
2971
|
+
|
|
2972
|
+
# ================================================================================
|
|
2973
|
+
# Manager Commands - Per-environment comfygit-manager management
|
|
2974
|
+
# ================================================================================
|
|
2975
|
+
|
|
2976
|
+
@with_env_logging("manager status")
|
|
2977
|
+
def manager_status(self, args: argparse.Namespace, logger: Any = None) -> None:
|
|
2978
|
+
"""Show manager version and update availability."""
|
|
2979
|
+
env = self._get_env(args)
|
|
2980
|
+
|
|
2981
|
+
status = env.get_manager_status()
|
|
2982
|
+
|
|
2983
|
+
print("comfygit-manager")
|
|
2984
|
+
print(f" Current: {status.current_version or 'not installed'}")
|
|
2985
|
+
print(f" Latest: {status.latest_version or 'unknown'}")
|
|
2986
|
+
|
|
2987
|
+
if status.is_legacy:
|
|
2988
|
+
print(" Legacy installation (symlinked)")
|
|
2989
|
+
print(f" Run 'cg -e {env.name} manager update' to migrate")
|
|
2990
|
+
elif not status.is_tracked:
|
|
2991
|
+
print(" Not installed")
|
|
2992
|
+
print(f" Run 'cg -e {env.name} manager update' to install")
|
|
2993
|
+
elif status.update_available:
|
|
2994
|
+
print(" Update available!")
|
|
2995
|
+
else:
|
|
2996
|
+
print(" Up to date")
|
|
2997
|
+
|
|
2998
|
+
@with_env_logging("manager update")
|
|
2999
|
+
def manager_update(self, args: argparse.Namespace, logger: Any = None) -> None:
|
|
3000
|
+
"""Update or migrate comfygit-manager."""
|
|
3001
|
+
from comfygit_core.strategies.confirmation import AutoConfirmStrategy, InteractiveConfirmStrategy
|
|
3002
|
+
|
|
3003
|
+
env = self._get_env(args)
|
|
3004
|
+
version = getattr(args, 'version', None) or "latest"
|
|
3005
|
+
use_yes = getattr(args, 'yes', False)
|
|
3006
|
+
|
|
3007
|
+
# Ensure backend is configured (same as sync/run commands)
|
|
3008
|
+
had_backend = env.pytorch_manager.has_backend()
|
|
3009
|
+
if not had_backend:
|
|
3010
|
+
print("⚠️ No PyTorch backend configured. Auto-detecting...")
|
|
3011
|
+
python_version = self._get_python_version(env)
|
|
3012
|
+
backend = env.pytorch_manager.ensure_backend(python_version)
|
|
3013
|
+
print(f"✓ Backend detected and saved: {backend}")
|
|
3014
|
+
print(" To change: cg env-config torch-backend set <backend>\n")
|
|
3015
|
+
|
|
3016
|
+
status = env.get_manager_status()
|
|
3017
|
+
|
|
3018
|
+
if status.is_legacy:
|
|
3019
|
+
print("Migrating comfygit-manager to per-environment installation...")
|
|
3020
|
+
elif not status.is_tracked:
|
|
3021
|
+
print("Installing comfygit-manager...")
|
|
3022
|
+
else:
|
|
3023
|
+
print("Updating comfygit-manager...")
|
|
3024
|
+
|
|
3025
|
+
strategy = AutoConfirmStrategy() if use_yes else InteractiveConfirmStrategy()
|
|
3026
|
+
|
|
3027
|
+
try:
|
|
3028
|
+
result = env.update_manager(version=version, confirmation_strategy=strategy)
|
|
3029
|
+
|
|
3030
|
+
if result.changed:
|
|
3031
|
+
print(f" {result.message}")
|
|
3032
|
+
print("\n Restart this environment to use the new version")
|
|
3033
|
+
else:
|
|
3034
|
+
print(f" {result.message}")
|
|
3035
|
+
|
|
3036
|
+
except Exception as e:
|
|
3037
|
+
print(f" Failed to update manager: {e}", file=sys.stderr)
|
|
3038
|
+
if logger:
|
|
3039
|
+
logger.error(f"Manager update failed: {e}", exc_info=True)
|
|
3040
|
+
sys.exit(1)
|
|
@@ -16,15 +16,6 @@ from .utils import create_progress_callback, paginate, show_civitai_auth_help, s
|
|
|
16
16
|
|
|
17
17
|
logger = get_logger(__name__)
|
|
18
18
|
|
|
19
|
-
# Default system nodes to install with new workspaces.
|
|
20
|
-
# These are infrastructure custom nodes that provide management capabilities.
|
|
21
|
-
# Use `cg init --bare` to skip installation.
|
|
22
|
-
DEFAULT_SYSTEM_NODES = {
|
|
23
|
-
"comfygit-manager": {
|
|
24
|
-
"url": "https://github.com/comfyhub-org/comfygit-manager.git",
|
|
25
|
-
"description": "ComfyGit management panel for ComfyUI",
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
19
|
|
|
29
20
|
|
|
30
21
|
class GlobalCommands:
|
|
@@ -141,12 +132,6 @@ class GlobalCommands:
|
|
|
141
132
|
|
|
142
133
|
print(f"✓ Workspace initialized at {workspace.path}")
|
|
143
134
|
|
|
144
|
-
# Install default system nodes (unless --bare)
|
|
145
|
-
if not getattr(args, 'bare', False):
|
|
146
|
-
self._install_system_nodes(workspace)
|
|
147
|
-
else:
|
|
148
|
-
print("📦 Skipping system node installation (--bare flag)")
|
|
149
|
-
|
|
150
135
|
# Handle models directory setup
|
|
151
136
|
self._setup_models_directory(workspace, args)
|
|
152
137
|
|
|
@@ -162,39 +147,6 @@ class GlobalCommands:
|
|
|
162
147
|
print(f"✗ Failed to initialize workspace: {e}", file=sys.stderr)
|
|
163
148
|
sys.exit(1)
|
|
164
149
|
|
|
165
|
-
def _install_system_nodes(self, workspace: Workspace) -> None:
|
|
166
|
-
"""Install default system nodes (comfygit-manager) into workspace.
|
|
167
|
-
|
|
168
|
-
System nodes are infrastructure custom nodes that:
|
|
169
|
-
- Live at workspace level (.metadata/system_nodes/)
|
|
170
|
-
- Are symlinked into every environment
|
|
171
|
-
- Are never tracked in pyproject.toml
|
|
172
|
-
"""
|
|
173
|
-
from comfygit_core.utils.git import git_clone
|
|
174
|
-
|
|
175
|
-
system_nodes_path = workspace.paths.system_nodes
|
|
176
|
-
|
|
177
|
-
for node_name, node_config in DEFAULT_SYSTEM_NODES.items():
|
|
178
|
-
target_path = system_nodes_path / node_name
|
|
179
|
-
|
|
180
|
-
if target_path.exists():
|
|
181
|
-
logger.debug(f"System node '{node_name}' already exists, skipping")
|
|
182
|
-
continue
|
|
183
|
-
|
|
184
|
-
print(f"📦 Installing system node: {node_name}")
|
|
185
|
-
try:
|
|
186
|
-
git_clone(
|
|
187
|
-
url=node_config["url"],
|
|
188
|
-
target_path=target_path,
|
|
189
|
-
depth=1 # Shallow clone for speed
|
|
190
|
-
)
|
|
191
|
-
print(f" ✓ Installed {node_name}")
|
|
192
|
-
logger.info(f"Installed system node: {node_name}")
|
|
193
|
-
except Exception as e:
|
|
194
|
-
print(f" ⚠️ Failed to install {node_name}: {e}")
|
|
195
|
-
print(" You can install it manually later")
|
|
196
|
-
logger.warning(f"Failed to install system node {node_name}: {e}")
|
|
197
|
-
|
|
198
150
|
def _show_workspace_env_setup(self, workspace_path: Path) -> None:
|
|
199
151
|
"""Show instructions for setting COMFYGIT_HOME for custom workspace location."""
|
|
200
152
|
import os
|
|
@@ -1811,3 +1763,26 @@ class GlobalCommands:
|
|
|
1811
1763
|
else:
|
|
1812
1764
|
print("(empty log file)")
|
|
1813
1765
|
|
|
1766
|
+
# === Workspace Management ===
|
|
1767
|
+
|
|
1768
|
+
@with_workspace_logging("workspace cleanup")
|
|
1769
|
+
def workspace_cleanup(self, args: argparse.Namespace) -> None:
|
|
1770
|
+
"""Clean up legacy workspace artifacts.
|
|
1771
|
+
|
|
1772
|
+
Removes .metadata/system_nodes/ directory if no environments
|
|
1773
|
+
still use legacy symlinked manager.
|
|
1774
|
+
"""
|
|
1775
|
+
force = getattr(args, 'force', False)
|
|
1776
|
+
|
|
1777
|
+
result = self.workspace.cleanup_legacy_system_nodes(force=force)
|
|
1778
|
+
|
|
1779
|
+
if result.success:
|
|
1780
|
+
print(f"Removed {result.removed_path}")
|
|
1781
|
+
else:
|
|
1782
|
+
if result.legacy_environments:
|
|
1783
|
+
print("Cannot cleanup: Some environments still use legacy manager")
|
|
1784
|
+
for env in result.legacy_environments:
|
|
1785
|
+
print(f" {env}")
|
|
1786
|
+
print("\nRun 'cg -e <ENV> manager update' to migrate, then retry.")
|
|
1787
|
+
else:
|
|
1788
|
+
print(f"{result.message}")
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "comfygit"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.10"
|
|
4
4
|
description = "ComfyGit - Git-based environment management for ComfyUI"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"comfygit-core==0.3.
|
|
8
|
+
"comfygit-core==0.3.10",
|
|
9
9
|
"argcomplete>=3.5.0",
|
|
10
10
|
]
|
|
11
11
|
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""Tests for manager commands (cg manager status/update)."""
|
|
2
|
+
import argparse
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from comfygit_core.models.shared import ManagerStatus, ManagerUpdateResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestManagerCommands:
|
|
11
|
+
"""Tests for manager status and update CLI commands."""
|
|
12
|
+
|
|
13
|
+
def test_manager_status_shows_not_installed(self, capsys):
|
|
14
|
+
"""manager status shows 'not installed' when manager is missing."""
|
|
15
|
+
from comfygit_cli.env_commands import EnvironmentCommands
|
|
16
|
+
|
|
17
|
+
env_cmds = EnvironmentCommands()
|
|
18
|
+
|
|
19
|
+
# Mock environment
|
|
20
|
+
mock_env = MagicMock()
|
|
21
|
+
mock_env.name = "test-env"
|
|
22
|
+
mock_env.get_manager_status.return_value = ManagerStatus(
|
|
23
|
+
current_version=None,
|
|
24
|
+
latest_version="0.3.0",
|
|
25
|
+
update_available=False,
|
|
26
|
+
is_legacy=False,
|
|
27
|
+
is_tracked=False,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
with patch.object(env_cmds, "_get_env", return_value=mock_env):
|
|
31
|
+
args = argparse.Namespace(target_env="test-env")
|
|
32
|
+
env_cmds.manager_status(args)
|
|
33
|
+
|
|
34
|
+
captured = capsys.readouterr()
|
|
35
|
+
assert "not installed" in captured.out
|
|
36
|
+
assert "manager update" in captured.out
|
|
37
|
+
|
|
38
|
+
def test_manager_status_shows_legacy(self, capsys):
|
|
39
|
+
"""manager status shows legacy notice for symlinked installations."""
|
|
40
|
+
from comfygit_cli.env_commands import EnvironmentCommands
|
|
41
|
+
|
|
42
|
+
env_cmds = EnvironmentCommands()
|
|
43
|
+
|
|
44
|
+
mock_env = MagicMock()
|
|
45
|
+
mock_env.name = "test-env"
|
|
46
|
+
mock_env.get_manager_status.return_value = ManagerStatus(
|
|
47
|
+
current_version="0.2.0",
|
|
48
|
+
latest_version="0.3.0",
|
|
49
|
+
update_available=True,
|
|
50
|
+
is_legacy=True,
|
|
51
|
+
is_tracked=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
with patch.object(env_cmds, "_get_env", return_value=mock_env):
|
|
55
|
+
args = argparse.Namespace(target_env="test-env")
|
|
56
|
+
env_cmds.manager_status(args)
|
|
57
|
+
|
|
58
|
+
captured = capsys.readouterr()
|
|
59
|
+
assert "Legacy" in captured.out
|
|
60
|
+
assert "manager update" in captured.out
|
|
61
|
+
|
|
62
|
+
def test_manager_status_shows_update_available(self, capsys):
|
|
63
|
+
"""manager status shows update available for tracked installations."""
|
|
64
|
+
from comfygit_cli.env_commands import EnvironmentCommands
|
|
65
|
+
|
|
66
|
+
env_cmds = EnvironmentCommands()
|
|
67
|
+
|
|
68
|
+
mock_env = MagicMock()
|
|
69
|
+
mock_env.name = "test-env"
|
|
70
|
+
mock_env.get_manager_status.return_value = ManagerStatus(
|
|
71
|
+
current_version="0.2.0",
|
|
72
|
+
latest_version="0.3.0",
|
|
73
|
+
update_available=True,
|
|
74
|
+
is_legacy=False,
|
|
75
|
+
is_tracked=True,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
with patch.object(env_cmds, "_get_env", return_value=mock_env):
|
|
79
|
+
args = argparse.Namespace(target_env="test-env")
|
|
80
|
+
env_cmds.manager_status(args)
|
|
81
|
+
|
|
82
|
+
captured = capsys.readouterr()
|
|
83
|
+
assert "Update available" in captured.out
|
|
84
|
+
assert "0.2.0" in captured.out
|
|
85
|
+
assert "0.3.0" in captured.out
|
|
86
|
+
|
|
87
|
+
def test_manager_status_shows_up_to_date(self, capsys):
|
|
88
|
+
"""manager status shows up to date when no update needed."""
|
|
89
|
+
from comfygit_cli.env_commands import EnvironmentCommands
|
|
90
|
+
|
|
91
|
+
env_cmds = EnvironmentCommands()
|
|
92
|
+
|
|
93
|
+
mock_env = MagicMock()
|
|
94
|
+
mock_env.name = "test-env"
|
|
95
|
+
mock_env.get_manager_status.return_value = ManagerStatus(
|
|
96
|
+
current_version="0.3.0",
|
|
97
|
+
latest_version="0.3.0",
|
|
98
|
+
update_available=False,
|
|
99
|
+
is_legacy=False,
|
|
100
|
+
is_tracked=True,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
with patch.object(env_cmds, "_get_env", return_value=mock_env):
|
|
104
|
+
args = argparse.Namespace(target_env="test-env")
|
|
105
|
+
env_cmds.manager_status(args)
|
|
106
|
+
|
|
107
|
+
captured = capsys.readouterr()
|
|
108
|
+
assert "Up to date" in captured.out
|
|
109
|
+
|
|
110
|
+
def test_manager_update_calls_env_update_manager(self, capsys):
|
|
111
|
+
"""manager update calls environment update_manager method."""
|
|
112
|
+
from comfygit_cli.env_commands import EnvironmentCommands
|
|
113
|
+
|
|
114
|
+
env_cmds = EnvironmentCommands()
|
|
115
|
+
|
|
116
|
+
mock_env = MagicMock()
|
|
117
|
+
mock_env.name = "test-env"
|
|
118
|
+
mock_env.get_manager_status.return_value = ManagerStatus(
|
|
119
|
+
current_version="0.2.0",
|
|
120
|
+
latest_version="0.3.0",
|
|
121
|
+
update_available=True,
|
|
122
|
+
is_legacy=False,
|
|
123
|
+
is_tracked=True,
|
|
124
|
+
)
|
|
125
|
+
mock_env.update_manager.return_value = ManagerUpdateResult(
|
|
126
|
+
changed=True,
|
|
127
|
+
message="Updated from 0.2.0 to 0.3.0",
|
|
128
|
+
old_version="0.2.0",
|
|
129
|
+
new_version="0.3.0",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
with patch.object(env_cmds, "_get_env", return_value=mock_env):
|
|
133
|
+
args = argparse.Namespace(target_env="test-env", version=None, yes=True)
|
|
134
|
+
env_cmds.manager_update(args)
|
|
135
|
+
|
|
136
|
+
mock_env.update_manager.assert_called_once()
|
|
137
|
+
captured = capsys.readouterr()
|
|
138
|
+
assert "Updated" in captured.out or "Updating" in captured.out
|
|
139
|
+
|
|
140
|
+
def test_manager_update_shows_migration_message_for_legacy(self, capsys):
|
|
141
|
+
"""manager update shows migration message for legacy installations."""
|
|
142
|
+
from comfygit_cli.env_commands import EnvironmentCommands
|
|
143
|
+
|
|
144
|
+
env_cmds = EnvironmentCommands()
|
|
145
|
+
|
|
146
|
+
mock_env = MagicMock()
|
|
147
|
+
mock_env.name = "test-env"
|
|
148
|
+
mock_env.get_manager_status.return_value = ManagerStatus(
|
|
149
|
+
current_version="0.2.0",
|
|
150
|
+
latest_version="0.3.0",
|
|
151
|
+
update_available=True,
|
|
152
|
+
is_legacy=True,
|
|
153
|
+
is_tracked=False,
|
|
154
|
+
)
|
|
155
|
+
mock_env.update_manager.return_value = ManagerUpdateResult(
|
|
156
|
+
changed=True,
|
|
157
|
+
was_migration=True,
|
|
158
|
+
message="Migrated and updated to 0.3.0",
|
|
159
|
+
old_version="0.2.0",
|
|
160
|
+
new_version="0.3.0",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
with patch.object(env_cmds, "_get_env", return_value=mock_env):
|
|
164
|
+
args = argparse.Namespace(target_env="test-env", version=None, yes=True)
|
|
165
|
+
env_cmds.manager_update(args)
|
|
166
|
+
|
|
167
|
+
captured = capsys.readouterr()
|
|
168
|
+
assert "Migrating" in captured.out
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class TestInitNoSystemNodes:
|
|
172
|
+
"""Tests that init no longer installs system nodes."""
|
|
173
|
+
|
|
174
|
+
def test_init_does_not_install_system_nodes(self, tmp_path):
|
|
175
|
+
"""init should not call _install_system_nodes (removed)."""
|
|
176
|
+
from comfygit_cli.global_commands import GlobalCommands
|
|
177
|
+
|
|
178
|
+
global_cmds = GlobalCommands()
|
|
179
|
+
|
|
180
|
+
# Mock workspace factory and creation
|
|
181
|
+
mock_workspace = MagicMock()
|
|
182
|
+
mock_workspace.paths.root = tmp_path
|
|
183
|
+
mock_workspace.path = tmp_path
|
|
184
|
+
mock_workspace.update_registry_data.return_value = True
|
|
185
|
+
mock_workspace.get_models_directory.return_value = tmp_path / "models"
|
|
186
|
+
|
|
187
|
+
with patch("comfygit_cli.global_commands.WorkspaceFactory") as mock_factory:
|
|
188
|
+
mock_factory.get_paths.return_value = mock_workspace.paths
|
|
189
|
+
mock_factory.create.return_value = mock_workspace
|
|
190
|
+
|
|
191
|
+
with patch.object(global_cmds, "_setup_models_directory"):
|
|
192
|
+
args = argparse.Namespace(
|
|
193
|
+
path=None,
|
|
194
|
+
models_dir=None,
|
|
195
|
+
yes=True,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
global_cmds.init(args)
|
|
199
|
+
|
|
200
|
+
# Verify workspace was created (no system nodes installation)
|
|
201
|
+
mock_factory.create.assert_called_once()
|
|
202
|
+
|
|
203
|
+
def test_bare_flag_no_longer_exists(self):
|
|
204
|
+
"""--bare flag should not exist in init parser."""
|
|
205
|
+
from comfygit_cli.cli import create_parser
|
|
206
|
+
|
|
207
|
+
parser = create_parser()
|
|
208
|
+
|
|
209
|
+
# Find the init subparser through the subparsers action
|
|
210
|
+
init_action = None
|
|
211
|
+
for action in parser._subparsers._actions:
|
|
212
|
+
if hasattr(action, "choices") and action.choices is not None:
|
|
213
|
+
if "init" in action.choices:
|
|
214
|
+
init_action = action.choices["init"]
|
|
215
|
+
break
|
|
216
|
+
|
|
217
|
+
assert init_action is not None, "init subparser should exist"
|
|
218
|
+
|
|
219
|
+
# Check that --bare is not in the init parser
|
|
220
|
+
option_strings = []
|
|
221
|
+
for action in init_action._actions:
|
|
222
|
+
option_strings.extend(action.option_strings)
|
|
223
|
+
|
|
224
|
+
assert "--bare" not in option_strings
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class TestStatusLegacyManagerNotice:
|
|
228
|
+
"""Tests that status command shows legacy manager notice."""
|
|
229
|
+
|
|
230
|
+
def test_status_shows_legacy_notice_even_when_clean(self, capsys):
|
|
231
|
+
"""status should show legacy manager notice even when environment is clean.
|
|
232
|
+
|
|
233
|
+
Bug: Previously the status command returned early for clean environments,
|
|
234
|
+
skipping the legacy manager notice check.
|
|
235
|
+
"""
|
|
236
|
+
from comfygit_cli.env_commands import EnvironmentCommands
|
|
237
|
+
from comfygit_core.models.environment import (
|
|
238
|
+
EnvironmentComparison,
|
|
239
|
+
EnvironmentStatus,
|
|
240
|
+
GitStatus,
|
|
241
|
+
)
|
|
242
|
+
from comfygit_core.models.shared import ManagerStatus
|
|
243
|
+
from comfygit_core.models.workflow import DetailedWorkflowStatus, WorkflowSyncStatus
|
|
244
|
+
|
|
245
|
+
env_cmds = EnvironmentCommands()
|
|
246
|
+
|
|
247
|
+
# Mock a clean environment with legacy manager
|
|
248
|
+
mock_env = MagicMock()
|
|
249
|
+
mock_env.name = "test-env"
|
|
250
|
+
|
|
251
|
+
# Create clean status (no workflows, no changes, synced)
|
|
252
|
+
mock_env.status.return_value = EnvironmentStatus(
|
|
253
|
+
git=GitStatus(
|
|
254
|
+
current_branch="main",
|
|
255
|
+
has_changes=False,
|
|
256
|
+
),
|
|
257
|
+
workflow=DetailedWorkflowStatus(
|
|
258
|
+
sync_status=WorkflowSyncStatus(
|
|
259
|
+
synced=[],
|
|
260
|
+
new=[],
|
|
261
|
+
modified=[],
|
|
262
|
+
deleted=[],
|
|
263
|
+
),
|
|
264
|
+
analyzed_workflows=[],
|
|
265
|
+
),
|
|
266
|
+
comparison=EnvironmentComparison(
|
|
267
|
+
missing_nodes=[],
|
|
268
|
+
extra_nodes=[],
|
|
269
|
+
version_mismatches=[],
|
|
270
|
+
packages_in_sync=True,
|
|
271
|
+
),
|
|
272
|
+
missing_models=[],
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Legacy manager detected
|
|
276
|
+
mock_env.get_manager_status.return_value = ManagerStatus(
|
|
277
|
+
current_version="0.2.0",
|
|
278
|
+
latest_version="0.3.0",
|
|
279
|
+
update_available=True,
|
|
280
|
+
is_legacy=True,
|
|
281
|
+
is_tracked=False,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
with patch.object(env_cmds, "_get_env", return_value=mock_env):
|
|
285
|
+
args = argparse.Namespace(target_env="test-env", verbose=False)
|
|
286
|
+
env_cmds.status(args)
|
|
287
|
+
|
|
288
|
+
captured = capsys.readouterr()
|
|
289
|
+
# Should show clean state
|
|
290
|
+
assert "No workflows" in captured.out
|
|
291
|
+
assert "No uncommitted changes" in captured.out
|
|
292
|
+
# AND also show legacy notice
|
|
293
|
+
assert "Legacy manager detected" in captured.out
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class TestLegacyWorkspaceNotice:
|
|
297
|
+
"""Tests for legacy notice in get_workspace_or_exit.
|
|
298
|
+
|
|
299
|
+
Note: Legacy notices have been moved to per-environment level.
|
|
300
|
+
The workspace-level notice has been removed. These tests verify
|
|
301
|
+
that get_workspace_or_exit() does NOT show legacy notices.
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
def test_no_legacy_notice_at_workspace_level(self, capsys):
|
|
305
|
+
"""Workspace-level legacy notice has been removed."""
|
|
306
|
+
from comfygit_cli import cli_utils
|
|
307
|
+
|
|
308
|
+
mock_workspace = MagicMock()
|
|
309
|
+
mock_workspace.has_legacy_system_nodes.return_value = True
|
|
310
|
+
|
|
311
|
+
with patch.object(cli_utils.WorkspaceFactory, "find", return_value=mock_workspace):
|
|
312
|
+
with patch.object(cli_utils.WorkspaceLogger, "set_workspace_path"):
|
|
313
|
+
result = cli_utils.get_workspace_or_exit()
|
|
314
|
+
|
|
315
|
+
assert result == mock_workspace
|
|
316
|
+
captured = capsys.readouterr()
|
|
317
|
+
# Legacy notices are now per-environment, not workspace-level
|
|
318
|
+
assert "Legacy workspace" not in captured.out
|
|
319
|
+
assert "Legacy" not in captured.out
|
|
@@ -152,15 +152,17 @@ class TestSyncBehavior:
|
|
|
152
152
|
"""Test that sync reads from file and doesn't overwrite user settings."""
|
|
153
153
|
|
|
154
154
|
@patch('comfygit_cli.env_commands.get_workspace_or_exit')
|
|
155
|
-
def
|
|
156
|
-
"""Sync should
|
|
155
|
+
def test_sync_uses_ensure_backend(self, mock_get_workspace):
|
|
156
|
+
"""Sync should use ensure_backend() which handles both existing and missing backends."""
|
|
157
157
|
from comfygit_cli.env_commands import EnvironmentCommands
|
|
158
158
|
|
|
159
159
|
# Setup mocks
|
|
160
160
|
mock_env = MagicMock()
|
|
161
161
|
mock_env.name = "test-env"
|
|
162
|
-
mock_env.
|
|
163
|
-
mock_env.
|
|
162
|
+
mock_env.cec_path = MagicMock()
|
|
163
|
+
mock_env.cec_path.__truediv__ = MagicMock(return_value=MagicMock(exists=MagicMock(return_value=True)))
|
|
164
|
+
mock_env.pytorch_manager.has_backend.return_value = True
|
|
165
|
+
mock_env.pytorch_manager.ensure_backend.return_value = "cu128"
|
|
164
166
|
mock_env.sync.return_value = MagicMock(success=True, packages_synced=0, dependency_groups_installed=[], errors=[])
|
|
165
167
|
|
|
166
168
|
mock_workspace = MagicMock()
|
|
@@ -175,17 +177,14 @@ class TestSyncBehavior:
|
|
|
175
177
|
|
|
176
178
|
args = argparse.Namespace(
|
|
177
179
|
target_env=None,
|
|
178
|
-
torch_backend=None, # No override - should
|
|
180
|
+
torch_backend=None, # No override - should use ensure_backend
|
|
179
181
|
verbose=False
|
|
180
182
|
)
|
|
181
183
|
|
|
182
184
|
cmd.sync(args)
|
|
183
185
|
|
|
184
|
-
# Should have
|
|
185
|
-
mock_env.pytorch_manager.
|
|
186
|
-
mock_env.pytorch_manager.detect_backend.assert_not_called()
|
|
187
|
-
# Should NOT have called set_backend (no file write)
|
|
188
|
-
mock_env.pytorch_manager.set_backend.assert_not_called()
|
|
186
|
+
# Should have called ensure_backend which reads from file or probes
|
|
187
|
+
mock_env.pytorch_manager.ensure_backend.assert_called()
|
|
189
188
|
|
|
190
189
|
@patch('comfygit_cli.env_commands.get_workspace_or_exit')
|
|
191
190
|
def test_sync_warns_when_no_backend_file(self, mock_get_workspace, capsys, tmp_path):
|
|
@@ -257,15 +256,17 @@ class TestRunBehavior:
|
|
|
257
256
|
"""Test that run reads from file like sync does."""
|
|
258
257
|
|
|
259
258
|
@patch('comfygit_cli.env_commands.get_workspace_or_exit')
|
|
260
|
-
def
|
|
261
|
-
"""Run should use
|
|
259
|
+
def test_run_uses_ensure_backend(self, mock_get_workspace):
|
|
260
|
+
"""Run should use ensure_backend() which handles both existing and missing backends."""
|
|
262
261
|
from comfygit_cli.env_commands import EnvironmentCommands
|
|
263
262
|
|
|
264
263
|
mock_env = MagicMock()
|
|
265
264
|
mock_env.name = "test-env"
|
|
266
265
|
mock_env.get_current_branch.return_value = "main"
|
|
267
|
-
mock_env.
|
|
268
|
-
mock_env.
|
|
266
|
+
mock_env.cec_path = MagicMock()
|
|
267
|
+
mock_env.cec_path.__truediv__ = MagicMock(return_value=MagicMock(exists=MagicMock(return_value=True)))
|
|
268
|
+
mock_env.pytorch_manager.has_backend.return_value = True
|
|
269
|
+
mock_env.pytorch_manager.ensure_backend.return_value = "cu128"
|
|
269
270
|
mock_env.sync.return_value = MagicMock(success=True)
|
|
270
271
|
mock_env.run.return_value = MagicMock(returncode=0)
|
|
271
272
|
|
|
@@ -279,7 +280,7 @@ class TestRunBehavior:
|
|
|
279
280
|
|
|
280
281
|
args = argparse.Namespace(
|
|
281
282
|
target_env=None,
|
|
282
|
-
torch_backend=None, # Should
|
|
283
|
+
torch_backend=None, # Should use ensure_backend
|
|
283
284
|
no_sync=False,
|
|
284
285
|
args=[]
|
|
285
286
|
)
|
|
@@ -287,6 +288,5 @@ class TestRunBehavior:
|
|
|
287
288
|
with pytest.raises(SystemExit) as exc_info:
|
|
288
289
|
cmd.run(args)
|
|
289
290
|
|
|
290
|
-
# Should have
|
|
291
|
-
mock_env.pytorch_manager.
|
|
292
|
-
mock_env.pytorch_manager.detect_backend.assert_not_called()
|
|
291
|
+
# Should have called ensure_backend which reads from file or probes
|
|
292
|
+
mock_env.pytorch_manager.ensure_backend.assert_called()
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
"""Tests for system node installation during workspace init."""
|
|
2
|
-
import argparse
|
|
3
|
-
from unittest.mock import MagicMock, patch
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestInitSystemNodes:
|
|
9
|
-
"""Tests for system node installation in init command."""
|
|
10
|
-
|
|
11
|
-
def test_default_system_nodes_constant_exists(self):
|
|
12
|
-
"""DEFAULT_SYSTEM_NODES constant should exist and include comfygit-manager."""
|
|
13
|
-
from comfygit_cli.global_commands import DEFAULT_SYSTEM_NODES
|
|
14
|
-
|
|
15
|
-
assert DEFAULT_SYSTEM_NODES is not None
|
|
16
|
-
assert "comfygit-manager" in DEFAULT_SYSTEM_NODES
|
|
17
|
-
assert "url" in DEFAULT_SYSTEM_NODES["comfygit-manager"]
|
|
18
|
-
|
|
19
|
-
def test_install_system_nodes_clones_comfygit_manager(self, tmp_path):
|
|
20
|
-
"""_install_system_nodes should clone comfygit-manager to system_nodes."""
|
|
21
|
-
from comfygit_cli.global_commands import GlobalCommands
|
|
22
|
-
|
|
23
|
-
# Create mock workspace
|
|
24
|
-
mock_workspace = MagicMock()
|
|
25
|
-
mock_workspace.paths.system_nodes = tmp_path / "system_nodes"
|
|
26
|
-
mock_workspace.paths.system_nodes.mkdir(parents=True)
|
|
27
|
-
|
|
28
|
-
global_cmds = GlobalCommands()
|
|
29
|
-
|
|
30
|
-
# Patch at the source module where git_clone is defined
|
|
31
|
-
with patch("comfygit_core.utils.git.git_clone") as mock_clone:
|
|
32
|
-
global_cmds._install_system_nodes(mock_workspace)
|
|
33
|
-
|
|
34
|
-
# Verify git_clone was called
|
|
35
|
-
mock_clone.assert_called_once()
|
|
36
|
-
call_args = mock_clone.call_args
|
|
37
|
-
|
|
38
|
-
# Check URL
|
|
39
|
-
assert "comfygit-manager" in call_args.kwargs["url"]
|
|
40
|
-
# Check target path
|
|
41
|
-
assert str(call_args.kwargs["target_path"]).endswith("comfygit-manager")
|
|
42
|
-
# Check shallow clone
|
|
43
|
-
assert call_args.kwargs["depth"] == 1
|
|
44
|
-
|
|
45
|
-
def test_install_system_nodes_skips_existing(self, tmp_path):
|
|
46
|
-
"""_install_system_nodes should skip if node already exists."""
|
|
47
|
-
from comfygit_cli.global_commands import GlobalCommands
|
|
48
|
-
|
|
49
|
-
# Create mock workspace with existing comfygit-manager
|
|
50
|
-
mock_workspace = MagicMock()
|
|
51
|
-
mock_workspace.paths.system_nodes = tmp_path / "system_nodes"
|
|
52
|
-
mock_workspace.paths.system_nodes.mkdir(parents=True)
|
|
53
|
-
(mock_workspace.paths.system_nodes / "comfygit-manager").mkdir()
|
|
54
|
-
|
|
55
|
-
global_cmds = GlobalCommands()
|
|
56
|
-
|
|
57
|
-
# Patch at the source module where git_clone is defined
|
|
58
|
-
with patch("comfygit_core.utils.git.git_clone") as mock_clone:
|
|
59
|
-
global_cmds._install_system_nodes(mock_workspace)
|
|
60
|
-
|
|
61
|
-
# git_clone should NOT be called since directory exists
|
|
62
|
-
mock_clone.assert_not_called()
|
|
63
|
-
|
|
64
|
-
def test_bare_flag_skips_system_nodes(self, tmp_path, monkeypatch):
|
|
65
|
-
"""init with --bare flag should skip system node installation."""
|
|
66
|
-
from comfygit_cli.global_commands import GlobalCommands
|
|
67
|
-
|
|
68
|
-
global_cmds = GlobalCommands()
|
|
69
|
-
|
|
70
|
-
# Mock workspace factory and creation
|
|
71
|
-
mock_workspace = MagicMock()
|
|
72
|
-
mock_workspace.paths.root = tmp_path
|
|
73
|
-
mock_workspace.paths.system_nodes = tmp_path / "system_nodes"
|
|
74
|
-
mock_workspace.path = tmp_path
|
|
75
|
-
mock_workspace.update_registry_data.return_value = True
|
|
76
|
-
mock_workspace.get_models_directory.return_value = tmp_path / "models"
|
|
77
|
-
|
|
78
|
-
with patch("comfygit_cli.global_commands.WorkspaceFactory") as mock_factory:
|
|
79
|
-
mock_factory.get_paths.return_value = mock_workspace.paths
|
|
80
|
-
mock_factory.create.return_value = mock_workspace
|
|
81
|
-
|
|
82
|
-
with patch.object(global_cmds, "_install_system_nodes") as mock_install:
|
|
83
|
-
with patch.object(global_cmds, "_setup_models_directory"):
|
|
84
|
-
args = argparse.Namespace(
|
|
85
|
-
path=None,
|
|
86
|
-
models_dir=None,
|
|
87
|
-
yes=True,
|
|
88
|
-
bare=True
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
global_cmds.init(args)
|
|
92
|
-
|
|
93
|
-
# _install_system_nodes should NOT be called
|
|
94
|
-
mock_install.assert_not_called()
|
|
95
|
-
|
|
96
|
-
def test_init_calls_install_system_nodes_by_default(self, tmp_path, monkeypatch):
|
|
97
|
-
"""init without --bare should call _install_system_nodes."""
|
|
98
|
-
from comfygit_cli.global_commands import GlobalCommands
|
|
99
|
-
|
|
100
|
-
global_cmds = GlobalCommands()
|
|
101
|
-
|
|
102
|
-
# Mock workspace factory and creation
|
|
103
|
-
mock_workspace = MagicMock()
|
|
104
|
-
mock_workspace.paths.root = tmp_path
|
|
105
|
-
mock_workspace.paths.system_nodes = tmp_path / "system_nodes"
|
|
106
|
-
mock_workspace.path = tmp_path
|
|
107
|
-
mock_workspace.update_registry_data.return_value = True
|
|
108
|
-
mock_workspace.get_models_directory.return_value = tmp_path / "models"
|
|
109
|
-
|
|
110
|
-
with patch("comfygit_cli.global_commands.WorkspaceFactory") as mock_factory:
|
|
111
|
-
mock_factory.get_paths.return_value = mock_workspace.paths
|
|
112
|
-
mock_factory.create.return_value = mock_workspace
|
|
113
|
-
|
|
114
|
-
with patch.object(global_cmds, "_install_system_nodes") as mock_install:
|
|
115
|
-
with patch.object(global_cmds, "_setup_models_directory"):
|
|
116
|
-
args = argparse.Namespace(
|
|
117
|
-
path=None,
|
|
118
|
-
models_dir=None,
|
|
119
|
-
yes=True,
|
|
120
|
-
bare=False
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
global_cmds.init(args)
|
|
124
|
-
|
|
125
|
-
# _install_system_nodes SHOULD be called
|
|
126
|
-
mock_install.assert_called_once_with(mock_workspace)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|