From 4a66106cb169c3ea2729ee4d784109603fdd2292 Mon Sep 17 00:00:00 2001 From: Robin Huang Date: Thu, 7 Nov 2024 13:06:14 -0800 Subject: [PATCH 1/6] Handle case when Github releases rate limit is reached. --- comfy_cli/command/install.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/comfy_cli/command/install.py b/comfy_cli/command/install.py index b80cba2b..3630e9e7 100644 --- a/comfy_cli/command/install.py +++ b/comfy_cli/command/install.py @@ -3,6 +3,7 @@ import subprocess import sys from typing import Dict, List, Optional, TypedDict +from datetime import datetime import requests import semver @@ -188,7 +189,11 @@ def execute( clone_comfyui(url=url, repo_dir=repo_dir) if version != "nightly": - checkout_stable_comfyui(version=version, repo_dir=repo_dir) + try: + checkout_stable_comfyui(version=version, repo_dir=repo_dir) + except Exception as e: + rprint(f"[bold red]Error checking out ComfyUI version: {e}[/bold red]") + sys.exit(1) elif not check_comfy_repo(repo_dir)[0]: rprint( @@ -281,12 +286,35 @@ def validate_version(version: str) -> Optional[str]: ) from exc +class GitHubRateLimitError(Exception): + """Raised when GitHub API rate limit is exceeded""" + + def fetch_github_releases(repo_owner: str, repo_name: str) -> List[Dict[str, str]]: """ Fetch the list of releases from the GitHub API. + Handles rate limiting by logging the wait time. """ url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases" - response = requests.get(url) + + response = requests.get(url, timeout=5) + + # Handle rate limiting + if response.status_code in (403, 429): + # Check rate limit headers + remaining = int(response.headers.get("x-ratelimit-remaining", 0)) + if remaining == 0: + reset_time = int(response.headers.get("x-ratelimit-reset", 0)) + message = f"Primary rate limit from Github exceeded! Please retry after: {reset_time})" + raise GitHubRateLimitError(message) + + if "retry-after" in response.headers: + wait_seconds = int(response.headers["retry-after"]) + message = f"Rate limit from Github exceeded! Please wait {wait_seconds} seconds before retrying." + rprint(f"[yellow]{message}[/yellow]") + raise GitHubRateLimitError(message) + + response.raise_for_status() return response.json() From 07e4c87989ca76c6e2d1989f4dbff7b8446717d2 Mon Sep 17 00:00:00 2001 From: Robin Huang Date: Thu, 7 Nov 2024 13:13:12 -0800 Subject: [PATCH 2/6] Attach GITHUB_TOKEN to releases request if available. --- comfy_cli/command/install.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/comfy_cli/command/install.py b/comfy_cli/command/install.py index 3630e9e7..07466512 100644 --- a/comfy_cli/command/install.py +++ b/comfy_cli/command/install.py @@ -191,7 +191,7 @@ def execute( if version != "nightly": try: checkout_stable_comfyui(version=version, repo_dir=repo_dir) - except Exception as e: + except GitHubRateLimitError as e: rprint(f"[bold red]Error checking out ComfyUI version: {e}[/bold red]") sys.exit(1) @@ -199,7 +199,7 @@ def execute( rprint( f"[bold red]'{repo_dir}' already exists. But it is an invalid ComfyUI repository. Remove it and retry.[/bold red]" ) - exit(-1) + sys.exit(-1) # checkout specified commit if commit is not None: @@ -297,7 +297,11 @@ def fetch_github_releases(repo_owner: str, repo_name: str) -> List[Dict[str, str """ url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases" - response = requests.get(url, timeout=5) + headers = {} + if github_token := os.getenv("GITHUB_TOKEN"): + headers["Authorization"] = f"Bearer {github_token}" + + response = requests.get(url, headers=headers, timeout=5) # Handle rate limiting if response.status_code in (403, 429): From dbfb8606db7cdd63c1f4c7aaaadd9deec34ceba3 Mon Sep 17 00:00:00 2001 From: Robin Huang Date: Thu, 7 Nov 2024 13:17:02 -0800 Subject: [PATCH 3/6] Fix test. --- tests/comfy_cli/test_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/comfy_cli/test_install.py b/tests/comfy_cli/test_install.py index 1e97b963..5e5cfd9f 100644 --- a/tests/comfy_cli/test_install.py +++ b/tests/comfy_cli/test_install.py @@ -53,7 +53,7 @@ def test_fetch_releases_success(mock_get): assert len(releases) == 2 assert releases[0]["tag_name"] == "v1.0.0" assert releases[1]["tag_name"] == "v1.1.0" - mock_get.assert_called_once_with("https://api.github.com/repos/owner/repo/releases") + mock_get.assert_called_once_with("https://api.github.com/repos/owner/repo/releases", 5) @patch("requests.get") From 78829d5622fd0aed3f9e52d667c4a8a48842f43e Mon Sep 17 00:00:00 2001 From: Robin Huang Date: Thu, 7 Nov 2024 13:19:40 -0800 Subject: [PATCH 4/6] Fix test. --- tests/comfy_cli/test_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/comfy_cli/test_install.py b/tests/comfy_cli/test_install.py index 5e5cfd9f..05d52903 100644 --- a/tests/comfy_cli/test_install.py +++ b/tests/comfy_cli/test_install.py @@ -53,7 +53,7 @@ def test_fetch_releases_success(mock_get): assert len(releases) == 2 assert releases[0]["tag_name"] == "v1.0.0" assert releases[1]["tag_name"] == "v1.1.0" - mock_get.assert_called_once_with("https://api.github.com/repos/owner/repo/releases", 5) + mock_get.assert_called_once_with("https://api.github.com/repos/owner/repo/releases", headers={}, timeout=5) @patch("requests.get") From 7d5642dab07594b89b966ea61b5edfcd2dc83f99 Mon Sep 17 00:00:00 2001 From: Robin Huang Date: Thu, 7 Nov 2024 13:20:23 -0800 Subject: [PATCH 5/6] Ruff fix. --- comfy_cli/command/install.py | 1 - 1 file changed, 1 deletion(-) diff --git a/comfy_cli/command/install.py b/comfy_cli/command/install.py index 07466512..1ddf16c7 100644 --- a/comfy_cli/command/install.py +++ b/comfy_cli/command/install.py @@ -3,7 +3,6 @@ import subprocess import sys from typing import Dict, List, Optional, TypedDict -from datetime import datetime import requests import semver From ca03b9c0b8083ca1bfff254385912731e0d68a95 Mon Sep 17 00:00:00 2001 From: Robin Huang Date: Mon, 11 Nov 2024 19:25:40 -0800 Subject: [PATCH 6/6] Checkout specific commit for ComfyUI-Manager. --- comfy_cli/cmdline.py | 6 ++++++ comfy_cli/command/install.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/comfy_cli/cmdline.py b/comfy_cli/cmdline.py index d903efc4..6c71c794 100644 --- a/comfy_cli/cmdline.py +++ b/comfy_cli/cmdline.py @@ -242,6 +242,10 @@ def install( help="Use new fast dependency installer", ), ] = False, + manager_commit: Annotated[ + Optional[str], + typer.Option(help="Specify commit hash for ComfyUI-Manager"), + ] = None, ): check_for_updates() checker = EnvChecker() @@ -279,6 +283,7 @@ def install( skip_torch_or_directml=skip_torch_or_directml, skip_requirement=skip_requirement, fast_deps=fast_deps, + manager_commit=manager_commit, ) rprint(f"ComfyUI is installed at: {comfy_path}") return None @@ -346,6 +351,7 @@ def install( skip_torch_or_directml=skip_torch_or_directml, skip_requirement=skip_requirement, fast_deps=fast_deps, + manager_commit=manager_commit, ) rprint(f"ComfyUI is installed at: {comfy_path}") diff --git a/comfy_cli/command/install.py b/comfy_cli/command/install.py index 1ddf16c7..4754ed28 100644 --- a/comfy_cli/command/install.py +++ b/comfy_cli/command/install.py @@ -157,6 +157,7 @@ def execute( skip_manager: bool, version: str, commit: Optional[str] = None, + manager_commit: Optional[str] = None, gpu: constants.GPU_OPTION = None, cuda_version: constants.CUDAVersion = constants.CUDAVersion.v12_1, plat: constants.OS = None, @@ -238,6 +239,8 @@ def execute( ) else: subprocess.run(["git", "clone", manager_url, manager_repo_dir], check=True) + if manager_commit is not None: + subprocess.run(["git", "checkout", manager_commit], check=True, cwd=manager_repo_dir) if not fast_deps: pip_install_manager_dependencies(repo_dir)