Skip to content

Commit 2244b89

Browse files
[feat] update frontend PR support for nx and pnpm (#314)
1 parent 42833cc commit 2244b89

3 files changed

Lines changed: 115 additions & 13 deletions

File tree

comfy_cli/command/install.py

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from rich import print as rprint
1212
from rich.console import Console
1313
from rich.panel import Panel
14+
from rich.prompt import Confirm
1415

1516
from comfy_cli import constants, ui, utils
1617
from comfy_cli.command.custom_nodes.command import update_node_id_cache
@@ -634,7 +635,7 @@ def find_pr_by_branch(repo_owner: str, repo_name: str, username: str, branch: st
634635

635636

636637
def verify_node_tools() -> bool:
637-
"""Verify that Node.js, npm, and vite are available"""
638+
"""Verify that Node.js, npm, and pnpm are available for frontend building"""
638639
try:
639640
# Check Node.js
640641
node_result = subprocess.run(["node", "--version"], capture_output=True, text=True, check=False)
@@ -651,7 +652,7 @@ def verify_node_tools() -> bool:
651652
node_version = node_result.stdout.strip()
652653
rprint(f"[green]Found Node.js {node_version}[/green]")
653654

654-
# Check npm
655+
# Check npm (needed for pnpm installation)
655656
npm_result = subprocess.run(["npm", "--version"], capture_output=True, text=True, check=False)
656657
if npm_result.returncode != 0:
657658
rprint("[bold red]npm is not installed.[/bold red]")
@@ -661,9 +662,49 @@ def verify_node_tools() -> bool:
661662
npm_version = npm_result.stdout.strip()
662663
rprint(f"[green]Found npm {npm_version}[/green]")
663664

665+
# Check pnpm (required for modern frontend)
666+
pnpm_result = subprocess.run(["pnpm", "--version"], capture_output=True, text=True, check=False)
667+
if pnpm_result.returncode != 0:
668+
rprint("[yellow]pnpm is not installed but is required for the modern frontend.[/yellow]")
669+
670+
# Ask user permission to install pnpm
671+
install_pnpm = Confirm.ask(
672+
"[bold yellow]Install pnpm automatically using npm?[/bold yellow] (This will run: npm install -g pnpm)"
673+
)
674+
675+
if not install_pnpm:
676+
rprint("[bold red]Cannot build frontend without pnpm.[/bold red]")
677+
rprint("[yellow]To install manually:[/yellow]")
678+
rprint(" npm install -g pnpm")
679+
return False
680+
681+
# Install pnpm
682+
rprint("[yellow]Installing pnpm...[/yellow]")
683+
install_result = subprocess.run(
684+
["npm", "install", "-g", "pnpm"], capture_output=True, text=True, check=False
685+
)
686+
687+
if install_result.returncode != 0:
688+
rprint("[bold red]Failed to install pnpm automatically.[/bold red]")
689+
rprint(f"[red]Error: {install_result.stderr}[/red]")
690+
rprint("[yellow]Please install manually: npm install -g pnpm[/yellow]")
691+
return False
692+
693+
# Verify pnpm installation
694+
verify_result = subprocess.run(["pnpm", "--version"], capture_output=True, text=True, check=False)
695+
if verify_result.returncode != 0:
696+
rprint("[bold red]pnpm installation failed to verify.[/bold red]")
697+
return False
698+
699+
pnpm_version = verify_result.stdout.strip()
700+
rprint(f"[green]Successfully installed pnpm {pnpm_version}[/green]")
701+
else:
702+
pnpm_version = pnpm_result.stdout.strip()
703+
rprint(f"[green]Found pnpm {pnpm_version}[/green]")
704+
664705
return True
665706
except FileNotFoundError as e:
666-
rprint(f"[bold red]Error checking Node.js tools: {e}[/bold red]")
707+
rprint(f"[bold red]Error checking frontend tools: {e}[/bold red]")
667708
return False
668709

669710

@@ -743,11 +784,11 @@ def handle_temporary_frontend_pr(frontend_pr: str) -> Optional[str]:
743784
try:
744785
os.chdir(repo_path)
745786

746-
# Run npm install
747-
rprint("Running npm install...")
748-
npm_install = subprocess.run(["npm", "install"], capture_output=True, text=True, check=False)
749-
if npm_install.returncode != 0:
750-
rprint(f"[bold red]npm install failed:[/bold red]\n{npm_install.stderr}")
787+
# Run pnpm install
788+
rprint("Running pnpm install...")
789+
pnpm_install = subprocess.run(["pnpm", "install"], capture_output=True, text=True, check=False)
790+
if pnpm_install.returncode != 0:
791+
rprint(f"[bold red]pnpm install failed:[/bold red]\n{pnpm_install.stderr}")
751792
return None
752793

753794
# Build with vite

tests/comfy_cli/command/test_frontend_pr.py

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ class TestNodeToolsVerification:
7878

7979
@patch("subprocess.run")
8080
def test_verify_node_tools_success(self, mock_run):
81-
"""Test successful Node.js and npm verification"""
82-
# Mock successful node and npm commands
81+
"""Test successful Node.js, npm, and pnpm verification"""
82+
# Mock successful node, npm, and pnpm commands
8383
node_result = Mock()
8484
node_result.returncode = 0
8585
node_result.stdout = "v18.0.0"
@@ -88,10 +88,14 @@ def test_verify_node_tools_success(self, mock_run):
8888
npm_result.returncode = 0
8989
npm_result.stdout = "9.0.0"
9090

91-
mock_run.side_effect = [node_result, npm_result]
91+
pnpm_result = Mock()
92+
pnpm_result.returncode = 0
93+
pnpm_result.stdout = "8.0.0"
94+
95+
mock_run.side_effect = [node_result, npm_result, pnpm_result]
9296

9397
assert verify_node_tools() is True
94-
assert mock_run.call_count == 2
98+
assert mock_run.call_count == 3
9599

96100
@patch("subprocess.run")
97101
def test_verify_node_tools_missing_node(self, mock_run):
@@ -119,6 +123,63 @@ def test_verify_node_tools_missing_npm(self, mock_run):
119123
assert verify_node_tools() is False
120124
assert mock_run.call_count == 2
121125

126+
@patch("rich.prompt.Confirm.ask")
127+
@patch("subprocess.run")
128+
def test_verify_node_tools_auto_install_pnpm(self, mock_run, mock_confirm):
129+
"""Test automatic pnpm installation when user agrees"""
130+
# Mock successful node and npm
131+
node_result = Mock()
132+
node_result.returncode = 0
133+
node_result.stdout = "v18.0.0"
134+
135+
npm_result = Mock()
136+
npm_result.returncode = 0
137+
npm_result.stdout = "9.0.0"
138+
139+
# Mock pnpm not found initially
140+
pnpm_missing = Mock()
141+
pnpm_missing.returncode = 1
142+
143+
# Mock successful pnpm installation
144+
install_result = Mock()
145+
install_result.returncode = 0
146+
147+
# Mock pnpm verification after install
148+
pnpm_verify = Mock()
149+
pnpm_verify.returncode = 0
150+
pnpm_verify.stdout = "8.0.0"
151+
152+
mock_run.side_effect = [node_result, npm_result, pnpm_missing, install_result, pnpm_verify]
153+
mock_confirm.return_value = True # User agrees to install
154+
155+
assert verify_node_tools() is True
156+
assert mock_run.call_count == 5
157+
mock_confirm.assert_called_once()
158+
159+
@patch("rich.prompt.Confirm.ask")
160+
@patch("subprocess.run")
161+
def test_verify_node_tools_user_declines_pnpm_install(self, mock_run, mock_confirm):
162+
"""Test when user declines pnpm installation"""
163+
# Mock successful node and npm
164+
node_result = Mock()
165+
node_result.returncode = 0
166+
node_result.stdout = "v18.0.0"
167+
168+
npm_result = Mock()
169+
npm_result.returncode = 0
170+
npm_result.stdout = "9.0.0"
171+
172+
# Mock pnpm not found
173+
pnpm_missing = Mock()
174+
pnpm_missing.returncode = 1
175+
176+
mock_run.side_effect = [node_result, npm_result, pnpm_missing]
177+
mock_confirm.return_value = False # User declines install
178+
179+
assert verify_node_tools() is False
180+
assert mock_run.call_count == 3
181+
mock_confirm.assert_called_once()
182+
122183
@patch("subprocess.run")
123184
def test_verify_node_tools_file_not_found(self, mock_run):
124185
"""Test when commands are not found"""

tests/comfy_cli/command/test_launch_frontend_pr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def test_launch_frontend_pr_cache_miss_builds(
108108

109109
# Mock successful build
110110
mock_run.side_effect = [
111-
Mock(returncode=0), # npm install
111+
Mock(returncode=0), # pnpm install
112112
Mock(returncode=0), # vite build
113113
]
114114

0 commit comments

Comments
 (0)