Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
.git
.github
.nx
node_modules
**/node_modules
.next
coverage
dist
**/dist
.pnpm-store
*.log
logs.txt
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env
.env.*
Dockerfile
**/*.tsbuildinfo
!.env.example
*.tsbuildinfo
tmp
temp
pnpm-lock.yaml.prev
ops/deploy
45 changes: 28 additions & 17 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
# ── DevImpact Global Environment Configuration ──────────────────────────────
# Copy this file to .env before starting the web application or background worker:
# cp .env.example .env

# ── GitHub API Configuration ────────────────────────────────────────────────
# GitHub Personal Access Token (PAT) for GraphQL / REST API requests.
# Required scope: public_repo (or read-only fine-grained token).
GITHUB_TOKEN=your_github_token_here
# Public GitHub repository URL shown by the app.
# If omitted, the app falls back to https://github.com/O2sa/DevImpact
NEXT_PUBLIC_GITHUB_REPO_URL=your_github_repo_url_here

# Public GitHub repository URL shown by the web application.
NEXT_PUBLIC_GITHUB_REPO_URL=https://github.com/O2sa/DevImpact

# GitHub query limits
# GitHub query fetch limits per user
GITHUB_REPO_COUNT=30
GITHUB_PR_COUNT=80
GITHUB_ISSUE_COUNT=20
GITHUB_DISCUSSION_COUNT=10
GITHUB_USER_STALE_DAYS=14

# Leaderboard source data
LEADERBOARD_SOURCE_URL_TEMPLATE=https://raw.githubusercontent.com/ashkulz/committers.top/gh-pages/_data/locations/{country}.yml
DISABLE_CALCULATE_LEADERBOARD_ENDPOINT=false
# ── Database (PostgreSQL) ───────────────────────────────────────────────────
DATABASE_URL=postgresql://devimpact:devimpact@localhost:5432/devimpact?sslmode=disable
POSTGRES_PASSWORD=CHANGE_THIS_TO_A_LONG_RANDOM_PASSWORD

# Redis caching (optional — strongly recommended for leaderboard performance)
# Use either redis://localhost:6379 or include password if enabled: redis://:password@localhost:6379
REDIS_URL=
# ── Caching (Redis) ─────────────────────────────────────────────────────────
REDIS_ENABLED=false
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=
# CACHE_NAMESPACE is also accepted as an alias.
# CACHE_NAMESPACE=devimpact:v1
REDIS_CACHE_NAMESPACE=devimpact:v1
# CACHE_TTL_SECONDS is also accepted as an alias. Valid range: 1-31536000.
# CACHE_TTL_SECONDS=604800
REDIS_CACHE_TTL_SECONDS=604800
REDIS_CONNECT_TIMEOUT_MS=1500

# ── PostgreSQL (local development) ─────────────────
DATABASE_URL=postgresql://devimpact:devimpact@localhost:5432/devimpact?sslmode=disable
POSTGRES_PASSWORD=CHANGE_THIS_TO_A_LONG_RANDOM_PASSWORD
# ── Leaderboard Data & Worker Options ───────────────────────────────────────
# Cron schedule for background calculation worker (standard 5-field cron syntax)
# Default: "0 0 * * *" (runs once per day at midnight)
LEADERBOARD_CRON_SCHEDULE=0 0 * * *

# Data source template for seed country profiles
LEADERBOARD_SOURCE_URL_TEMPLATE=https://raw.githubusercontent.com/ashkulz/committers.top/gh-pages/_data/locations/{country}.yml

LEADERBOARD_SEED_LIMIT=256
LEADERBOARD_REFRESH_LIMIT=500
LEADERBOARD_USER_STALE_DAYS=30
DISABLE_CALCULATE_LEADERBOARD_ENDPOINT=false
38 changes: 38 additions & 0 deletions .github/workflows/deploy-vps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Deploy Leaderboard Worker to VPS

on:
workflow_run:
workflows: ["Leaderboard Worker GHCR Image"]
types:
- completed
branches:
- main
workflow_dispatch:

permissions:
contents: read

jobs:
deploy:
name: Deploy to VPS
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
timeout-minutes: 10
env:
VPS_HOST: ${{ secrets.VPS_HOST }}
VPS_USER: ${{ secrets.VPS_USER }}
VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
VPS_SSH_PORT: ${{ secrets.VPS_SSH_PORT || '22' }}

steps:
- name: Deploy via SSH
if: ${{ env.VPS_HOST != '' && env.VPS_USER != '' && env.VPS_SSH_KEY != '' }}
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.VPS_HOST }}
username: ${{ env.VPS_USER }}
key: ${{ env.VPS_SSH_KEY }}
port: ${{ env.VPS_SSH_PORT }}
script: |
cd ~/projects/DevImpact || cd /app || exit 1
bash ops/deploy/deploy-leaderboard.sh
71 changes: 71 additions & 0 deletions .github/workflows/leaderboard-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Leaderboard Worker GHCR Image

on:
push:
branches:
- main
paths:
- "ops/docker/**"
- "ops/cron/**"
- "package.json"
- "pnpm-lock.yaml"
- "scripts/**"
- "lib/**"
- "app/**"
- "types/**"
- ".github/workflows/leaderboard-image.yml"
workflow_dispatch:

permissions:
contents: read
packages: write

concurrency:
group: leaderboard-image-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-push:
name: Build & Push GHCR Image
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/o2sa/devimpact-leaderboard
tags: |
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
type=sha,prefix=,format=long
type=ref,event=branch

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ops/docker/Dockerfile.worker
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
72 changes: 37 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,57 +161,59 @@ Final Score =

## 🚀 Getting Started

### 1. Clone the repo
### ⚡ Option A: Run Full Platform via Docker (Fastest)

```bash
git clone https://github.com/O2sa/DevImpact.git
cd DevImpact
```

---

### 2. Install dependencies
Run the entire platform (Web App UI + PostgreSQL + Redis + Worker) in 2 simple steps:

```bash
pnpm install
# 1. Copy environment template and set GITHUB_TOKEN
cp .env.example .env

# 2. Start full platform
docker compose -f ops/docker/docker-compose.yml up -d --build
```
Then open `http://localhost:3000` in your browser!

---

### 3. Set up environment variables
### 📦 Option B: Run Locally with Node.js & pnpm

Create a `.env` file:
1. **Install dependencies**:
```bash
pnpm install
```

```
GITHUB_TOKEN=your_github_token
NEXT_PUBLIC_GITHUB_REPO_URL=your_github_repo_url
GITHUB_REPO_COUNT=30
GITHUB_PR_COUNT=80
GITHUB_ISSUE_COUNT=20
GITHUB_DISCUSSION_COUNT=10
REDIS_URL=redis://localhost:6379
REDIS_ENABLED=false
REDIS_CACHE_NAMESPACE=devimpact:v1
REDIS_CACHE_TTL_SECONDS=604800
```
2. **Configure environment**:
```bash
cp .env.example .env
```

`CACHE_NAMESPACE` and `CACHE_TTL_SECONDS` are accepted as aliases for the
Redis-prefixed cache settings. The namespace must be non-empty. Cache TTL must
be a positive integer no greater than `31536000` seconds (one year); invalid or
missing values fall back to `devimpact:v1` and `604800` seconds (seven days).
3. **Start local database & Redis**:
```bash
pnpm db:up && pnpm redis:up
```

---
4. **Run development server**:
```bash
pnpm run dev
```

### 4. Run the app
5. **Calculate leaderboard scores manually**:
```bash
pnpm leaderboard:calculate
```

```bash
pnpm run dev
```
### Leaderboard Worker & Infrastructure

The leaderboard score updates run via a dedicated background worker container using Docker & Supercronic.

To calculate the next leaderboard country from the script, run:
For complete local setup, Docker Compose instructions, GHCR publishing, and VPS deployment documentation, see **[ops/README.md](file:///c:/Users/msii/Documents/DevImpact/ops/README.md)**.

```bash
pnpm run calculate-next-country
# Quick worker setup (pulls & runs published image)
cp .env.example .env
docker compose -f ops/docker/leaderboard-compose.yml pull
docker compose -f ops/docker/leaderboard-compose.yml up -d
```

---
Expand Down
42 changes: 0 additions & 42 deletions app/api/calculate-leaderboard/route.ts

This file was deleted.

7 changes: 7 additions & 0 deletions lib/db-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ export class DatabaseStore {

async getNextCountryToCalculate(): Promise<{ slug: string; title: string } | null> {
const client = getPool();
// Auto-recover calculations that were interrupted (e.g. by container deploy/restart)
await client.query(
`UPDATE leaderboard_calculation
SET status = 'failed', error_message = 'Interrupted by process restart or timeout', updated_at = NOW()
WHERE status = 'running' AND updated_at < NOW() - INTERVAL '6 hours'`,
);

const result = await client.query(
`SELECT country_slug, country_title FROM leaderboard_calculation
WHERE status != 'running'
Expand Down
Loading
Loading