Skip to content

Commit b11aafa

Browse files
committed
Merge branch 'main' into improve-env
2 parents 3bc4ed7 + 8445050 commit b11aafa

File tree

12 files changed

+84
-97
lines changed

12 files changed

+84
-97
lines changed

.github/workflows/docker_image.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Build Docker Image
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
workflow_dispatch:
7+
8+
jobs:
9+
docker:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout code
13+
uses: actions/checkout@v4
14+
15+
- name: Set up Docker Buildx
16+
uses: docker/setup-buildx-action@v3
17+
18+
- name: Build
19+
uses: docker/build-push-action@v6
20+
with:
21+
push: false
22+
context: .
23+
file: Dockerfile
24+
tags: "${{ github.sha }}"

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ requires-python = ">= 3.8"
77
dependencies = [
88
"click>=8.0.0",
99
"fastapi[standard]>=0.109.1", # Minimum safe release (https://osv.dev/vulnerability/PYSEC-2024-38)
10+
"httpx",
11+
"pathspec>=0.12.1",
1012
"pydantic",
1113
"python-dotenv",
1214
"slowapi",
1315
"starlette>=0.40.0", # Minimum safe release (https://osv.dev/vulnerability/GHSA-f96h-pmfr-66vw)
1416
"tiktoken>=0.7.0", # Support for o200k_base encoding
15-
"pathspec>=0.12.1",
1617
"typing_extensions>= 4.0.0; python_version < '3.10'",
1718
"uvicorn>=0.11.7", # Minimum safe release (https://osv.dev/vulnerability/PYSEC-2020-150)
1819
]
@@ -44,7 +45,7 @@ dev = [
4445
]
4546

4647
[project.scripts]
47-
gitingest = "gitingest.cli:main"
48+
gitingest = "gitingest.__main__:main"
4849

4950
[project.urls]
5051
homepage = "https://gitingest.com"

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
click>=8.0.0
22
fastapi[standard]>=0.109.1 # Vulnerable to https://osv.dev/vulnerability/PYSEC-2024-38
3+
httpx
34
pathspec>=0.12.1
45
pydantic
56
python-dotenv

src/gitingest/utils/git_utils.py

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,17 @@
44

55
import asyncio
66
import base64
7-
import os
87
import re
8+
import sys
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-
)
12+
import httpx
13+
from starlette.status import HTTP_200_OK, HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND
2014

2115
from gitingest.utils.compat_func import removesuffix
2216
from 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]:
8176
async 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

97108
async 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

src/server/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,17 @@ async def robots() -> FileResponse:
8585
return FileResponse("static/robots.txt")
8686

8787

88-
@app.get("/llm.txt")
88+
@app.get("/llms.txt")
8989
async def llm_txt() -> FileResponse:
90-
"""Serve the ``llm.txt`` file to provide information about the site to LLMs.
90+
"""Serve the ``llms.txt`` file to provide information about the site to LLMs.
9191
9292
Returns
9393
-------
9494
FileResponse
95-
The ``llm.txt`` file located in the static directory.
95+
The ``llms.txt`` file located in the static directory.
9696
9797
"""
98-
return FileResponse("static/llm.txt")
98+
return FileResponse("static/llms.txt")
9999

100100

101101
# Include routers for modular endpoints

src/server/templates/components/navbar.jinja

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
</div>
1212
{# Navigation with updated styling #}
1313
<nav class="flex items-center space-x-6">
14-
<a href="/llm.txt" class="link-bounce flex items-center text-gray-900">
14+
<a href="/llms.txt" class="link-bounce flex items-center text-gray-900">
1515
<span class="badge-new">NEW</span>
16-
/llm.txt
16+
/llms.txt
1717
</a>
1818
{# GitHub link #}
1919
<div class="flex items-center gap-2">
File renamed without changes.

tests/test_cli.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pytest
99
from click.testing import CliRunner, Result
1010

11-
from gitingest.cli import main
11+
from gitingest.__main__ import main
1212
from gitingest.config import MAX_FILE_SIZE, OUTPUT_FILE_NAME
1313

1414

@@ -72,8 +72,8 @@ def test_cli_with_stdout_output() -> None:
7272
# ─── core expectations (stdout) ────────────────────────────────────-
7373
assert result.exit_code == 0, f"CLI exited with code {result.exit_code}, stderr: {result.stderr}"
7474
assert "---" in result.stdout, "Expected file separator '---' not found in STDOUT"
75-
assert "src/gitingest/cli.py" in result.stdout, (
76-
"Expected content (e.g., src/gitingest/cli.py) not found in STDOUT"
75+
assert "src/gitingest/__main__.py" in result.stdout, (
76+
"Expected content (e.g., src/gitingest/__main__.py) not found in STDOUT"
7777
)
7878
assert not output_file.exists(), f"Output file {output_file} was unexpectedly created."
7979

0 commit comments

Comments
 (0)