From c1fde0aed34bc8b71bb4b9c6624214a03c1ebb78 Mon Sep 17 00:00:00 2001 From: Haylan Date: Thu, 28 May 2026 20:15:12 +0200 Subject: [PATCH] Update environment configuration to development mode and add CLAUDE.md for project guidance --- .env | 2 +- CLAUDE.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md diff --git a/.env b/.env index 094a439..8a9238b 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -APP_ENV=prod +APP_ENV=dev APP_DEBUG=0 APP_SECRET=changeme_replace_with_random_32char_string diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..046ee28 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,73 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development + +```bash +# Install dependencies (generates composer.lock if missing — commit it) +composer install + +# Run dev server +APP_ENV=dev php -S localhost:8080 -t public + +# Test the graph endpoint +curl "http://localhost:8080/graph.svg?theme=dark" -o graph.svg +curl "http://localhost:8080/health" +``` + +## Docker + +```bash +# Build and run +docker compose up -d --build + +# Force cache clear (clears the 1h filesystem cache) +docker compose exec graph rm -rf var/cache/* + +# View logs +docker compose logs -f graph +``` + +There is no `composer.lock` in the repo. If you add or change dependencies, run `composer install` locally and commit the resulting lock file — without it, Docker builds resolve versions fresh each time and may produce inconsistent results. + +The Dockerfile runs `composer install --no-scripts` (skipping Symfony post-install scripts) then `composer dump-autoload --optimize --no-dev` in the final stage. If `bin/console` fails with a missing class error inside the container, the most likely cause is the absent lock file causing an incomplete dependency resolution. + +## Architecture + +Single-controller Symfony app with no database. The request flow: + +``` +GET /graph.svg?theme=dark|light + └─ GraphController + ├─ host check (ALLOWED_HOSTS env, optional) + ├─ cache lookup (filesystem, 1h TTL, key = "graph_{theme}") + │ └─ on miss: + │ ├─ GitHubProvider → GitHub GraphQL API (contributionCalendar query) + │ ├─ GitLabProvider → GitLab REST /users/:id/events (paginated, 100/page) + │ └─ GiteaProvider → Gitea REST /api/v1/users/:user/heatmap + │ each returns array (Y-m-d => count) + │ failures are caught and logged; remaining providers still render + │ └─ merge by date (sum counts across providers) + │ └─ SvgRenderer::render() + └─ Response: image/svg+xml, Cache-Control: public max-age=3600 +``` + +**Provider activation:** a provider only runs when its env vars are non-empty. GitHub and GitLab require `_USER` + `_TOKEN`; Gitea additionally requires `_URL`. GitLab resolves a numeric user ID from the username via a `/api/v4/users?username=` lookup before fetching events. + +**SvgRenderer:** builds a 53-column × 7-row grid aligned so the last column always ends on the Saturday of the current week. Five intensity levels (0 contributions → level 0, 1–3 → 1, 4–6 → 2, 7–9 → 3, 10+ → 4) mapped to GitHub's exact colour tokens. No external assets — the SVG is fully self-contained. + +**Cache:** filesystem adapter (`var/cache/`), mounted as a Docker volume to survive container restarts. Theme is part of the cache key so dark and light are cached independently. + +## Environment variables + +| Variable | Required | Notes | +|---|---|---| +| `APP_SECRET` | Yes | 32+ char random string | +| `GITHUB_USER` / `GITHUB_TOKEN` | For GitHub | Token scope: `read:user` | +| `GITLAB_USER` / `GITLAB_TOKEN` | For GitLab | Token scopes: `read_user`, `read_api` | +| `GITLAB_URL` | No | Defaults to `https://gitlab.com` | +| `GITEA_USER` / `GITEA_TOKEN` / `GITEA_URL` | For Gitea | Token scope: `read:user` | +| `ALLOWED_HOSTS` | No | Comma-separated hostnames; empty = allow all | + +Copy `.env` to `.env.local` for local development — `.env.local` is gitignored.