Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/install snapshot #15

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ will simply update the comfy.yaml file to reflect the local setup
- `comfy install --skip-manager`: Install ComfyUI without ComfyUI-Manager.
- `comfy --workspace=<path> install`: Install ComfyUI into `<path>/ComfyUI`.
- For `comfy install`, if no path specification like `--workspace, --recent, or --here` is provided, it will be implicitly installed in `<HOME>/comfy`.
- **(WIP)** `comfy install --snapshot=<comfy-lock.yaml path>`: Install ComfyUI and whole environments from snapshot.
- **(WIP)** Currently, only the installation of ComfyUI and custom nodes is being applied.


### Snapshot [WIP]

To save ComfyUI Environment:

`comfy snapshot save --output=<.yaml path>`

To retore ComfyUI Environment:

`comfy snapshot restore --input=<.yaml path>`

This command is used to perform a full backup/restore of the currently installed ComfyUI Environment.
**(WIP)** Currently, only the ComfyUI and Custom node information are backed up.


### Specifying execution path

Expand Down
9 changes: 9 additions & 0 deletions comfy_cli/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from comfy_cli.command import run as run_inner
from comfy_cli.command.launch import launch as launch_command
from comfy_cli.command.models import models as models_command
from comfy_cli.command.snapshot import command as snapshot_command
from comfy_cli.config_manager import ConfigManager
from comfy_cli.constants import GPU_OPTION, CUDAVersion
from comfy_cli.env_checker import EnvChecker
Expand Down Expand Up @@ -221,10 +222,14 @@ def install(
help="Use new fast dependency installer",
),
] = False,
snapshot: Annotated[str, typer.Option(help="Specify path to comfy-lock.yaml")] = None,
):
check_for_updates()
checker = EnvChecker()

if snapshot is not None:
snapshot = os.path.abspath(snapshot)

comfy_path, _ = workspace_manager.get_workspace_path()

is_comfy_installed_at_path, repo_dir = check_comfy_repo(comfy_path)
Expand Down Expand Up @@ -325,6 +330,9 @@ def install(
fast_deps=fast_deps,
)

if snapshot is not None:
snapshot_command.apply_snapshot(snapshot)

print(f"ComfyUI is installed at: {comfy_path}")


Expand Down Expand Up @@ -654,3 +662,4 @@ def standalone(
app.add_typer(custom_nodes.app, name="node", help="Manage custom nodes.")
app.add_typer(custom_nodes.manager_app, name="manager", help="Manage ComfyUI-Manager.")
app.add_typer(tracking.app, name="tracking", help="Manage analytics tracking settings.")
app.add_typer(snapshot_command.app, name="snapshot", help="Manage custom nodes.")
5 changes: 3 additions & 2 deletions comfy_cli/command/custom_nodes/cm_cli_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
}


def execute_cm_cli(args, channel=None, fast_deps=False, mode=None) -> str | None:
def execute_cm_cli(args, channel=None, fast_deps=False, mode=None, silent=False) -> str | None:
_config_manager = ConfigManager()

workspace_path = workspace_manager.workspace_path
Expand Down Expand Up @@ -58,7 +58,8 @@ def execute_cm_cli(args, channel=None, fast_deps=False, mode=None) -> str | None

try:
result = subprocess.run(cmd, env=new_env, check=True, capture_output=True, text=True)
print(result.stdout)
if not silent:
print(result.stdout)

if fast_deps and args[0] in _dependency_cmds:
# we're using the fast_deps behavior and just ran a command that invalidated the dependencies
Expand Down
3 changes: 0 additions & 3 deletions comfy_cli/command/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ def request_civitai_model_api(model_id: int, version_id: int = None, headers: Op
@app.command(help="Download model file from url")
@tracking.track_command("model")
def download(
_ctx: typer.Context,
url: Annotated[
str,
typer.Option(help="The URL from which to download the model", show_default=False),
Expand Down Expand Up @@ -238,7 +237,6 @@ def download(
@app.command()
@tracking.track_command("model")
def remove(
ctx: typer.Context,
relative_path: str = typer.Option(
DEFAULT_COMFY_MODEL_PATH,
help="The relative path from the current workspace where the models are stored.",
Expand Down Expand Up @@ -302,7 +300,6 @@ def remove(
@app.command()
@tracking.track_command("model")
def list(
ctx: typer.Context,
relative_path: str = typer.Option(
DEFAULT_COMFY_MODEL_PATH,
help="The relative path from the current workspace where the models are stored.",
Expand Down
83 changes: 83 additions & 0 deletions comfy_cli/command/snapshot/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import os
import typer
from typing_extensions import Annotated
from comfy_cli import tracking, ui
from comfy_cli.workspace_manager import WorkspaceManager
from comfy_cli.command import custom_nodes
from rich import print
import uuid
import yaml

workspace_manager = WorkspaceManager()

app = typer.Typer()


@app.command(help="Save current snapshot to .yaml file")
@tracking.track_command()
def save(
output: Annotated[
str,
"--output",
typer.Option(show_default=False, help="Specify the output file path. (.yaml)"),
],
):

if not output.endswith(".yaml"):
print("[bold red]The output path must end with '.yaml'.[/bold red]")
raise typer.Exit(code=1)

output_path = os.path.abspath(output)

config_manager = workspace_manager.config_manager
tmp_path = (
os.path.join(config_manager.get_config_path(), "tmp", str(uuid.uuid4()))
+ ".yaml"
)
tmp_path = os.path.abspath(tmp_path)
custom_nodes.command.execute_cm_cli(
["save-snapshot", "--output", tmp_path], silent=True
)

with open(tmp_path, "r", encoding="UTF-8") as yaml_file:
info = yaml.load(yaml_file, Loader=yaml.SafeLoader)
os.remove(tmp_path)

info["basic"] = "N/A" # TODO:
info["models"] = [] # TODO:

with open(output_path, "w", encoding="UTF-8") as yaml_file:
yaml.dump(info, yaml_file, allow_unicode=True)

print(f"Snapshot file is saved as `{output_path}`")


@app.command(help="Restore from snapshot file")
@tracking.track_command()
def restore(
input: Annotated[
str,
"--input",
typer.Option(show_default=False, help="Specify the input file path. (.yaml)"),
],
):
input_path = os.path.abspath(input)
apply_snapshot(input_path)

# TODO: restore other properties


def apply_snapshot(filepath):
if not os.path.exists(filepath):
print(f"[bold red]File not found: {filepath}[/bold red]")
raise typer.Exit(code=1)

if workspace_manager.get_comfyui_manager_path() is None or not os.path.exists(
workspace_manager.get_comfyui_manager_path()
):
print(
"[bold red]If ComfyUI-Manager is not installed, the snapshot feature cannot be used.[/bold red]"
)
raise typer.Exit(code=1)

custom_nodes.command.restore_snapshot(filepath)
Loading