aleph65 commited on
Commit
c9c1685
·
verified ·
1 Parent(s): 06e77d1

download_missing_models.sh: GITHUB_TOKEN auth + codeload tarball fallback for git clones (datacenter IPs get 403 from github.com)

Browse files
Files changed (1) hide show
  1. download_missing_models.sh +77 -3
download_missing_models.sh CHANGED
@@ -20,6 +20,12 @@
20
  #
21
  # Auth: uses the HUGGING_FACE_ACCESS_TOKEN environment variable if set
22
  # (falls back to HF_TOKEN), otherwise prompts for a token.
 
 
 
 
 
 
23
 
24
  set -euo pipefail
25
 
@@ -312,14 +318,82 @@ def resolve_repo_url(dep):
312
  return None, None
313
 
314
  GIT_ENV = {**os.environ, "GIT_TERMINAL_PROMPT": "0"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
 
316
  def install_pack(name, url, ver):
317
  dest = os.path.join(CUSTOM_NODES_DIR, url.rstrip("/").split("/")[-1].removesuffix(".git"))
318
  print(f"\n{C_CYAN}Installing {name} from {url} ...{C_RESET}")
319
  if not os.path.isdir(dest):
320
- subprocess.run(["git", "clone", "--recursive", "--progress", url, dest],
321
- check=True, env=GIT_ENV, timeout=900, stdin=subprocess.DEVNULL)
322
- if ver and re.fullmatch(r"[0-9a-f]{40}", str(ver), re.I):
 
 
 
 
 
 
323
  r = subprocess.run(["git", "-C", dest, "checkout", ver], env=GIT_ENV, timeout=120,
324
  stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
325
  print(f" pinned to commit {ver[:12]}" if r.returncode == 0
 
20
  #
21
  # Auth: uses the HUGGING_FACE_ACCESS_TOKEN environment variable if set
22
  # (falls back to HF_TOKEN), otherwise prompts for a token.
23
+ # If GITHUB_TOKEN (or GH_TOKEN) is set, git clones of github.com repos are
24
+ # authenticated with it — datacenter IPs (e.g. RunPod) often get 403s on
25
+ # anonymous github.com traffic. Use a fine-grained PAT with no repo
26
+ # permissions; the token is sent as a one-shot header, never written to disk.
27
+ # With or without a token, a failed clone falls back to a repo tarball from
28
+ # codeload.github.com, which is typically not IP-blocked.
29
 
30
  set -euo pipefail
31
 
 
318
  return None, None
319
 
320
  GIT_ENV = {**os.environ, "GIT_TERMINAL_PROMPT": "0"}
321
+ GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
322
+
323
+ def github_owner_repo(url):
324
+ m = re.match(r"https?://github\.com/([^/]+)/([^/]+?)(?:\.git)?/?$", url)
325
+ return (m.group(1), m.group(2)) if m else None
326
+
327
+ def gh_headers():
328
+ h = {"User-Agent": "comfy-dep-installer"}
329
+ if GITHUB_TOKEN:
330
+ h["Authorization"] = f"Bearer {GITHUB_TOKEN}"
331
+ return h
332
+
333
+ def git_clone(url, dest):
334
+ """Clone, authenticating github.com with GITHUB_TOKEN when available.
335
+ The token goes in a per-invocation header (git -c), so it is never
336
+ persisted in the clone's .git/config."""
337
+ cmd = ["git"]
338
+ if GITHUB_TOKEN and github_owner_repo(url):
339
+ import base64
340
+ basic = base64.b64encode(f"x-access-token:{GITHUB_TOKEN}".encode()).decode()
341
+ cmd += ["-c", f"http.https://github.com/.extraheader=Authorization: basic {basic}"]
342
+ cmd += ["clone", "--recursive", "--progress", url, dest]
343
+ subprocess.run(cmd, check=True, env=GIT_ENV, timeout=900, stdin=subprocess.DEVNULL)
344
+
345
+ def install_from_tarball(url, ver, dest):
346
+ """Fallback when git clone is refused (github.com 403s anonymous requests
347
+ from many datacenter IPs): fetch the repo tarball from codeload.github.com,
348
+ which sits on separate infrastructure and is typically not blocked.
349
+ Caveats vs a real clone: no .git (ComfyUI-Manager can't update the pack)
350
+ and no submodules."""
351
+ owner, repo = github_owner_repo(url)
352
+ if ver and re.fullmatch(r"[0-9a-f]{40}", str(ver), re.I):
353
+ ref = str(ver)
354
+ else:
355
+ info = fetch_json_gh(f"https://api.github.com/repos/{owner}/{repo}")
356
+ ref = info.get("default_branch") or "main"
357
+ print(f" {C_YELLOW}falling back to tarball of {owner}/{repo}@{ref[:12]} via codeload.github.com ...{C_RESET}")
358
+ import shutil, tarfile, tempfile
359
+ req = urllib.request.Request(
360
+ f"https://codeload.github.com/{owner}/{repo}/tar.gz/{ref}", headers=gh_headers())
361
+ os.makedirs(CUSTOM_NODES_DIR, exist_ok=True)
362
+ # tmp dir lives next to dest so the final rename stays on one filesystem;
363
+ # "." prefix keeps installed_packs() from ever seeing it
364
+ with tempfile.TemporaryDirectory(dir=CUSTOM_NODES_DIR, prefix=".tarball-") as tmp:
365
+ tar_path = os.path.join(tmp, "repo.tar.gz")
366
+ with urllib.request.urlopen(req, timeout=120) as r, open(tar_path, "wb") as fh:
367
+ shutil.copyfileobj(r, fh)
368
+ with tarfile.open(tar_path) as tar:
369
+ try:
370
+ tar.extractall(tmp, filter="data")
371
+ except TypeError: # Python < 3.12 has no filter=
372
+ tar.extractall(tmp)
373
+ tops = [e for e in os.listdir(tmp)
374
+ if os.path.isdir(os.path.join(tmp, e))]
375
+ if len(tops) != 1:
376
+ raise RuntimeError(f"unexpected tarball layout: {tops}")
377
+ os.rename(os.path.join(tmp, tops[0]), dest)
378
+
379
+ def fetch_json_gh(url):
380
+ req = urllib.request.Request(url, headers=gh_headers())
381
+ with urllib.request.urlopen(req, timeout=30) as r:
382
+ return json.load(r)
383
 
384
  def install_pack(name, url, ver):
385
  dest = os.path.join(CUSTOM_NODES_DIR, url.rstrip("/").split("/")[-1].removesuffix(".git"))
386
  print(f"\n{C_CYAN}Installing {name} from {url} ...{C_RESET}")
387
  if not os.path.isdir(dest):
388
+ try:
389
+ git_clone(url, dest)
390
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
391
+ if not github_owner_repo(url):
392
+ raise
393
+ print(f" {C_YELLOW}⚠ git clone failed ({e}){C_RESET}")
394
+ install_from_tarball(url, ver, dest)
395
+ if ver and re.fullmatch(r"[0-9a-f]{40}", str(ver), re.I) \
396
+ and os.path.isdir(os.path.join(dest, ".git")):
397
  r = subprocess.run(["git", "-C", dest, "checkout", ver], env=GIT_ENV, timeout=120,
398
  stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
399
  print(f" pinned to commit {ver[:12]}" if r.returncode == 0