3.4 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Development
# 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
# 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<string, int> (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.