44
55import asyncio
66import base64
7- import os
87import re
8+ import sys
99from typing import Final
1010from urllib .parse import urlparse
1111
12- from starlette .status import (
13- HTTP_200_OK ,
14- HTTP_301_MOVED_PERMANENTLY ,
15- HTTP_302_FOUND ,
16- HTTP_401_UNAUTHORIZED ,
17- HTTP_403_FORBIDDEN ,
18- HTTP_404_NOT_FOUND ,
19- )
12+ import httpx
13+ from starlette .status import HTTP_200_OK , HTTP_401_UNAUTHORIZED , HTTP_403_FORBIDDEN , HTTP_404_NOT_FOUND
2014
2115from gitingest .utils .compat_func import removesuffix
2216from gitingest .utils .exceptions import InvalidGitHubTokenError
17+ from server .server_utils import Colors
2318
2419# GitHub Personal-Access tokens (classic + fine-grained).
2520# - ghp_ / gho_ / ghu_ / ghs_ / ghr_ → 36 alphanumerics
@@ -81,6 +76,8 @@ async def run_command(*args: str) -> tuple[bytes, bytes]:
8176async def ensure_git_installed () -> None :
8277 """Ensure Git is installed and accessible on the system.
8378
79+ On Windows, this also checks whether Git is configured to support long file paths.
80+
8481 Raises
8582 ------
8683 RuntimeError
@@ -92,6 +89,20 @@ async def ensure_git_installed() -> None:
9289 except RuntimeError as exc :
9390 msg = "Git is not installed or not accessible. Please install Git first."
9491 raise RuntimeError (msg ) from exc
92+ if sys .platform == "win32" :
93+ try :
94+ stdout , _ = await run_command ("git" , "config" , "core.longpaths" )
95+ if stdout .decode ().strip ().lower () != "true" :
96+ print (
97+ f"{ Colors .BROWN } WARN{ Colors .END } : { Colors .RED } Git clone may fail on Windows "
98+ f"due to long file paths:{ Colors .END } " ,
99+ )
100+ print (f"{ Colors .RED } To avoid this issue, consider enabling long path support with:{ Colors .END } " )
101+ print (f"{ Colors .RED } git config --global core.longpaths true{ Colors .END } " )
102+ print (f"{ Colors .RED } Note: This command may require administrator privileges.{ Colors .END } " )
103+ except RuntimeError :
104+ # Ignore if checking 'core.longpaths' fails.
105+ pass
95106
96107
97108async def check_repo_exists (url : str , token : str | None = None ) -> bool :
@@ -115,45 +126,28 @@ async def check_repo_exists(url: str, token: str | None = None) -> bool:
115126 If the host returns an unrecognised status code.
116127
117128 """
118- # TODO: use `requests` instead of `curl`
119- cmd : list [str ] = [
120- "curl" ,
121- "--silent" , # Suppress output
122- "--location" , # Follow redirects
123- "--write-out" ,
124- "%{http_code}" , # Write the HTTP status code to stdout
125- "-o" ,
126- os .devnull ,
127- ]
129+ headers = {}
128130
129131 if token and is_github_host (url ):
130132 host , owner , repo = _parse_github_url (url )
131133 # Public GitHub vs. GitHub Enterprise
132134 base_api = "https://api.github.com" if host == "github.com" else f"https://{ host } /api/v3"
133135 url = f"{ base_api } /repos/{ owner } /{ repo } "
134- cmd += ["--header" , f"Authorization: Bearer { token } " ]
135-
136- cmd .append (url )
136+ headers ["Authorization" ] = f"Bearer { token } "
137137
138- proc = await asyncio .create_subprocess_exec (
139- * cmd ,
140- stdout = asyncio .subprocess .PIPE ,
141- stderr = asyncio .subprocess .PIPE ,
142- )
143- stdout , _ = await proc .communicate ()
138+ async with httpx .AsyncClient (follow_redirects = True ) as client :
139+ try :
140+ response = await client .head (url , headers = headers )
141+ except httpx .RequestError :
142+ return False
144143
145- if proc .returncode != 0 :
146- return False
144+ status_code = response .status_code
147145
148- status = int (stdout .decode ().strip ())
149- if status in {HTTP_200_OK , HTTP_301_MOVED_PERMANENTLY }:
146+ if status_code == HTTP_200_OK :
150147 return True
151- # TODO: handle 302 redirects
152- if status in {HTTP_404_NOT_FOUND , HTTP_302_FOUND }:
153- return False
154- if status in {HTTP_401_UNAUTHORIZED , HTTP_403_FORBIDDEN }:
148+ if status_code in {HTTP_401_UNAUTHORIZED , HTTP_403_FORBIDDEN , HTTP_404_NOT_FOUND }:
155149 return False
156- msg = f"Unexpected HTTP status { status } for { url } "
150+ msg = f"Unexpected HTTP status { status_code } for { url } "
157151 raise RuntimeError (msg )
158152
159153
0 commit comments