diff --git a/py/config.py b/py/config.py index 68354937..b929049f 100644 --- a/py/config.py +++ b/py/config.py @@ -359,6 +359,47 @@ class Config: "Failed to rename legacy 'default' library: %s", rename_error ) + # Clean up a stale "default" library entry that has no meaningful + # paths configured (e.g. leftover bootstrap artifact). This only + # fires when "comfyui" already exists so we never delete the last + # remaining library. + if ( + "default" in libraries + and "comfyui" in libraries + and isinstance(default_library, Mapping) + ): + default_folder_paths = _normalize_library_folder_paths( + default_library + ) + default_extra_paths = default_library.get("extra_folder_paths", {}) + has_meaningful_paths = bool(default_folder_paths) or bool( + default_extra_paths + ) or any( + default_library.get(key) + for key in ( + "default_lora_root", + "default_checkpoint_root", + "default_unet_root", + "default_embedding_root", + "recipes_path", + ) + ) + if not has_meaningful_paths: + try: + settings_service.delete_library("default") + libraries_changed = True + logger.info( + "Removed stale 'default' library entry " + "with no meaningful paths configured" + ) + libraries = settings_service.get_libraries() + comfy_library = libraries.get("comfyui", {}) + except Exception as delete_error: + logger.debug( + "Failed to remove stale 'default' library: %s", + delete_error, + ) + default_lora_root = _resolve_valid_default_root( comfy_library.get("default_lora_root", ""), list(self.loras_roots or []), diff --git a/py/utils/example_images_paths.py b/py/utils/example_images_paths.py index 1c559ba2..44a59f6a 100644 --- a/py/utils/example_images_paths.py +++ b/py/utils/example_images_paths.py @@ -113,6 +113,35 @@ def get_model_folder(model_hash: str, library_name: Optional[str] = None) -> str exc, ) return legacy_folder + elif not os.path.exists(resolved_folder): + # Reverse migration: when consolidating from multi-library to + # single-library mode (e.g. after "default" was cleaned up), look + # for existing example images inside library-named subdirectories + # and bring them back to the root level. + root = get_example_images_root() + if root: + try: + for entry in os.listdir(root): + entry_path = os.path.join(root, entry) + if not os.path.isdir(entry_path): + continue + if is_hash_folder(entry) or entry == "_deleted": + continue + if not _library_folder_has_only_hash_dirs(entry_path): + continue + legacy = os.path.join(entry_path, normalized_hash) + if os.path.exists(legacy): + shutil.move(legacy, resolved_folder) + logger.info( + "Consolidated example images from '%s' to '%s'", + legacy, resolved_folder, + ) + break + except OSError as exc: + logger.error( + "Failed to consolidate example images during " + "library merge: %s", exc, + ) return resolved_folder diff --git a/tests/config/test_config_save_paths.py b/tests/config/test_config_save_paths.py index a7721cdb..59ffe481 100644 --- a/tests/config/test_config_save_paths.py +++ b/tests/config/test_config_save_paths.py @@ -823,3 +823,73 @@ def test_apply_library_settings_ignores_extra_lora_path_overlapping_primary_root "same lora folder" in record.message.lower() for record in caplog.records ) + + +def test_save_paths_removes_stale_empty_default_when_comfyui_exists( + monkeypatch: pytest.MonkeyPatch, tmp_path, +): + """When an empty-shell 'default' library coexists with 'comfyui', the + stale 'default' entry should be removed and 'comfyui' activated.""" + folder_paths = _setup_config_environment(monkeypatch, tmp_path) + + class FakeSettingsService: + def __init__(self): + # Replicate the user's settings.json: empty default + populated comfyui + self.libraries = { + "default": { + "folder_paths": {}, + "extra_folder_paths": {}, + "default_lora_root": "", + "default_checkpoint_root": "", + "default_unet_root": "", + "default_embedding_root": "", + "recipes_path": "", + }, + "comfyui": { + "folder_paths": { + key: list(value) for key, value in folder_paths.items() + }, + "default_lora_root": folder_paths["loras"][0], + "default_checkpoint_root": folder_paths["checkpoints"][0], + "default_embedding_root": folder_paths["embeddings"][0], + }, + } + # No active_library key — get_active_library_name() falls back to + # dict order, returning "default". + self.active_library = "default" + self.delete_calls: list[str] = [] + self.upsert_calls: list[tuple[str, dict]] = [] + + def get_libraries(self): + return dict(self.libraries) + + def delete_library(self, name: str): + self.delete_calls.append(name) + self.libraries.pop(name, None) + + def rename_library(self, *_): + raise AssertionError("rename_library should not be invoked") + + def get_active_library_name(self): + return self.active_library + + def upsert_library(self, name: str, **payload): + self.upsert_calls.append((name, payload)) + self.libraries[name] = {**payload} + if payload.get("activate"): + self.active_library = name + + fake_settings = FakeSettingsService() + monkeypatch.setattr(settings_manager_module, "settings", fake_settings) + + config_module.Config() + + assert fake_settings.delete_calls == ["default"] + assert "default" not in fake_settings.libraries + assert set(fake_settings.libraries.keys()) == {"comfyui"} + + assert len(fake_settings.upsert_calls) == 1 + name, payload = fake_settings.upsert_calls[0] + assert name == "comfyui" + assert payload["activate"] is True + assert fake_settings.active_library == "comfyui"