Metadata-Version: 2.4
Name: git-activity-merge
Version: 0.1.0
Summary: FastAPI service that merges GitHub and Gitea contribution heatmaps and renders embeddable images.
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: fastapi>=0.115.0
Requires-Dist: uvicorn[standard]>=0.30.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic-settings>=2.4.0
Requires-Dist: cairosvg>=2.7.1
Provides-Extra: dev
Requires-Dist: pytest>=8.3.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
Requires-Dist: anyio>=4.4.0; extra == "dev"

# git-activity-merge

Small self-hosted FastAPI service that merges GitHub + Gitea contribution data and returns embeddable contribution heatmap images.

Example for GitHub profile README:

```md
![Activity](https://example.com/activity.svg)
```

## What It Does

- Fetches contribution data from:
  - GitHub GraphQL API (`GITHUB_USERNAME`, optional `GITHUB_TOKEN`)
  - Gitea heatmap API (`{GITEA_BASE_URL}/api/v1/users/{GITEA_USERNAME}/heatmap`)
- Normalizes into date-based counts:
  - `github`
  - `gitea`
  - `total`
- Caches source responses and rendered images on disk.
- Returns:
  - merged JSON
  - GitHub-style SVG heatmap
  - PNG heatmap

## API Routes

- `GET /health`
- `GET /activity.json`
- `GET /activity.svg`
- `GET /activity.png`

Supported query params:

- `year=YYYY` or `days=365` (mutually exclusive)
- `theme=dark|light`
- `source=all|github|gitea`

Examples:

```bash
curl "http://localhost:8000/activity.json?days=365&source=all"
curl "http://localhost:8000/activity.svg?year=2026&theme=dark"
curl "http://localhost:8000/activity.png?days=180&source=gitea"
```

## Setup

### 1. Install

```bash
python -m venv .venv
. .venv/bin/activate  # or .venv\Scripts\activate on Windows
pip install -e ".[dev]"
```

### 2. Configure

Copy `.env.example` to `.env` and edit values.

Required:

- `GITHUB_USERNAME`
- `GITEA_BASE_URL`
- `GITEA_USERNAME`

Optional:

- `GITHUB_TOKEN`
- `GITEA_TOKEN`
- `CACHE_TTL_SECONDS` (default: `3600`)
- `CACHE_DIR` (default: `./cache` locally, `/app/cache` in container)
- `DEFAULT_THEME` (`light` or `dark`, default: `light`)
- `SERVICE_TITLE` (default: `git-activity-merge`)

### 3. Run

```bash
uvicorn app.main:app --reload --port 8000
```

## Docker

```bash
docker compose up --build
```

Container serves on port `8000` and mounts a named cache volume at `/app/cache`.

## Reverse Proxy Example (Nginx)

```nginx
server {
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

Then embed directly:

```md
![Activity](https://example.com/activity.svg?days=365&theme=dark)
```

## Notes

- GitHub GraphQL without token is possible but lower-rate/less reliable. Set `GITHUB_TOKEN` for stability.
- Gitea instances may require `GITEA_TOKEN` for private activity or stricter rate limits.
- If upstream APIs fail and stale cache exists, `/activity.json` includes `"stale": true` and stale data is served.
