A self-hosted dashboard that tracks code coverage, cyclomatic complexity, and code duplication across your GitHub repositories — with trend charts, per-PR diff checks, and README badges.
Runs entirely on your own Cloudflare account (Worker + D1). Your data stays in your own database. No SaaS, no subscriptions, no third-party access to your metrics.
Catppuccin |
Gruvbox |
Nord |
Solarized |
Dracula |
Tokyo Night |
Note
The button deploys the Worker and provisions the D1 database automatically. You still need to complete the GitHub App, Cloudflare Zero Trust, and secrets setup described in docs/INSTALLATION.md.
- Install the GitHub App on the repos you want to track. The Worker registers them automatically via webhook.
- Add a workflow step to your CI that runs the reporting Action after your test suite. It collects coverage/complexity/duplication numbers and pushes them to the Worker using a GitHub Actions OIDC token — no static secrets.
- View trends in the dashboard (served as static assets by the Worker), protected by Cloudflare Access so only you can see it.
- Optionally embed a badge in your README.
┌─────────────────────────────────────────────────────────┐
│ Your CI (GitHub Actions) │
│ │
│ run tests → collect metrics → POST /api/ci/coverage │
│ (OIDC token, no static secret) │
└───────────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Cloudflare Worker (coverage-tracker.yourdomain.com) │
│ │
│ POST /api/ci/coverage ← OIDC-verified, project-scoped │
│ GET /api/projects/* ← Cloudflare Access JWT │
│ GET /api/badge/* ← public (per-project opt-in) │
│ POST /api/webhooks/* ← GitHub HMAC │
│ POST /api/admin/* ← Cloudflare Access JWT │
│ GET /api/health ← public │
│ * ← dashboard SPA (static assets) │
└───────────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Cloudflare D1 (your SQLite database) │
│ owners · projects · coverage_runs · coverage_daily │
└─────────────────────────────────────────────────────────┘
- No static ingest secret — CI authenticates with a GitHub Actions OIDC token. The Worker verifies the signature and checks the
repositoryclaim against your registered projects, so only your registered repos can push data. - Private by default — all data routes are behind Cloudflare Access. Badge numbers are opt-in per repo.
- Append-only metrics — trend history is never modified. PR jobs read baselines but never write.
- Multi-repo — one instance tracks all the repos you install the GitHub App on.
- Idempotent ingest — re-running CI for the same commit is a safe no-op.
- shields.io badge format — drop a badge into any README with a one-liner.
See docs/INSTALLATION.md for the full setup guide. The short version:
- Add your domain to Cloudflare (DNS must be proxied through Cloudflare)
- Create the D1 database and apply the migration
- Create a GitHub App (for webhooks + API access)
- Create a GitHub OAuth App (for Cloudflare Access login)
- Configure Cloudflare Zero Trust and deploy the Worker (the dashboard SPA deploys with it automatically)
- Install the GitHub App on your repos
Once a repo has metrics ingested and badge_enabled is turned on, add this to your README:
The endpoint returns shields.io endpoint format, so you can use the shields.io URL builder to customise the label and style:
Available metric names: coverage, complexity, duplication.
To enable the badge for a project:
# Find the project ID
npx wrangler d1 execute DB --remote --command "SELECT id, full_slug FROM projects"
# Enable badge
curl -X PATCH https://coverage-tracker.yourdomain.com/api/admin/projects/1/badge \
-H "Cf-Access-Jwt-Assertion: <your-access-token>" \
-H "Content-Type: application/json" \
-d '{"enabled": true}'.
├── migrations/ # D1 schema migrations (0001 owners/projects, 0002 coverage_runs/daily)
├── src/
│ ├── index.ts # Hono app entry point + scheduled cron handler
│ ├── types.ts # Bindings and shared types
│ ├── middleware/
│ │ ├── access.ts # Cloudflare Access JWT verification
│ │ ├── oidc.ts # GitHub Actions OIDC JWT verification
│ │ └── webhook.ts # GitHub webhook HMAC + replay protection
│ ├── lib/
│ │ ├── db.ts # D1 query helpers
│ │ ├── github.ts # GitHub App token minting
│ │ ├── metrics.ts # Metric name → column mapping
│ │ └── resync.ts # Installation reconciliation
│ ├── db/
│ │ └── rollup.ts # Daily coverage_runs → coverage_daily rollup + prune
│ └── routes/
│ ├── ci.ts # POST /api/ci/coverage
│ ├── baseline.ts # GET /api/baseline/:owner/:repo
│ ├── api.ts # GET /api/projects/*
│ ├── badge.ts # GET /api/badge/*
│ ├── webhooks.ts # POST /api/webhooks/github
│ └── admin.ts # POST /api/admin/*
├── dashboard/ # SvelteKit 5 source; builds to dashboard/build/ (served by Worker)
│ └── src/routes/ # Overview + per-repo drill-in views
├── .github/
│ ├── actions/report/ # Composite reporting Action (collect + ingest + Check Runs)
│ └── workflows/ # CI: action-test.yml, deploy.yml
├── scripts/
│ └── setup-waf-rules.mjs # WAF skip rule for /api/ci/coverage + /api/webhooks/github
├── test/
│ ├── seed-local.sql # Local D1 seed data
│ └── collect-parsers.sh # Parser fixture tests for collect.sh
├── docs/
│ ├── INSTALLATION.md # Setup guide
│ ├── PROGRESS.md # Phase implementation status
│ └── plans/ # Design documents
├── wrangler.example.jsonc # Config template — copy to wrangler.jsonc and fill in
└── .dev.vars.example # Local secrets template — copy to .dev.vars and fill in
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/api/ci/coverage |
OIDC token | Push typed coverage metrics from CI |
GET |
/api/projects |
Access | List all registered owners and repos |
GET |
/api/projects/:owner/:repo/metrics |
Access | Trend data for one repo |
GET |
/api/baseline/:owner/:repo |
OIDC token | Latest value on default branch (threshold checks) |
GET |
/api/badge/:owner/:repo/:metric.json |
Public | shields.io endpoint — only for badge_enabled repos |
POST |
/api/webhooks/github |
GitHub HMAC | GitHub App installation events |
POST |
/api/admin/resync |
Access | Reconcile projects table against GitHub |
PATCH |
/api/admin/projects/:id/badge |
Access | Toggle badge visibility |
GET |
/api/health |
Public | Liveness check |
{
"line_coverage": 82.4,
"branch_coverage": 79.1,
"cyclomatic": 4.2,
"cognitive": 2.1,
"duplication_pct": 1.8,
"maintainability": 95.0
}line_coverage is required; all other fields are optional. repository, branch, and commit_sha are derived from the OIDC token claims — they are not accepted in the body.
npm install
npm --prefix dashboard install # install dashboard dependencies
cp wrangler.example.jsonc wrangler.jsonc # fill in your D1 database ID
cp .dev.vars.example .dev.vars # fill in local secrets
npm run db:migrate:local # apply schema to local D1
npm run dev # start Worker + SPA (builds dashboard first)Type-check:
npm run typecheckDeploy:
npm run db:migrate:remote
npm run deploySee docs/PROGRESS.md for a full breakdown. Current state:
| Phase | Description | Status |
|---|---|---|
| 1–6 | Core Worker, webhooks, dashboard, reporting Action | Complete |
| — | Convergence refactor (single Worker + static assets) | Complete |
| 7 | "Deploy to Cloudflare" button | In progress |
| 8 | Docs, OSS hygiene, public release | In progress |
MIT — see LICENSE.