DevWrapped turns a GitHub username into an editorial-style developer activity dashboard. It collects GitHub contribution data through a Go worker, stores the completed audit in Redis, and renders the result as a shareable dashboard.
Status: early-stage personal project. The implementation is functional, but some analytics use bounded GitHub result windows. See
docs/METRICS.mdfor the exact definitions and limitations.
For a GitHub user, DevWrapped currently presents:
- Profile information, follower/following counts, and biography
- Total contributions, commits, pull requests created, pull requests reviewed, and issues
- Top language composition based on aggregated repository language bytes
- Current and longest contribution streaks
- Pull-request totals, state counts, merge rate, and merged-organization breakdown
- A client-side PNG export of the dashboard
The landing page describes the experience as an annual developer intelligence audit. The implementation is an asynchronous GitHub-data pipeline rather than a full historical analytics warehouse.
Browser
|
| POST /api/audit
v
Next.js API
|
| acquire lock + LPUSH
v
Redis queue: queue:github-audit
|
| BLMove
v
Go audit engine
|
| GitHub GraphQL
v
GitHub API
|
| process metrics
v
Redis stats: stats:<username>
|
| GET /api/audit/status/<username>
v
Browser
|
| navigate after COMPLETED
v
/<username>
|
| read cached stats
v
Editorial dashboard
The Go engine is a separate process from the Next.js server. Redis is both the job queue and the shared store for completed audit results and pipeline state.
- Next.js 16 App Router
- React 19 + TypeScript
- Go 1.25 worker engine using
shurcooL/githubv4 - Redis 7 via
ioredis(Next.js) andgo-redis/v9(Go) - Tailwind CSS 4
html-to-imagefor dashboard PNG export- Docker / Docker Compose
The Next.js application uses standalone output for its production Docker image. The Go engine has its own Dockerfile under go-engine/.
You need:
- Node.js and npm
- Go 1.25 if running the worker directly
- Redis, either locally or through Docker
- A GitHub token with the access required by the GraphQL queries
The Next.js process requires a reachable Redis instance for the audit endpoints:
REDIS_URL=redis://localhost:6379The Go engine uses:
GITHUB_TOKEN=your_github_token
REDIS_URL=redis://localhost:6379
QUEUE_NAME=queue:github-audit
WORKERS=5GITHUB_TOKEN is required by the Go GitHub client. The root Compose file also passes the token to the web container, but GitHub authentication for the audit fetch itself happens in the Go engine. REDIS_URL defaults to redis://localhost:6379 in both application layers when omitted. QUEUE_NAME defaults to queue:github-audit, and WORKERS defaults to 5 in the Go engine.
Never expose GITHUB_TOKEN through client-side variables such as NEXT_PUBLIC_*.
This is the simplest way to run the same three-process architecture locally:
docker compose up --buildCompose starts:
| Service | Container | Purpose |
|---|---|---|
web |
devwrap-web |
Next.js production server on port 3000 |
engine |
devwrap-engine |
Go audit worker consuming queue:github-audit |
redis |
devwrap-redis |
Redis 7 queue/cache on port 6379 |
The two application containers connect to Redis through the Compose hostname redis.
Open http://localhost:3000 after the stack starts.
Start Redis:
docker run --name devwrap-redis -p 6379:6379 -d redis:7-alpineThen install dependencies and run the Next.js application:
npm ci
npm run devIf you use this mode, the Go engine is not running unless you start it separately. A complete audit requires the worker to be running and connected to the same Redis instance.
From go-engine/:
go mod download
go run .The engine starts a worker pool and waits for jobs on queue:github-audit.
- Enter a GitHub username on
/. - The browser sends
POST /api/audit. - The Next.js server normalizes and validates the username, acquires a Redis lock, and enqueues an audit job.
- The Go worker consumes the job from Redis and queries GitHub GraphQL.
- The worker calculates the dashboard metrics and stores them under
stats:<username>for 24 hours. - The browser polls
/api/audit/status/<username>with adaptive backoff. - When the status becomes
COMPLETED, the browser navigates to/<username>. - The user page reads the completed stats from Redis and renders the dashboard server-side.
A failed audit gets a temporary failed:audit:<username> marker so the browser can stop polling instead of waiting for the lock to expire.
| Method | Route | Purpose |
|---|---|---|
GET |
/ |
Landing page and audit controls |
GET |
/<username> |
Render a completed dashboard or an audit-state fallback |
POST |
/api/audit |
Enqueue an asynchronous audit job |
GET |
/api/audit/status/<username> |
Return the current audit status and completed data |
GET |
/api/wrapped/<username> |
Read completed audit data through a direct JSON endpoint |
See docs/API.md for request/response contracts and status behavior.
The audit pipeline uses a small, shared Redis key scheme:
| Key | Purpose | TTL |
|---|---|---|
lock:audit:<username> |
Prevent duplicate audits for the same username | 120 seconds |
queue:github-audit |
Pending audit jobs | Persistent list |
queue:github-audit:processing |
Jobs currently being executed | Persistent list |
stats:<username> |
Completed DevWrappedStats payload |
24 hours |
failed:audit:<username> |
Terminal failure marker | 10 minutes |
The Go worker moves jobs atomically from the queue into the processing list. On startup it reclaims jobs left there by a previous crash or redeploy, providing at-least-once job handling.
The exact formulas are documented in docs/METRICS.md.
Important implementation limits include:
- Up to 50 owned, non-fork repositories are considered for language composition.
- Up to 10 language entries are returned per selected repository before aggregation.
- Pull requests are fetched in pages of 100 until GitHub reports no further pages.
- GitHub aggregate contribution counters are used for overview totals and are not derived from the repository or pull-request node windows.
Language composition is therefore based on the repository/language sample returned by the current GraphQL query, not necessarily every repository or language associated with a user's account.
Redis is a required part of the audit pipeline: /api/audit needs it to acquire the distributed lock and enqueue jobs, and the Go engine needs it to consume jobs and write results.
The dashboard read path is more defensive. A missing, malformed, or unreadable stats payload is treated as unavailable, allowing the user page to show an audit-incomplete or processing state instead of crashing.
Completed audit results expire after 24 hours. Running the same username after expiry creates a fresh audit.
The repository has two independent Docker images:
- Root
Dockerfile: builds and runs the Next.js web application. go-engine/Dockerfile: builds and runs the Go audit worker.
docker-compose.yml combines both images with Redis into the complete application stack. The root Dockerfile by itself does not start Redis or the Go worker.
For a Compose deployment, use the Compose file rather than treating the root Dockerfile as the complete stack.
Before submitting a change:
npm run lint
npm run build
docker compose buildFor Go changes, also run:
cd go-engine
go test ./...
go build ./...For API changes, verify at least one valid username, one invalid username, and the processing/completed audit paths.
See:
docs/ARCHITECTURE.md— system design and request/worker flowdocs/DEVELOPMENT.md— local development and contribution workflowdocs/API.md— HTTP contracts and audit state machinedocs/METRICS.md— metric definitions and limitationsdocs/DEPLOYMENT.md— Docker and deployment topology
No license file is currently present in the repository, so the project should be treated as all rights reserved unless the repository owner adds a license.