ComfyUI-Manager/glob/manager_migration.py
2025-12-05 07:01:01 +09:00

357 lines
12 KiB
Python

"""
ComfyUI-Manager migration module.
Handles migration from legacy paths to new __manager path structure.
"""
import os
import sys
import subprocess
import configparser
# Startup notices for notice board
startup_notices = [] # List of (message, level) tuples
def add_startup_notice(message, level='warning'):
"""Add a notice to be displayed on Manager notice board.
Args:
message: HTML-formatted message string
level: 'warning', 'error', 'info'
"""
global startup_notices
startup_notices.append((message, level))
# Cache for API check (computed once per session)
_cached_has_system_user_api = None
def has_system_user_api():
"""Check if ComfyUI has the System User Protection API (PR #10966).
Result is cached for performance.
"""
global _cached_has_system_user_api
if _cached_has_system_user_api is None:
try:
import folder_paths
_cached_has_system_user_api = hasattr(folder_paths, 'get_system_user_directory')
except Exception:
_cached_has_system_user_api = False
return _cached_has_system_user_api
def get_manager_path(user_dir):
"""Get the appropriate manager files path based on ComfyUI version.
Returns:
str: manager_files_path
"""
if has_system_user_api():
return os.path.abspath(os.path.join(user_dir, '__manager'))
else:
return os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager'))
def run_migration_checks(user_dir, manager_files_path):
"""Run all migration and security checks.
Call this after get_manager_path() to handle:
- Legacy config migration (new ComfyUI)
- Legacy backup notification (every startup)
- Suspicious directory detection (old ComfyUI)
- Outdated ComfyUI warning (old ComfyUI)
"""
if has_system_user_api():
migrated = migrate_legacy_config(user_dir, manager_files_path)
# Only check for legacy backup if migration didn't just happen
# (migration already shows backup location in its message)
if not migrated:
check_legacy_backup(manager_files_path)
else:
check_suspicious_manager(user_dir)
warn_outdated_comfyui()
def check_legacy_backup(manager_files_path):
"""Check for legacy backup and notify user to verify and remove it.
This runs on every startup to remind users about pending legacy backup.
"""
backup_dir = os.path.join(manager_files_path, '.legacy-manager-backup')
if not os.path.exists(backup_dir):
return
# Terminal output
print("\n" + "-"*70)
print("[ComfyUI-Manager] NOTICE: Legacy backup exists")
print(" - Your old Manager data was backed up to:")
print(f" {backup_dir}")
print(" - Please verify and remove it when no longer needed.")
print("-"*70 + "\n")
# Notice board output
add_startup_notice(
"Legacy ComfyUI-Manager data backup exists. Please verify and remove when no longer needed. See terminal for details.",
level='info'
)
def check_suspicious_manager(user_dir):
"""Check for suspicious __manager directory on old ComfyUI.
On old ComfyUI without System User API, if __manager exists with low security,
warn the user to verify manually.
Returns:
bool: True if suspicious setup detected
"""
if has_system_user_api():
return False # Not suspicious on new ComfyUI
suspicious_path = os.path.abspath(os.path.join(user_dir, '__manager'))
if not os.path.exists(suspicious_path):
return False
config_path = os.path.join(suspicious_path, 'config.ini')
if not os.path.exists(config_path):
return False
config = configparser.ConfigParser()
config.read(config_path)
sec_level = config.get('default', 'security_level', fallback='normal').lower()
if sec_level in ['weak', 'normal-']:
# Terminal output
print("\n" + "!"*70)
print("[ComfyUI-Manager] ERROR: Suspicious path detected!")
print(f" - '__manager' exists with low security level: '{sec_level}'")
print(" - Please verify manually:")
print(f" {config_path}")
print("!"*70 + "\n")
# Notice board output
add_startup_notice(
"[Security Alert] Suspicious path detected. See terminal log for details.",
level='error'
)
return True
return False
def warn_outdated_comfyui():
"""Warn user about outdated ComfyUI without System User API."""
if has_system_user_api():
return
# Terminal output
print("\n" + "!"*70)
print("[ComfyUI-Manager] ERROR: ComfyUI version is outdated!")
print(" - Most operations are blocked for security.")
print(" - ComfyUI update is still allowed.")
print(" - Please update ComfyUI to use Manager normally.")
print("!"*70 + "\n")
# Notice board output
add_startup_notice(
"[Security Alert] ComfyUI outdated. Installations blocked (update allowed).<BR>"
"Update ComfyUI for normal operation.",
level='error'
)
def migrate_legacy_config(user_dir, manager_files_path):
"""Migrate ONLY config.ini to new __manager path if needed.
IMPORTANT: Only config.ini is migrated. Other files (snapshots, cache, etc.)
are NOT migrated - users must recreate them.
Scenarios:
1. Legacy exists, New doesn't exist → Migrate config.ini
2. Legacy exists, New exists → First update after upgrade
- Run ComfyUI dependency installation
- Rename legacy to .backup
3. Legacy doesn't exist → No migration needed
Returns:
bool: True if migration was performed
"""
if not has_system_user_api():
return False
legacy_dir = os.path.join(user_dir, 'default', 'ComfyUI-Manager')
legacy_config = os.path.join(legacy_dir, 'config.ini')
new_config = os.path.join(manager_files_path, 'config.ini')
if not os.path.exists(legacy_dir):
return False # No legacy directory, nothing to migrate
# IMPORTANT: Check for config.ini existence, not just directory
# (because makedirs() creates __manager before this function is called)
# Case: Both configs exist (first update after ComfyUI upgrade)
# This means user ran new ComfyUI at least once, creating __manager/config.ini
if os.path.exists(legacy_config) and os.path.exists(new_config):
_handle_first_update_migration(user_dir, legacy_dir, manager_files_path)
return True
# Case: Legacy config exists but new config doesn't (normal migration)
# This is the first run after ComfyUI upgrade
if os.path.exists(legacy_config) and not os.path.exists(new_config):
pass # Continue with normal migration below
else:
return False
# Terminal output
print("\n" + "-"*70)
print("[ComfyUI-Manager] NOTICE: Legacy config.ini detected")
print(f" - Old: {legacy_config}")
print(f" - New: {new_config}")
print(" - Migrating config.ini only (other files are NOT migrated).")
print(" - Security level below 'normal' will be raised.")
print("-"*70 + "\n")
_migrate_config_with_security_check(legacy_config, new_config)
# Move legacy directory to backup
_move_legacy_to_backup(legacy_dir, manager_files_path)
return True
def _handle_first_update_migration(user_dir, legacy_dir, manager_files_path):
"""Handle first ComfyUI update when both legacy and new directories exist.
This scenario happens when:
- User was on old ComfyUI (using default/ComfyUI-Manager)
- ComfyUI was updated (now has System User API)
- Manager already created __manager on first new run
- But legacy directory still exists
Actions:
1. Run ComfyUI dependency installation
2. Move legacy to __manager/.legacy-manager-backup
"""
# Terminal output
print("\n" + "-"*70)
print("[ComfyUI-Manager] NOTICE: First update after ComfyUI upgrade detected")
print(" - Both legacy and new directories exist.")
print(" - Running ComfyUI dependency installation...")
print("-"*70 + "\n")
# Run ComfyUI dependency installation
# Path: glob/manager_migration.py → glob → comfyui-manager → custom_nodes → ComfyUI
try:
comfyui_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
requirements_path = os.path.join(comfyui_path, 'requirements.txt')
if os.path.exists(requirements_path):
subprocess.run([sys.executable, '-m', 'pip', 'install', '-r', requirements_path],
capture_output=True, check=False)
print("[ComfyUI-Manager] ComfyUI dependencies installation completed.")
except Exception as e:
print(f"[ComfyUI-Manager] WARNING: Failed to install ComfyUI dependencies: {e}")
# Move legacy to backup inside __manager
_move_legacy_to_backup(legacy_dir, manager_files_path)
def _move_legacy_to_backup(legacy_dir, manager_files_path):
"""Move legacy directory to backup inside __manager.
Returns:
str: Path to backup directory if successful, None if failed
"""
import shutil
backup_dir = os.path.join(manager_files_path, '.legacy-manager-backup')
try:
if os.path.exists(backup_dir):
shutil.rmtree(backup_dir) # Remove old backup if exists
shutil.move(legacy_dir, backup_dir)
# Terminal output (full paths shown here only)
print("\n" + "-"*70)
print("[ComfyUI-Manager] NOTICE: Legacy settings migrated")
print(f" - Old location: {legacy_dir}")
print(f" - Backed up to: {backup_dir}")
print(" - Please verify and remove the backup when no longer needed.")
print("-"*70 + "\n")
# Notice board output (no full paths for security)
add_startup_notice(
"Legacy ComfyUI-Manager data migrated. See terminal for details.",
level='info'
)
return backup_dir
except Exception as e:
print(f"[ComfyUI-Manager] WARNING: Failed to backup legacy directory: {e}")
add_startup_notice(
f"[MIGRATION] Failed to backup legacy directory: {e}",
level='warning'
)
return None
def _migrate_config_with_security_check(legacy_path, new_path):
"""Migrate legacy config, raising security level only if below default."""
config = configparser.ConfigParser()
try:
config.read(legacy_path)
except Exception as e:
print(f"[ComfyUI-Manager] WARNING: Failed to parse config.ini: {e}")
print(" - Creating fresh config with default settings.")
add_startup_notice(
"[MIGRATION] Failed to parse legacy config. Using defaults.",
level='warning'
)
return # Skip migration, let Manager create fresh config
# Security level hierarchy: strong > normal > normal- > weak
# Default is 'normal', only raise if below default
if 'default' in config:
current_level = config['default'].get('security_level', 'normal').lower()
below_default_levels = ['weak', 'normal-']
if current_level in below_default_levels:
config['default']['security_level'] = 'normal'
# Terminal output
print("\n" + "="*70)
print("[ComfyUI-Manager] WARNING: Security level adjusted")
print(f" - Previous: '{current_level}' → New: 'normal'")
print(" - Raised to prevent unauthorized remote access.")
print("="*70 + "\n")
# Notice board output
add_startup_notice(
f"[MIGRATION] Security level raised: '{current_level}''normal'.<BR>"
"To prevent unauthorized remote access.",
level='warning'
)
else:
print(f" - Security level: '{current_level}' (no change needed)")
# Ensure directory exists
os.makedirs(os.path.dirname(new_path), exist_ok=True)
with open(new_path, 'w') as f:
config.write(f)
def force_security_level_if_needed(config_dict):
"""Force security level to 'strong' if on old ComfyUI.
Args:
config_dict: Configuration dictionary to modify in-place
Returns:
bool: True if security level was forced
"""
if not has_system_user_api():
config_dict['security_level'] = 'strong'
return True
return False