Skip to content

Commit a898966

Browse files
authored
Non-interactive mode (#54)
Feat: enable non-interactive mode
1 parent 42f7986 commit a898966

File tree

10 files changed

+141
-59
lines changed

10 files changed

+141
-59
lines changed

.github/workflows/build-and-test.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,5 @@ jobs:
3434
3535
- name: Run Tests
3636
run: |
37-
comfy --yes env
38-
comfy tracking disable
39-
comfy install --cpu
37+
comfy --skip-prompt --no-enable-telemetry env
38+
comfy --skip-prompt install --cpu

.pylintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ disable=
77
W0622, # redefined-builtin
88
W1113, # too-many-arguments
99
W0613, # unused-argument
10+
W0718, # broad-exception-caught
1011

1112
[FORMAT]
1213
max-line-length=120

comfy_cli/cmdline.py

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,33 @@
55
import uuid
66
import webbrowser
77
from typing import Optional
8-
from comfy_cli.constants import GPU_OPTION
98

109
import questionary
1110
import typer
1211
from rich import print
12+
from rich.console import Console
1313
from typing_extensions import Annotated, List
1414

1515
from comfy_cli import constants, env_checker, logging, tracking, ui, utils
1616
from comfy_cli.command import custom_nodes
1717
from comfy_cli.command import install as install_inner
1818
from comfy_cli.command.models import models as models_command
19-
from comfy_cli.update import check_for_updates
2019
from comfy_cli.config_manager import ConfigManager
20+
from comfy_cli.constants import GPU_OPTION
2121
from comfy_cli.env_checker import EnvChecker, check_comfy_server_running
22+
from comfy_cli.update import check_for_updates
2223
from comfy_cli.workspace_manager import (
2324
WorkspaceManager,
24-
check_comfy_repo,
2525
WorkspaceType,
26+
check_comfy_repo,
2627
)
2728

2829
logging.setup_logging()
2930
app = typer.Typer()
3031
workspace_manager = WorkspaceManager()
3132

33+
console = Console()
34+
3235

3336
def main():
3437
app()
@@ -62,33 +65,53 @@ def help(ctx: typer.Context):
6265
@app.callback(invoke_without_command=True)
6366
def entry(
6467
ctx: typer.Context,
65-
workspace: Optional[str] = typer.Option(
66-
default=None,
67-
show_default=False,
68-
help="Path to ComfyUI workspace",
69-
callback=exclusivity_callback,
70-
),
71-
recent: Optional[bool] = typer.Option(
72-
default=None,
73-
show_default=False,
74-
is_flag=True,
75-
help="Execute from recent path",
76-
callback=exclusivity_callback,
77-
),
78-
here: Optional[bool] = typer.Option(
79-
default=None,
80-
show_default=False,
81-
is_flag=True,
82-
help="Execute from current path",
83-
callback=exclusivity_callback,
84-
),
85-
yes: bool = typer.Option(
86-
False, "--yes", "-y", help="Enable without confirmation", is_flag=True
87-
),
68+
workspace: Annotated[
69+
Optional[str],
70+
typer.Option(
71+
show_default=False,
72+
help="Path to ComfyUI workspace",
73+
callback=exclusivity_callback,
74+
),
75+
] = None,
76+
recent: Annotated[
77+
Optional[bool],
78+
typer.Option(
79+
show_default=False,
80+
is_flag=True,
81+
help="Execute from recent path",
82+
callback=exclusivity_callback,
83+
),
84+
] = None,
85+
here: Annotated[
86+
Optional[bool],
87+
typer.Option(
88+
show_default=False,
89+
is_flag=True,
90+
help="Execute from current path",
91+
callback=exclusivity_callback,
92+
),
93+
] = None,
94+
skip_prompt: Annotated[
95+
Optional[bool],
96+
typer.Option(
97+
show_default=False,
98+
is_flag=True,
99+
help="Do not prompt user for input, use default options",
100+
),
101+
] = None,
102+
enable_telemetry: Annotated[
103+
Optional[bool],
104+
typer.Option(
105+
show_default=False,
106+
hidden=True,
107+
is_flag=True,
108+
help="Enable tracking",
109+
),
110+
] = True,
88111
):
89-
workspace_manager.setup_workspace_manager(workspace, here, recent)
112+
workspace_manager.setup_workspace_manager(workspace, here, recent, skip_prompt)
90113

91-
tracking.prompt_tracking_consent(yes)
114+
tracking.prompt_tracking_consent(skip_prompt, default_value=enable_telemetry)
92115

93116
if ctx.invoked_subcommand is None:
94117
print(
@@ -172,6 +195,9 @@ def install(
172195

173196
comfy_path = workspace_manager.get_specified_workspace()
174197

198+
if comfy_path is None:
199+
comfy_path = workspace_manager.workspace_path
200+
175201
if comfy_path is None:
176202
comfy_path = utils.get_not_user_set_default_workspace()
177203

@@ -234,6 +260,12 @@ def install(
234260
"[bold yellow]Feel free to follow this thread to manually install:\nhttps://github.com/comfyanonymous/ComfyUI/discussions/476[/bold yellow]"
235261
)
236262

263+
if gpu is None and not cpu:
264+
print(
265+
"[bold red]No GPU option selected or `--cpu` enabled, use --\[gpu option] flag (e.g. --nvidia) to pick GPU. use `--cpu` to install for CPU. Exiting...[/bold red]"
266+
)
267+
raise typer.Exit(code=1)
268+
237269
install_inner.execute(
238270
url,
239271
manager_url,
@@ -242,7 +274,7 @@ def install(
242274
skip_manager,
243275
commit=commit,
244276
gpu=gpu,
245-
platform=platform,
277+
plat=platform,
246278
skip_torch_or_directml=skip_torch_or_directml,
247279
skip_requirement=skip_requirement,
248280
)
@@ -266,7 +298,6 @@ def update(
266298
)
267299
raise typer.Exit(code=1)
268300

269-
_env_checker = EnvChecker()
270301
comfy_path = workspace_manager.workspace_path
271302

272303
if "all" == target:
@@ -482,7 +513,9 @@ def which():
482513
def env():
483514
check_for_updates()
484515
_env_checker = EnvChecker()
485-
_env_checker.print()
516+
table = _env_checker.fill_print_table()
517+
workspace_manager.fill_print_table(table)
518+
console.print(table)
486519

487520

488521
@app.command(hidden=True)
@@ -510,6 +543,7 @@ def feedback():
510543
general_satisfaction_score = ui.prompt_select(
511544
question="On a scale of 1 to 5, how satisfied are you with the Comfy CLI tool? (1 being very dissatisfied and 5 being very satisfied)",
512545
choices=["1", "2", "3", "4", "5"],
546+
force_prompting=True,
513547
)
514548
tracking.track_event(
515549
"feedback_general_satisfaction", {"score": general_satisfaction_score}
@@ -519,6 +553,7 @@ def feedback():
519553
usability_satisfaction_score = ui.prompt_select(
520554
question="On a scale of 1 to 5, how satisfied are you with the usability and user experience of the Comfy CLI tool? (1 being very dissatisfied and 5 being very satisfied)",
521555
choices=["1", "2", "3", "4", "5"],
556+
force_prompting=True,
522557
)
523558
tracking.track_event(
524559
"feedback_usability_satisfaction", {"score": usability_satisfaction_score}

comfy_cli/command/custom_nodes/command.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ def execute_cm_cli(args, channel=None, mode=None):
6262

6363
print(f"Execute from: {workspace_path}")
6464

65-
subprocess.run(cmd, env=new_env)
65+
res = subprocess.run(cmd, env=new_env, check=True)
66+
if res.returncode != 0:
67+
print(f"Failed to execute: {cmd}", file=sys.stderr)
68+
raise typer.Exit(code=1)
6669
workspace_manager.set_recent_workspace(workspace_path)
6770

6871

@@ -71,7 +74,7 @@ def validate_comfyui_manager(_env_checker):
7174

7275
if manager_path is None:
7376
print(
74-
f"[bold red]If ComfyUI is not installed, this feature cannot be used.[/bold red]"
77+
"[bold red]If ComfyUI is not installed, this feature cannot be used.[/bold red]"
7578
)
7679
raise typer.Exit(code=1)
7780
elif not os.path.exists(manager_path):
@@ -422,7 +425,13 @@ def update_node_id_cache():
422425

423426
new_env = os.environ.copy()
424427
new_env["COMFYUI_PATH"] = workspace_path
425-
subprocess.check_output(cmd, env=new_env)
428+
res = subprocess.run(cmd, env=new_env, check=True)
429+
if res.returncode != 0:
430+
typer.echo(
431+
"Failed to update node id cache.",
432+
err=True,
433+
)
434+
raise typer.Exit(code=1)
426435
except Exception:
427436
pass
428437

comfy_cli/command/install.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import sys
55

66
from rich import print
7+
import typer
78

8-
from comfy_cli import constants
9+
from comfy_cli import constants, ui
910
from comfy_cli.constants import GPU_OPTION
1011
from comfy_cli.workspace_manager import WorkspaceManager, check_comfy_repo
1112
from comfy_cli.command.custom_nodes.command import update_node_id_cache
1213

14+
workspace_manager = WorkspaceManager()
15+
1316

1417
def get_os_details():
1518
os_name = platform.system() # e.g., Linux, Darwin (macOS), Windows
@@ -120,13 +123,21 @@ def execute(
120123
skip_manager: bool,
121124
commit=None,
122125
gpu: constants.GPU_OPTION = None,
123-
platform: constants.OS = None,
126+
plat: constants.OS = None,
124127
skip_torch_or_directml: bool = False,
125128
skip_requirement: bool = False,
126129
*args,
127130
**kwargs,
128131
):
129-
print(f"Installing from '{url}' to '{comfy_path}'")
132+
133+
if not workspace_manager.skip_prompting:
134+
res = ui.prompt_confirm_action(f"Install from {url} to {comfy_path}?")
135+
136+
if not res:
137+
print("Aborting...")
138+
raise typer.Exit(code=1)
139+
140+
print(f"Installing from [bold yellow]'{url}'[/bold yellow] to '{comfy_path}'")
130141

131142
repo_dir = comfy_path
132143
parent_path = os.path.join(repo_dir, "..")
@@ -148,7 +159,7 @@ def execute(
148159
subprocess.run(["git", "checkout", commit])
149160

150161
install_comfyui_dependencies(
151-
repo_dir, gpu, platform, skip_torch_or_directml, skip_requirement
162+
repo_dir, gpu, plat, skip_torch_or_directml, skip_requirement
152163
)
153164

154165
WorkspaceManager().set_recent_workspace(repo_dir)

comfy_cli/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class OS(Enum):
3838
CONFIG_KEY_INSTALL_EVENT_TRIGGERED = "install_event_triggered"
3939
CONFIG_KEY_BACKGROUND = "background"
4040

41+
DEFAULT_TRACKING_VALUE = True
42+
4143
COMFY_LOCK_YAML_FILE = "comfy.lock.yaml"
4244

4345
# TODO: figure out a better way to check if this is a comfy repo

comfy_cli/env_checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def check(self):
100100
self.python_version = sys.version_info
101101

102102
# TODO: use ui.display_table
103-
def print(self):
103+
def fill_print_table(self):
104104
table = Table(":laptop_computer: Environment", "Value")
105105
table.add_row("Python Version", format_python_version(sys.version_info))
106106
table.add_row("Python Executable", sys.executable)
@@ -120,4 +120,4 @@ def print(self):
120120
else:
121121
table.add_row("Comfy Server Running", "[bold red]No[/bold red]")
122122

123-
console.print(table)
123+
return table

comfy_cli/tracking.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
from comfy_cli import logging, ui, constants
1010
from comfy_cli.config_manager import ConfigManager
11+
from comfy_cli.workspace_manager import WorkspaceManager
1112

1213
# Ignore logs from urllib3 that Mixpanel uses.
1314
logginglib.getLogger("urllib3").setLevel(logginglib.ERROR)
1415

1516
MIXPANEL_TOKEN = "93aeab8962b622d431ac19800ccc9f67"
16-
DISABLE_TELEMETRY = os.getenv("DISABLE_TELEMETRY", False)
1717
mp = Mixpanel(MIXPANEL_TOKEN) if MIXPANEL_TOKEN else None
1818

1919
# Generate a unique tracing ID per command.
@@ -24,24 +24,27 @@
2424
user_id = config_manager.get(constants.CONFIG_KEY_USER_ID)
2525
# tracking all events for a single command
2626
tracing_id = str(uuid.uuid4())
27+
workspace_manager = WorkspaceManager()
2728

2829
app = typer.Typer()
2930

3031

3132
@app.command()
3233
def enable():
3334
set_tracking_enabled(True)
34-
typer.echo(f"Tracking is now {'enabled' if enable else 'disabled'}.")
35+
typer.echo(f"Tracking is now {'enabled'}.")
3536
init_tracking(True)
3637

3738

3839
@app.command()
3940
def disable():
4041
set_tracking_enabled(False)
41-
typer.echo(f"Tracking is now {'enabled' if enable else 'disabled'}.")
42+
typer.echo(f"Tracking is now {'disabled'}.")
4243

4344

44-
def track_event(event_name: str, properties: any = {}):
45+
def track_event(event_name: str, properties: any = None):
46+
if properties is None:
47+
properties = {}
4548
logging.debug(
4649
f"tracking event called with event_name: {event_name} and properties: {properties}"
4750
)
@@ -89,13 +92,13 @@ def wrapper(*args, **kwargs):
8992
return decorator
9093

9194

92-
def prompt_tracking_consent(yes: bool = False):
95+
def prompt_tracking_consent(skip_prompt: bool = False, default_value: bool = False):
9396
tracking_enabled = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING)
9497
if tracking_enabled is not None:
9598
return
9699

97-
if yes:
98-
init_tracking(True)
100+
if skip_prompt:
101+
init_tracking(default_value)
99102
else:
100103
enable_tracking = ui.prompt_confirm_action(
101104
"Do you agree to enable tracking to improve the application?"
@@ -112,12 +115,12 @@ def init_tracking(enable_tracking: bool):
112115
if not enable_tracking:
113116
return
114117

115-
user_id = config_manager.get(constants.CONFIG_KEY_USER_ID)
116-
logging.debug(f'User identifier for tracking user_id found: {user_id}."')
117-
if user_id is None:
118-
user_id = str(uuid.uuid4())
119-
config_manager.set(constants.CONFIG_KEY_USER_ID, user_id)
120-
logging.debug(f'Setting user identifier for tracking user_id: {user_id}."')
118+
curr_user_id = config_manager.get(constants.CONFIG_KEY_USER_ID)
119+
logging.debug(f'User identifier for tracking user_id found: {curr_user_id}."')
120+
if curr_user_id is None:
121+
curr_user_id = str(uuid.uuid4())
122+
config_manager.set(constants.CONFIG_KEY_USER_ID, curr_user_id)
123+
logging.debug(f'Setting user identifier for tracking user_id: {curr_user_id}."')
121124

122125
# Note: only called once when the user interacts with the CLI for the
123126
# first time iff the permission is granted.

0 commit comments

Comments
 (0)