git-contribution-graph

A self-hosted Symfony service that merges contribution data from GitHub, GitLab and Gitea into a single GitHub-style heatmap SVG you can embed anywhere.

https://your-host/graph.svg?github_user=you&github_token=ghp_…&gitea_user=you&gitea_token=…&gitea_url=https://git.example.com

Example graph


Features

  • Three platforms — GitHub, GitLab (cloud or self-hosted), Gitea (self-hosted)
  • Merged heatmap — contributions from all sources are summed per day
  • GitHub colour palette — exact dark/light theme tokens
  • Embeddable — returns image/svg+xml, works in any <img> tag or Markdown
  • Cached — responses cached for 1 hour, safe to embed in public READMEs
  • Graceful degradation — if one platform fails, the others still render

Deploy

Requirements

  • Docker + Docker Compose

1. Clone and configure

git clone https://git.arthurerlich.de/haylan/git-contribution-graph.git
cd git-contribution-graph
cp .env .env.local

Edit .env.local:

APP_ENV=prod
APP_DEBUG=0
APP_SECRET=<generate with: openssl rand -hex 16>

2. Start

docker compose up -d

The service listens on port 8080 by default. Put Traefik or nginx in front of it for HTTPS.

3. Health check

GET /health  →  {"status":"ok"}

API

GET /graph.svg

Parameter Required Description
github_user GitHub username
github_token GitHub PAT — scope: read:user
gitlab_user GitLab username
gitlab_token GitLab PAT — scope: read_user, read_api
gitlab_url GitLab base URL (default: https://gitlab.com)
gitea_user Gitea username
gitea_token Gitea PAT — scope: read:user
gitea_url Gitea instance URL, e.g. https://git.example.com
theme dark (default) or light

All platform parameters are optional — include only the ones you use. At least one platform must be configured for a non-empty graph.


Embedding in a README

GitHub-only

![Contribution Graph](https://your-host/graph.svg?github_user=YOUR_USER&github_token=YOUR_TOKEN)

GitHub + Gitea

![Contribution Graph](https://your-host/graph.svg?github_user=YOUR_GH_USER&github_token=GH_TOKEN&gitea_user=YOUR_GITEA_USER&gitea_token=GITEA_TOKEN&gitea_url=https://git.arthurerlich.de)

All three platforms

![Contribution Graph](https://your-host/graph.svg?github_user=GH_USER&github_token=GH_TOKEN&gitlab_user=GL_USER&gitlab_token=GL_TOKEN&gitlab_url=https://gitlab.com&gitea_user=GITEA_USER&gitea_token=GITEA_TOKEN&gitea_url=https://git.arthurerlich.de)

Dark vs Light theme

<!-- Dark (default) -->
![Contribution Graph](https://your-host/graph.svg?github_user=USER&github_token=TOKEN&theme=dark)

<!-- Light -->
![Contribution Graph](https://your-host/graph.svg?github_user=USER&github_token=TOKEN&theme=light)

Security note: API tokens embedded in public Markdown URLs are visible to anyone. Use read-only fine-grained tokens with minimal scopes. For private repos or sensitive setups, proxy the request server-side and keep tokens in environment variables.


Token setup

GitHub

  1. Go to Settings → Developer settings → Personal access tokens → Fine-grained tokens
  2. Set Resource owner to your account
  3. Under Permissions → Account permissions, set Contribution activity → Read-only
  4. Generate and copy the token

GitLab

  1. Go to User Settings → Access Tokens
  2. Add a token with scopes: read_user, read_api
  3. Generate and copy the token

Gitea

  1. Go to Settings → Applications → Manage Access Tokens
  2. Add a token with permission: user → Read
  3. Generate and copy the token

Development

# Install deps
composer install

# Run dev server
APP_ENV=dev php -S localhost:8080 -t public

# Test endpoint
curl "http://localhost:8080/graph.svg?gitea_user=haylan&gitea_token=YOUR_TOKEN&gitea_url=https://git.arthurerlich.de" -o graph.svg

Architecture

GET /graph.svg
      │
      ├─ GitHubProvider  →  GitHub GraphQL API (contributionCalendar)
      ├─ GitLabProvider  →  GitLab REST API    (/users/:id/events)
      └─ GiteaProvider   →  Gitea REST API     (/users/:user/heatmap)
                │
          merge by date (sum counts)
                │
          SvgRenderer
                │
          image/svg+xml  (cached 1h)

License

MIT

S
Description
No description provided
Readme 269 KiB
Languages
PHP 98.2%
Dockerfile 1.8%