@@ -631,3 +631,172 @@ def find_pr_by_branch(repo_owner: str, repo_name: str, username: str, branch: st
631631
632632 except requests .RequestException :
633633 return None
634+
635+
636+ def verify_node_tools () -> bool :
637+ """Verify that Node.js, npm, and vite are available"""
638+ try :
639+ # Check Node.js
640+ node_result = subprocess .run (["node" , "--version" ], capture_output = True , text = True , check = False )
641+ if node_result .returncode != 0 :
642+ rprint ("[bold red]Node.js is not installed.[/bold red]" )
643+ rprint ("[yellow]To use --frontend-pr, please install Node.js first:[/yellow]" )
644+ rprint (" • Download from: https://nodejs.org/" )
645+ rprint (" • Or use a package manager:" )
646+ rprint (" - macOS: brew install node" )
647+ rprint (" - Ubuntu/Debian: sudo apt install nodejs npm" )
648+ rprint (" - Windows: winget install OpenJS.NodeJS" )
649+ return False
650+
651+ node_version = node_result .stdout .strip ()
652+ rprint (f"[green]Found Node.js { node_version } [/green]" )
653+
654+ # Check npm
655+ npm_result = subprocess .run (["npm" , "--version" ], capture_output = True , text = True , check = False )
656+ if npm_result .returncode != 0 :
657+ rprint ("[bold red]npm is not installed.[/bold red]" )
658+ rprint ("[yellow]npm usually comes with Node.js. Try reinstalling Node.js.[/yellow]" )
659+ return False
660+
661+ npm_version = npm_result .stdout .strip ()
662+ rprint (f"[green]Found npm { npm_version } [/green]" )
663+
664+ return True
665+ except FileNotFoundError as e :
666+ rprint (f"[bold red]Error checking Node.js tools: { e } [/bold red]" )
667+ return False
668+
669+
670+ def handle_temporary_frontend_pr (frontend_pr : str ) -> Optional [str ]:
671+ """Handle temporary frontend PR for launch - returns path to built frontend"""
672+ from comfy_cli .pr_cache import PRCache
673+
674+ rprint ("\n [bold blue]Preparing frontend PR for launch...[/bold blue]" )
675+
676+ # Verify Node.js tools first
677+ if not verify_node_tools ():
678+ rprint ("[bold red]Cannot build frontend without Node.js and npm[/bold red]" )
679+ return None
680+
681+ # Parse frontend PR reference
682+ try :
683+ repo_owner , repo_name , pr_number = parse_frontend_pr_reference (frontend_pr )
684+ except ValueError as e :
685+ rprint (f"[bold red]Error parsing frontend PR reference: { e } [/bold red]" )
686+ return None
687+
688+ # Fetch PR info
689+ try :
690+ if pr_number :
691+ pr_info = fetch_pr_info (repo_owner , repo_name , pr_number )
692+ else :
693+ username , branch = frontend_pr .split (":" , 1 )
694+ pr_info = find_pr_by_branch (repo_owner , repo_name , username , branch )
695+
696+ if not pr_info :
697+ rprint (f"[bold red]Frontend PR not found: { frontend_pr } [/bold red]" )
698+ return None
699+ except Exception as e :
700+ rprint (f"[bold red]Error fetching frontend PR information: { e } [/bold red]" )
701+ return None
702+
703+ # Check cache first
704+ cache = PRCache ()
705+ cached_path = cache .get_cached_frontend_path (pr_info )
706+ if cached_path :
707+ rprint (f"[bold green]Using cached frontend build for PR #{ pr_info .number } [/bold green]" )
708+ rprint (f"[bold green]PR #{ pr_info .number } : { pr_info .title } by { pr_info .user } [/bold green]" )
709+ return str (cached_path )
710+
711+ # Need to build - show PR info
712+ console .print (
713+ Panel (
714+ f"[bold]Frontend PR #{ pr_info .number } [/bold]: { pr_info .title } \n "
715+ f"[yellow]Author[/yellow]: { pr_info .user } \n "
716+ f"[yellow]Branch[/yellow]: { pr_info .head_branch } \n "
717+ f"[yellow]Source[/yellow]: { pr_info .head_repo_url } " ,
718+ title = "[bold blue]Building Frontend PR[/bold blue]" ,
719+ border_style = "blue" ,
720+ )
721+ )
722+
723+ # Build in cache directory
724+ cache_path = cache .get_frontend_cache_path (pr_info )
725+ cache_path .mkdir (parents = True , exist_ok = True )
726+
727+ # Clone or update repository
728+ repo_path = cache_path / "repo"
729+ if not (repo_path / ".git" ).exists ():
730+ rprint ("Cloning frontend repository..." )
731+ clone_comfyui (url = pr_info .base_repo_url , repo_dir = str (repo_path ))
732+
733+ # Checkout PR
734+ rprint (f"Checking out PR #{ pr_info .number } ..." )
735+ success = checkout_pr (str (repo_path ), pr_info )
736+ if not success :
737+ rprint ("[bold red]Failed to checkout frontend PR[/bold red]" )
738+ return None
739+
740+ # Build frontend
741+ rprint ("\n [bold yellow]Building frontend (this may take a moment)...[/bold yellow]" )
742+ original_dir = os .getcwd ()
743+ try :
744+ os .chdir (repo_path )
745+
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 } " )
751+ return None
752+
753+ # Build with vite
754+ rprint ("Building with vite..." )
755+ vite_build = subprocess .run (["npx" , "vite" , "build" ], capture_output = True , text = True , check = False )
756+ if vite_build .returncode != 0 :
757+ rprint (f"[bold red]vite build failed:[/bold red]\n { vite_build .stderr } " )
758+ return None
759+
760+ # Check if dist exists
761+ dist_path = repo_path / "dist"
762+ if dist_path .exists ():
763+ # Save cache info
764+ cache .save_cache_info (pr_info , cache_path )
765+ rprint ("[bold green]✓ Frontend built and cached successfully[/bold green]" )
766+ rprint (f"[bold green]Using frontend from PR #{ pr_info .number } : { pr_info .title } [/bold green]" )
767+ rprint (f"[dim]Cache will expire in { cache .DEFAULT_MAX_CACHE_AGE_DAYS } days[/dim]" )
768+ return str (dist_path )
769+ else :
770+ rprint ("[bold red]Frontend build completed but dist folder not found[/bold red]" )
771+ return None
772+
773+ finally :
774+ os .chdir (original_dir )
775+
776+
777+ def parse_frontend_pr_reference (pr_ref : str ) -> tuple [str , str , Optional [int ]]:
778+ """
779+ Parse frontend PR reference. Similar to parse_pr_reference but defaults to Comfy-Org/ComfyUI_frontend
780+ """
781+ pr_ref = pr_ref .strip ()
782+
783+ if pr_ref .startswith ("https://github.com/" ):
784+ parsed = urlparse (pr_ref )
785+ if "/pull/" in parsed .path :
786+ path_parts = parsed .path .strip ("/" ).split ("/" )
787+ if len (path_parts ) >= 4 :
788+ repo_owner = path_parts [0 ]
789+ repo_name = path_parts [1 ]
790+ pr_number = int (path_parts [3 ])
791+ return repo_owner , repo_name , pr_number
792+
793+ elif pr_ref .startswith ("#" ):
794+ pr_number = int (pr_ref [1 :])
795+ return "Comfy-Org" , "ComfyUI_frontend" , pr_number
796+
797+ elif ":" in pr_ref :
798+ username , branch = pr_ref .split (":" , 1 )
799+ return "Comfy-Org" , "ComfyUI_frontend" , None
800+
801+ else :
802+ raise ValueError (f"Invalid frontend PR reference format: { pr_ref } " )
0 commit comments