refactor(tailwind,build,ci,docker): drop local Tailwind build & Node toolchain (#373)
Some checks failed
OSSF Scorecard / Scorecard analysis (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.8) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.8) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.8) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled

* refactor(build,ci,docker): drop local Tailwind build & Node toolchain

* Remove npm ecosystem from Dependabot, workflows and Dockerfile.
* Delete package.json, tailwind.config.js and generated CSS sources.
* Replace compiled CSS with Tailwind CDN plus inline component layer.
* Simplify CI: eliminate frontend build job; Python-only release pipeline.
* Shrink Docker image to single Python stage.
* Trim .gitignore and CONTRIBUTING docs accordingly.

* fix max_file_size limit

* fix #375
This commit is contained in:
Filip Christiansen 2025-07-04 01:16:06 +02:00 committed by GitHub
parent 38c23171a1
commit 3e83ba3936
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 69 additions and 1745 deletions

View file

@ -10,16 +10,6 @@ updates:
dependency-type: "development"
update-types: ["minor", "patch"]
# ─── Node (npm) ───────────────────────────────
- package-ecosystem: "npm"
directory: "/"
schedule: { interval: "weekly" }
labels: [ "dependencies", "npm" ]
cooldown: # wait before opening PRs
semver-major-days: 30
semver-minor-days: 7
semver-patch-days: 3
# ─── GitHub Actions ───────────────────────────
- package-ecosystem: "github-actions"
directory: "/"

View file

@ -48,27 +48,3 @@ jobs:
- name: Run pre-commit hooks
uses: pre-commit/action@v3.0.1
if: ${{ matrix.python-version == '3.13' && matrix.os == 'ubuntu-latest' }}
frontend:
needs: test # Builds Tailwind CSS only if tests pass
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install Node deps
run: npm ci
- name: Build CSS
run: npm run build:css # Creates src/static/css/site.css
- name: Upload artefact
uses: actions/upload-artifact@v4
with:
name: static-css
path: src/static/css/site.css

View file

@ -8,47 +8,13 @@ on:
permissions:
contents: read
# ── Build the Tailwind CSS bundle ───────────────────────────────
jobs:
frontend-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- name: Install deps + build Tailwind
run: |
npm ci
npm run build:css
- name: Upload built CSS
uses: actions/upload-artifact@v4
with:
name: frontend-assets
path: src/static/css/site.css
if-no-files-found: error
# ── Build wheel/sdist (needs CSS) and upload “dist/” ────────────
release-build:
needs: frontend-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Grab site.css produced above
- uses: actions/download-artifact@v4
with:
name: frontend-assets
path: src/static/css/
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
@ -56,11 +22,10 @@ jobs:
cache: pip
cache-dependency-path: pyproject.toml
- name: Install build backend
- name: Build package
run: |
python -m pip install --upgrade pip
python -m pip install build twine
python -m build
twine check dist/*
- name: Upload dist artefact
uses: actions/upload-artifact@v4
@ -68,11 +33,11 @@ jobs:
name: dist
path: dist/
# ── Publish to PyPI (only if “dist/” succeeded) ─────────────────
# Publish to PyPI (only if “dist/” succeeded)
pypi-publish:
needs: release-build
runs-on: ubuntu-latest
environment: pypi # Creates the “pypi” environment in repo-settings
environment: pypi
permissions:
id-token: write # OIDC token for trusted publishing

View file

@ -6,9 +6,9 @@ on:
push:
branches: [ main ]
permissions: read-all # Default for the whole workflow
permissions: read-all
concurrency: # (optional) avoid overlapping runs
concurrency: # avoid overlapping runs
group: scorecard-${{ github.ref }}
cancel-in-progress: true

34
.gitignore vendored
View file

@ -84,31 +84,7 @@ target/
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
@ -158,21 +134,13 @@ dmypy.json
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# VSCode
.vscode/settings.json
.DS_Store
# JavaScript tooling
node_modules/
# CSS
src/static/css/site.css
# Project specific
history.txt
cleanup.py

View file

@ -17,7 +17,6 @@ If you ever get stuck, reach out on [Discord](https://discord.com/invite/zerRaGK
## How to submit a Pull Request
> **Prerequisites**: The project uses **Python 3.9+** and `pre-commit` for development.
> If you plan to touch the frontend, you'll also need **Node ≥18** (for Tailwind).
1. **Fork** the repository.
@ -63,22 +62,7 @@ If you ever get stuck, reach out on [Discord](https://discord.com/invite/zerRaGK
pre-commit run --all-files
```
9. **If you edited templates or CSS** rebuild Tailwind:
```bash
# one-time install
npm ci
# build once
npm run build:css
# or watch & rebuild on every save
npm run dev:css
```
*Skip this step if your PR only touches Python code.*
10. **Run the local server** to sanity-check:
9. **Run the local server** to sanity-check:
```bash
cd src
@ -87,30 +71,22 @@ If you ever get stuck, reach out on [Discord](https://discord.com/invite/zerRaGK
Open [http://localhost:8000](http://localhost:8000) to confirm everything works.
11. **Commit** (signed):
10. **Commit** (signed):
```bash
git commit -S -m "Your commit message"
```
If *pre-commit* complains, fix the problems and repeat **6 10**.
If *pre-commit* complains, fix the problems and repeat **5 9**.
12. **Push** your branch:
11. **Push** your branch:
```bash
git push origin your-branch
```
13. **Open a pull request** on GitHub with a clear description.
12. **Open a pull request** on GitHub with a clear description.
14. **Iterate** on any review feedback—update your branch and repeat **6 13** as needed.
13. **Iterate** on any review feedback—update your branch and repeat **6 11** as needed.
*(Optional) Invite a maintainer to your branch for easier collaboration.*
---
## CSS & build artefacts
- **Do not commit `src/static/css/site.css`.** The CI pipeline runs `npm run build:css` during the container/image build, so the artefact is produced automatically.
- When developing locally you may run the build yourself (see step 9) so you can preview the styles.

View file

@ -1,60 +1,42 @@
# ---------- Stage 1: Build CSS with Node -------------------------
FROM node:20-alpine AS css-builder
WORKDIR /frontend
# Copy only files that affect the CSS build to leverage Docker cache
COPY package*.json ./
RUN npm ci
# Tailwind source --> final CSS
# (adjust the paths if you store Tailwind input elsewhere)
COPY tailwind.config.js ./ # Tailwind config
COPY src/static/css/ ./src/static/css/ # Tailwind input file(s)
RUN npm run build:css # writes ./src/static/css/site.css
# ---------- Stage 2: Install Python dependencies -----------------
FROM python:3.12-slim AS python-builder
# Stage 1: Install Python dependencies
FROM python:3.13-slim AS python-builder
WORKDIR /build
# System build tools first (so later layers are cached if unchanged)
# System build tools
RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc python3-dev \
&& rm -rf /var/lib/apt/lists/*
# Python dependencies
# Metadata and code that setuptools needs
COPY pyproject.toml .
COPY src/ ./src/
# Install runtime dependencies defined in pyproject.toml
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir --timeout 1000 "."
&& pip install --no-cache-dir --timeout 1000 .
# ---------- Stage 3: Final runtime image -------------------------
FROM python:3.12-slim
# Stage 2: Runtime image
FROM python:3.13-slim
LABEL org.opencontainers.image.source="https://github.com/cyclotruc/gitingest"
# Minimal runtime utilities
RUN apt-get update \
&& apt-get install -y --no-install-recommends git curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
# Create non-root user (uid 1000 == common default on Linux host)
RUN useradd -m -u 1000 appuser
# ── Copy Python site-packages & app code ───────────────────────────
COPY --from=python-builder /usr/local/lib/python3.12/site-packages/ \
/usr/local/lib/python3.12/site-packages/
COPY src/ ./
# Copy Python site-packages and code
COPY --from=python-builder /usr/local/lib/python3.13/site-packages/ \
/usr/local/lib/python3.13/site-packages/
COPY src/ ./src/
# ── Copy the freshly-built CSS ────────────────────────────────────
COPY --from=css-builder /frontend/src/static/css/site.css \
src/static/css/site.css
# Fix permissions
# Set permissions
RUN chown -R appuser:appuser /app
USER appuser

1461
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,16 +0,0 @@
{
"private": true,
"name": "gitingest-frontend",
"version": "0.0.0",
"scripts": {
"build:css": "npx @tailwindcss/cli -i ./src/static/css/tailwind.css -o ./src/static/css/site.css --minify",
"dev:css": "npx @tailwindcss/cli -i ./src/static/css/tailwind.css -o ./src/static/css/site.css --watch"
},
"devDependencies": {
"autoprefixer": "^10.4.21",
"postcss": "^8.5.6",
"simple-icons": "^15.4.0",
"tailwindcss": "^4.1.11",
"@tailwindcss/cli": "^4.1.11"
}
}

View file

@ -57,7 +57,6 @@ build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = {find = {where = ["src"]}}
include-package-data = true
package-data = {"gitingest" = ["static/css/*.css"]}
# Linting configuration
[tool.pylint.format]

View file

@ -65,7 +65,7 @@ def ingest_query(query: IngestionQuery) -> tuple[str, str, str]:
msg = f"File {file_node.name} has no content"
raise ValueError(msg)
return format_node(file_node, query)
return format_node(file_node, query=query)
root_node = FileSystemNode(
name=path.name,
@ -76,13 +76,9 @@ def ingest_query(query: IngestionQuery) -> tuple[str, str, str]:
stats = FileSystemStats()
_process_node(
node=root_node,
query=query,
stats=stats,
)
_process_node(node=root_node, query=query, stats=stats)
return format_node(root_node, query)
return format_node(root_node, query=query)
def _process_node(node: FileSystemNode, query: IngestionQuery, stats: FileSystemStats) -> None:
@ -101,7 +97,7 @@ def _process_node(node: FileSystemNode, query: IngestionQuery, stats: FileSystem
Statistics tracking object for the total file count and size.
"""
if limit_exceeded(stats, node.depth):
if limit_exceeded(stats, depth=node.depth):
return
for sub_path in node.path.iterdir():
@ -114,6 +110,9 @@ def _process_node(node: FileSystemNode, query: IngestionQuery, stats: FileSystem
if sub_path.is_symlink():
_process_symlink(path=sub_path, parent_node=node, stats=stats, local_path=query.local_path)
elif sub_path.is_file():
if sub_path.stat().st_size > query.max_file_size:
print(f"Skipping file {sub_path}: would exceed max file size limit")
continue
_process_file(path=sub_path, parent_node=node, stats=stats, local_path=query.local_path)
elif sub_path.is_dir():
child_directory_node = FileSystemNode(
@ -124,11 +123,7 @@ def _process_node(node: FileSystemNode, query: IngestionQuery, stats: FileSystem
depth=node.depth + 1,
)
_process_node(
node=child_directory_node,
query=query,
stats=stats,
)
_process_node(node=child_directory_node, query=query, stats=stats)
if not child_directory_node.children:
continue
@ -190,6 +185,10 @@ def _process_file(path: Path, parent_node: FileSystemNode, stats: FileSystemStat
The base path of the repository or directory being processed.
"""
if stats.total_files + 1 > MAX_FILES:
print(f"Maximum file limit ({MAX_FILES}) reached")
return
file_size = path.stat().st_size
if stats.total_size + file_size > MAX_TOTAL_SIZE_BYTES:
print(f"Skipping file {path}: would exceed total size limit")
@ -198,10 +197,6 @@ def _process_file(path: Path, parent_node: FileSystemNode, stats: FileSystemStat
stats.total_files += 1
stats.total_size += file_size
if stats.total_files > MAX_FILES:
print(f"Maximum file limit ({MAX_FILES}) reached")
return
child = FileSystemNode(
name=path.name,
type=FileSystemNodeType.FILE,

View file

@ -29,7 +29,6 @@ class FileSystemNodeType(Enum):
class FileSystemStats:
"""Class for tracking statistics during file system traversal."""
visited: set[Path] = field(default_factory=set)
total_files: int = 0
total_size: int = 0

View file

@ -53,6 +53,8 @@ DEFAULT_IGNORE_PATTERNS: set[str] = {
"*.out",
"*.a",
"*.pdb",
# Binary
"*.bin",
# Swift/Xcode
".build/",
"*.xcodeproj/",
@ -147,6 +149,10 @@ DEFAULT_IGNORE_PATTERNS: set[str] = {
".docusaurus",
".next",
".nuxt",
# Database
"*.db",
"*.sqlite",
"*.sqlite3",
# Other common patterns
## Minified files
"*.min.js",

View file

@ -10,10 +10,7 @@ from gitingest.ingestion import ingest_query
from gitingest.query_parser import IngestionQuery, parse_query
from gitingest.utils.git_utils import validate_github_token
from server.models import IngestErrorResponse, IngestResponse, IngestSuccessResponse
from server.server_config import (
DEFAULT_MAX_FILE_SIZE_KB,
MAX_DISPLAY_SIZE,
)
from server.server_config import MAX_DISPLAY_SIZE
from server.server_utils import Colors, log_slider_to_size
@ -148,10 +145,11 @@ def _print_query(url: str, max_file_size: int, pattern_type: str, pattern: str)
The actual pattern string to include or exclude in the query.
"""
default_max_file_kb = 50
print(f"{Colors.WHITE}{url:<20}{Colors.END}", end="")
if int(max_file_size / 1024) != DEFAULT_MAX_FILE_SIZE_KB:
if int(max_file_size / 1024) != default_max_file_kb:
print(
f" | {Colors.YELLOW}Size: {int(max_file_size / 1024)}kb{Colors.END}",
f" | {Colors.YELLOW}Size: {int(max_file_size / 1024)}kB{Colors.END}",
end="",
)
if pattern_type == "include" and pattern != "":

View file

@ -7,8 +7,6 @@ from fastapi.templating import Jinja2Templates
MAX_DISPLAY_SIZE: int = 300_000
DELETE_REPO_AFTER: int = 60 * 60 # In seconds (1 hour)
DEFAULT_MAX_FILE_SIZE_KB: int = 50 # Default maximum file size to include in the digest
# Slider configuration (if updated, update the logSliderToSize function in src/static/js/utils.js)
MAX_FILE_SIZE_KB: int = 100 * 1024 # 100 MB
MAX_SLIDER_POSITION: int = 500 # Maximum slider position

View file

@ -50,9 +50,8 @@
{% endif %}
{% endblock %}
</title>
{# Style sheets #}
<link rel="preload" href="/static/css/site.css" as="style">
{% block css %}<link rel="stylesheet" href="/static/css/site.css">{% endblock %}
<script src="https://cdn.tailwindcss.com"></script>
{% include 'components/tailwind_components.html' %}
</head>
<body class="bg-[#FFFDF8] min-h-screen flex flex-col">
{% include 'components/navbar.jinja' %}

View file

@ -0,0 +1,15 @@
<style type="text/tailwindcss">
@layer components {
.badge-new { @apply inline-block -rotate-6 -translate-y-1 mx-1 px-1 bg-[#FE4A60] border border-gray-900 text-white text-[10px] font-bold shadow-[2px_2px_0_0_rgba(0,0,0,1)]; }
.landing-page-title { @apply inline-block w-full relative text-center text-4xl sm:text-5xl md:text-6xl lg:text-7xl sm:pt-20 lg:pt-5 font-bold tracking-tighter; }
.intro-text { @apply text-center text-gray-600 text-lg max-w-2xl mx-auto; }
.sparkle-red { @apply absolute flex-shrink-0 h-auto w-14 sm:w-20 md:w-24 p-2 left-0 lg:ml-32 -translate-x-2 md:translate-x-10 lg:-translate-x-full -translate-y-4 sm:-translate-y-8 md:-translate-y-0 lg:-translate-y-10; }
.sparkle-green { @apply absolute flex-shrink-0 right-0 bottom-0 w-10 sm:w-16 lg:w-20 -translate-x-10 lg:-translate-x-12 translate-y-4 sm:translate-y-10 md:translate-y-2 lg:translate-y-4; }
.pattern-select { @apply min-w-max appearance-none pr-6 pl-2 py-2 bg-[#e6e8eb] border-r-[3px] border-gray-900 cursor-pointer focus:outline-none; }
}
@layer utilities {
.no-drag { @apply pointer-events-none select-none; -webkit-user-drag: none; }
.link-bounce { @apply transition-transform hover:-translate-y-0.5; }
}
</style>

View file

@ -1,59 +0,0 @@
@import "tailwindcss";
@layer components {
.badge-new {
@apply inline-block -rotate-6 -translate-y-1 mx-1 px-1
bg-[#FE4A60] border border-gray-900 text-white
text-[10px] font-bold shadow-[2px_2px_0_0_rgba(0,0,0,1)];
}
.landing-page-title {
@apply inline-block w-full relative
text-center
text-4xl sm:text-5xl md:text-6xl lg:text-7xl
sm:pt-20 lg:pt-5
font-bold tracking-tighter;
}
.intro-text {
@apply text-center
text-gray-600 text-lg max-w-2xl mx-auto;
}
.sparkle-red {
@apply absolute flex-shrink-0 h-auto
w-14 sm:w-20 md:w-24 p-2
left-0 lg:ml-32
-translate-x-2 md:translate-x-10 lg:-translate-x-full
-translate-y-4 sm:-translate-y-8 md:-translate-y-0 lg:-translate-y-10;
}
.sparkle-green {
@apply absolute flex-shrink-0
right-0 bottom-0
w-10 sm:w-16 lg:w-20
-translate-x-10 lg:-translate-x-12
translate-y-4 sm:translate-y-10 md:translate-y-2 lg:translate-y-4;
}
.pattern-select {
@apply min-w-max appearance-none pr-6 pl-2 py-2
bg-[#e6e8eb] border-r-[3px] border-gray-900
cursor-pointer focus:outline-none;
}
}
@layer utilities {
.no-drag {
@apply pointer-events-none select-none;
-webkit-user-drag: none;
}
.link-bounce {
@apply transition-transform
hover:-translate-y-0.5;
}
}

View file

@ -1,6 +0,0 @@
module.exports = {
content: [
"./src/**/*.jinja",
"./src/**/*.js",
],
};