Skip to content

Commit c5f50fc

Browse files
pr fixes
1 parent dc3745b commit c5f50fc

File tree

4 files changed

+30
-15
lines changed

4 files changed

+30
-15
lines changed

src/gitingest/cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ async def _async_main(
131131
If ``True``, also ingest files matched by ``.gitignore`` or ``.gitingestignore`` (default: ``False``).
132132
token : str | None
133133
GitHub personal access token (PAT) for accessing private repositories.
134+
Can also be set via the ``GITHUB_TOKEN`` environment variable.
134135
output : str | None
135136
The path where the output file will be written (default: ``digest.txt`` in current directory).
136137
Use ``"-"`` to write to ``stdout``.

src/gitingest/utils/exceptions.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def __init__(self, message: str) -> None:
4141
class InvalidGitHubTokenError(ValueError):
4242
"""Exception raised when a GitHub Personal Access Token is malformed."""
4343

44-
def __init__(self, token: str) -> None:
45-
msg = f"Invalid GitHub token format: {token!r}. To generate a token, see https://github.com/settings/tokens."
44+
def __init__(self) -> None:
45+
msg = (
46+
"Invalid GitHub token format. To generate a token, go to "
47+
"https://github.com/settings/tokens/new?description=gitingest&scopes=repo."
48+
)
4649
super().__init__(msg)

src/gitingest/utils/git_utils.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@
99
from typing import Final
1010
from 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+
)
20+
1221
from gitingest.utils.exceptions import InvalidGitHubTokenError
1322

1423
# GitHub Personal-Access tokens (classic + fine-grained).
1524
# - ghp_ / gho_ / ghu_ / ghs_ / ghr_ → 36 alphanumerics
1625
# - github_pat_ → 22 alphanumerics + "_" + 59 alphanumerics
1726
_GITHUB_PAT_PATTERN: Final[str] = r"^(?:gh[pousr]_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59})$"
1827

19-
_OK: Final[set[str]] = {"200", "301"} # reachable / canonical redirect
20-
_MISSING: Final[set[str]] = {"404", "302"} # not found / redirect
21-
_NEEDS_AUTH: Final[set[str]] = {"401", "403"} # login or forbidden
22-
2328

2429
def is_github_host(url: str) -> bool:
2530
"""Check if a URL is from a GitHub host (github.com or GitHub Enterprise).
@@ -109,6 +114,7 @@ async def check_repo_exists(url: str, token: str | None = None) -> bool:
109114
If the host returns an unrecognised status code.
110115
111116
"""
117+
# TODO: use `requests` instead of `curl`
112118
cmd: list[str] = [
113119
"curl",
114120
"--silent",
@@ -139,14 +145,15 @@ async def check_repo_exists(url: str, token: str | None = None) -> bool:
139145
if proc.returncode != 0:
140146
return False
141147

142-
status = stdout.decode().strip()
143-
if status in _OK:
148+
status = int(stdout.decode().strip())
149+
if status in {HTTP_200_OK, HTTP_301_MOVED_PERMANENTLY}:
144150
return True
145-
if status in _MISSING:
151+
# TODO: handle 302 redirects
152+
if status in {HTTP_404_NOT_FOUND, HTTP_302_FOUND}:
146153
return False
147-
if status in _NEEDS_AUTH:
154+
if status in {HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN}:
148155
return False
149-
msg = f"Unexpected HTTP status {status!r} for {url}"
156+
msg = f"Unexpected HTTP status {status} for {url}"
150157
raise RuntimeError(msg)
151158

152159

@@ -294,4 +301,4 @@ def validate_github_token(token: str) -> None:
294301
295302
"""
296303
if not re.fullmatch(_GITHUB_PAT_PATTERN, token):
297-
raise InvalidGitHubTokenError(token)
304+
raise InvalidGitHubTokenError

src/server/routers/download.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from fastapi import APIRouter, HTTPException
44
from fastapi.responses import FileResponse
5+
from starlette.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND
56

67
from gitingest.config import TMP_BASE_PATH
78

@@ -32,14 +33,17 @@ async def download_ingest(digest_id: str) -> FileResponse:
3233
directory = TMP_BASE_PATH / digest_id
3334

3435
if not directory.is_dir():
35-
raise HTTPException(status_code=404, detail=f"Digest {digest_id!r} not found")
36+
raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=f"Digest {digest_id!r} not found")
3637

3738
try:
3839
first_txt_file = next(directory.glob("*.txt"))
3940
except StopIteration as exc:
40-
raise HTTPException(status_code=404, detail=f"No .txt file found for digest {digest_id!r}") from exc
41+
raise HTTPException(
42+
status_code=HTTP_404_NOT_FOUND,
43+
detail=f"No .txt file found for digest {digest_id!r}",
44+
) from exc
4145

4246
try:
4347
return FileResponse(path=first_txt_file, media_type="text/plain", filename=first_txt_file.name)
4448
except PermissionError as exc:
45-
raise HTTPException(status_code=403, detail=f"Permission denied for {first_txt_file}") from exc
49+
raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail=f"Permission denied for {first_txt_file}") from exc

0 commit comments

Comments
 (0)