Skip to content

Updater

updater

ComfyUI Update System.

Migrated to uv — no raw pip. Handles: - ComfyUI core update (git pull) - Custom nodes update via manifest - Python dependencies update - Triton / SageAttention re-install

update_comfyui_core(comfy_path, log)

Update ComfyUI core via git pull.

Source code in src/installer/updater.py
def update_comfyui_core(comfy_path: Path, log: InstallerLogger) -> None:
    """Update ComfyUI core via git pull."""
    log.step("Updating ComfyUI Core")

    if not comfy_path.exists():
        log.error("ComfyUI directory not found!")
        return

    log.item("Pulling latest changes...")
    try:
        run_and_log("git", ["-C", str(comfy_path), "pull", "--ff-only"])
        log.sub("ComfyUI updated.", style="success")
    except CommandError:
        log.warning("Git pull failed. You may have local changes.", level=2)
        log.info("Try: git -C ComfyUI stash && git -C ComfyUI pull")

update_custom_nodes(python_exe, comfy_path, install_path, log)

Update bundled custom nodes. User-installed nodes are NEVER touched.

Merges new nodes from the source manifest (additive-only) so that user customizations are preserved while newly added nodes are picked up on every update.

Source code in src/installer/updater.py
def update_custom_nodes(
    python_exe: Path,
    comfy_path: Path,
    install_path: Path,
    log: InstallerLogger,
) -> None:
    """Update bundled custom nodes. User-installed nodes are NEVER touched.

    Merges new nodes from the source manifest (additive-only) so that
    user customizations are preserved while newly added nodes are
    picked up on every update.
    """

    from src.installer.environment import find_source_scripts
    from src.installer.nodes import load_manifest, update_all_nodes

    scripts_dir = install_path / "scripts"
    manifest_path = scripts_dir / "custom_nodes.json"

    # Merge new nodes from source (additive-only — never overwrites user changes)
    source_dir = find_source_scripts()
    if source_dir:
        source_manifest = source_dir / "custom_nodes.json"
        if source_manifest.exists():
            scripts_dir.mkdir(parents=True, exist_ok=True)
            added = _merge_node_manifests(source_manifest, manifest_path, log)
            if added > 0:
                log.item(f"custom_nodes.json: {added} new node(s) added from source.", style="cyan")
            else:
                log.sub("custom_nodes.json is up to date.", style="success")

    if not manifest_path.exists():
        log.skip_step("Custom Nodes — manifest not found")
        return

    custom_nodes_dir = comfy_path / "custom_nodes"
    manifest = load_manifest(manifest_path)
    update_all_nodes(manifest, custom_nodes_dir, python_exe, log)

update_dependencies(python_exe, comfy_path, install_path, log)

Update Python dependencies via uv.

Source code in src/installer/updater.py
def update_dependencies(
    python_exe: Path,
    comfy_path: Path,
    install_path: Path,
    log: InstallerLogger,
) -> None:
    """Update Python dependencies via uv."""
    log.step("Updating Python Dependencies")

    deps_file = install_path / "scripts" / "dependencies.json"
    if not deps_file.exists():
        log.warning("dependencies.json not found. Skipping.", level=1)
        return

    deps = load_dependencies(deps_file)

    # Update ComfyUI requirements
    req_file = comfy_path / deps.pip_packages.comfyui_requirements
    if req_file.exists():
        log.item("Updating ComfyUI requirements...")
        uv_install(python_exe, requirements=req_file, upgrade=True)

    # Update torch
    if confirm("Update PyTorch? (Only if there's a new CUDA version)"):
        # Detect CUDA tag from the currently installed torch build
        from src.installer.optimizations import _get_cuda_version_from_torch

        cuda_ver = _get_cuda_version_from_torch(python_exe)
        cuda_tag: str | None = None
        if cuda_ver:
            try:
                parts = cuda_ver.split(".")
                from src.utils.gpu import cuda_tag_from_version
                cuda_tag = cuda_tag_from_version((int(parts[0]), int(parts[1])))
            except (ValueError, IndexError):
                pass

        # Fallback: use first supported tag from config
        if cuda_tag is None:
            supported = deps.pip_packages.supported_cuda_tags
            cuda_tag = supported[0] if supported else "cu130"
            log.sub(f"Could not detect CUDA from torch. Using {cuda_tag}.", style="yellow")

        torch_cfg = deps.pip_packages.get_torch(cuda_tag)
        if torch_cfg:
            torch_pkgs = torch_cfg.packages.split()
            log.item(f"Updating PyTorch [{cuda_tag}]...")
            uv_install(
                python_exe,
                torch_pkgs,
                index_url=torch_cfg.index_url,
                upgrade=True,
            )
        else:
            log.warning(f"No PyTorch config for '{cuda_tag}'. Skipping torch update.", level=1)

run_update(install_path, *, verbose=False)

Run the full update process.

Parameters:

Name Type Description Default
install_path Path

Root installation directory.

required
verbose bool

Show detailed subprocess output.

False
Source code in src/installer/updater.py
def run_update(install_path: Path, *, verbose: bool = False) -> None:
    """
    Run the full update process.

    Args:
        install_path: Root installation directory.
        verbose: Show detailed subprocess output.
    """
    log = setup_logger(
        log_file=install_path / "logs" / "update_log.txt",
        total_steps=5,
        verbose=verbose,
    )
    log.banner("UmeAiRT", "ComfyUI — Updater", __version__)

    comfy_path = install_path / "ComfyUI"
    scripts_dir = install_path / "scripts"

    # Detect python executable
    python_exe = _detect_python(scripts_dir, log)

    # Run update steps
    update_comfyui_core(comfy_path, log)
    update_custom_nodes(python_exe, comfy_path, install_path, log)
    update_dependencies(python_exe, comfy_path, install_path, log)

    # Model security scan (non-blocking)
    _scan_models_warning(install_path, log)

    log.step("Update Complete!")
    log.success("All components have been updated.", level=1)