Skip to content

Commit 2efa98a

Browse files
authored
feat: add dotenv support
add dotenv support ( `.env` file with environmental variables at startup). incidentally also what today's Gen AI code generators usually need when asked to generate tests.
1 parent 2375ec7 commit 2efa98a

10 files changed

Lines changed: 141 additions & 68 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ src/assets/data
66
dist
77
tests/config.txt
88
configBackup.json
9+
.env

AGENTS.md

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ The rank column and some aggregate stats are rendered with invisible/white text.
5050
```bash
5151
git clone <repo-url> && cd Opensource-Contribution-Leaderboard
5252
cp src/server/config-example.json src/server/config.json
53-
# Edit config.json with your GitHub token + org + contributors
53+
cp src/server/.env.example src/server/.env
54+
# Edit .env with your GitHub token, org, admin password, etc.
55+
# Edit config.json with your contributors list
5456
npm run add
5557
npm run build
5658
cd dist/server
@@ -72,26 +74,36 @@ npm run serve # backend API server
7274
```
7375

7476
Frontend: http://localhost:8080
75-
Backend port: whatever `serverPort` is set to in config.json (default 62050)
77+
Backend port: whatever `SERVER_PORT` is set to in .env (default 62050)
7678

7779
## Config Reference
7880

81+
Static/environment settings live in `.env` (copy from `src/server/.env.example`):
82+
83+
```bash
84+
AUTH_TOKEN=ghp_YOUR_GITHUB_TOKEN # required — GitHub personal access token
85+
ORGANIZATION=YourOrg # required — GitHub org name
86+
ORGANIZATION_HOMEPAGE=https://yourorg.com/
87+
ORGANIZATION_GITHUB_URL=https://github.com/YourOrg
88+
ADMIN_PASSWORD=change-this # required — admin panel password
89+
SERVER_PORT=62050 # backend API port
90+
```
91+
92+
Dynamic/runtime values stay in `config.json` (modifiable via admin panel):
93+
7994
```json
8095
{
81-
"organization": "YourOrg",
82-
"organizationHomepage": "https://yourorg.com/",
83-
"organizationGithubUrl": "https://github.com/YourOrg",
84-
"authToken": "ghp_YOUR_GITHUB_TOKEN",
85-
"adminPassword": "change-this",
8696
"delay": "10",
87-
"serverPort": "62050",
88-
"contributors": ["user1", "user2"]
97+
"startDate": "2025-06-01",
98+
"contributors": ["user1", "user2"],
99+
"includedRepositories": ["Repo1", "Repo2"]
89100
}
90101
```
91102

92-
- `authToken` — required. GitHub personal access token with repo read access.
93103
- `delay` — seconds between API calls per contributor (respect rate limits).
94-
- `contributors` — array of GitHub usernames to track. Add users here even before their first contribution.
104+
- `startDate` — filter contributions from this date onwards.
105+
- `contributors` — array of GitHub usernames to track.
106+
- `includedRepositories` — repos to include in contribution tracking.
95107

96108
## Rules for Agents
97109

GEMINI.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,34 @@ npm start # webpack-dev-server on :8080
4242
npm run serve # backend on :62050 (in a second terminal)
4343
```
4444

45-
## Config File (src/server/config.json)
45+
## Config
46+
47+
Static/environment settings live in `.env` (copy from `src/server/.env.example`):
48+
49+
```bash
50+
AUTH_TOKEN=ghp_... # GitHub personal access token
51+
ORGANIZATION=YourOrg # GitHub org name
52+
ORGANIZATION_HOMEPAGE=https://yourorg.com/
53+
ORGANIZATION_GITHUB_URL=https://github.com/YourOrg
54+
ADMIN_PASSWORD=change-this # admin panel password
55+
SERVER_PORT=62050 # backend API port
56+
```
57+
58+
Dynamic/runtime values stay in `config.json` (modifiable via admin panel):
4659

4760
```json
4861
{
49-
"organization": "YourOrg",
50-
"organizationHomepage": "https://yourorg.com/",
51-
"organizationGithubUrl": "https://github.com/YourOrg",
52-
"authToken": "ghp_...",
53-
"adminPassword": "change-this",
5462
"delay": "10",
55-
"serverPort": "62050",
56-
"contributors": ["username1", "username2"]
63+
"startDate": "2025-06-01",
64+
"contributors": ["username1", "username2"],
65+
"includedRepositories": ["Repo1", "Repo2"]
5766
}
5867
```
5968

60-
- `authToken` — GitHub personal access token (needed for API rate limits)
6169
- `delay` — seconds between each contributor's API poll
70+
- `startDate` — filter contributions from this date onwards
6271
- `contributors` — GitHub usernames to track
72+
- `includedRepositories` — repos to include in tracking
6373

6474
## Design Decisions You Must Respect
6575

README.md

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,28 @@ cd Opensource-Contribution-Leaderboard
2323

2424
# 2. Create your config
2525
cp src/server/config-example.json src/server/config.json
26+
cp src/server/.env.example src/server/.env
2627
```
2728

28-
Edit `src/server/config.json`:
29+
Edit `src/server/.env` with your static settings:
30+
31+
```bash
32+
AUTH_TOKEN=ghp_YOUR_GITHUB_TOKEN
33+
ORGANIZATION=YourOrg
34+
ORGANIZATION_HOMEPAGE=https://yourorg.com/
35+
ORGANIZATION_GITHUB_URL=https://github.com/YourOrg
36+
ADMIN_PASSWORD=pick-something-better
37+
SERVER_PORT=62050
38+
```
39+
40+
Edit `src/server/config.json` with dynamic/runtime values:
2941

3042
```json
3143
{
32-
"organization": "YourOrg",
33-
"organizationHomepage": "https://yourorg.com/",
34-
"organizationGithubUrl": "https://github.com/YourOrg",
35-
"authToken": "ghp_YOUR_GITHUB_TOKEN",
36-
"adminPassword": "pick-something-better",
3744
"delay": "10",
38-
"serverPort": "62050",
39-
"contributors": ["contributor1", "contributor2", "contributor3"]
45+
"startDate": "2025-06-01",
46+
"contributors": ["contributor1", "contributor2"],
47+
"includedRepositories": ["Repo1", "Repo2"]
4048
}
4149
```
4250

@@ -56,14 +64,25 @@ Open **http://localhost:8080** — you're done.
5664

5765
## Config Reference
5866

67+
Static settings live in `.env`:
68+
69+
| Env Variable | What it is |
70+
|---|---|
71+
| `AUTH_TOKEN` | GitHub personal access token (repo read access) |
72+
| `ORGANIZATION` | Your GitHub org name |
73+
| `ORGANIZATION_HOMEPAGE` | Org homepage URL |
74+
| `ORGANIZATION_GITHUB_URL` | Org GitHub URL |
75+
| `ADMIN_PASSWORD` | Password for the admin panel |
76+
| `SERVER_PORT` | Internal backend port (default 62050) |
77+
78+
Dynamic settings live in `config.json` (modifiable via admin panel):
79+
5980
| Key | What it is |
6081
|---|---|
61-
| `organization` | Your GitHub org name |
62-
| `authToken` | GitHub personal access token (repo read access) |
63-
| `adminPassword` | Password for the admin panel |
6482
| `delay` | Seconds between API calls per contributor (respect rate limits) |
65-
| `serverPort` | Internal backend port (default 62050) |
83+
| `startDate` | Filter contributions from this date onwards |
6684
| `contributors` | Array of GitHub usernames to track |
85+
| `includedRepositories` | Repos to include in contribution tracking |
6786

6887
## Local Development
6988

src/server/.env.example

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Leaderboard .env configuration
2+
# Copy this file to .env and fill in your values.
3+
# Static/environment-specific settings live here.
4+
# Dynamic values (contributors, delay, startDate, includedRepositories)
5+
# remain in config.json since they are modified at runtime via the admin panel.
6+
7+
# GitHub personal access token (repo read access) — REQUIRED
8+
AUTH_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
9+
10+
# GitHub organization name — REQUIRED
11+
ORGANIZATION=RocketChat
12+
13+
# Organization homepage URL
14+
ORGANIZATION_HOMEPAGE=https://rocket.chat/
15+
16+
# Organization GitHub URL
17+
ORGANIZATION_GITHUB_URL=https://github.com/RocketChat
18+
19+
# Admin panel password — REQUIRED
20+
ADMIN_PASSWORD=change-this
21+
22+
# Server port for the backend API
23+
SERVER_PORT=62050
24+
25+
# Path overrides (relative to src/server/)
26+
# CONFIG_PATH=./config.json
27+
# ADMINDATA_PATH=./admindata.json
28+
# DATA_PATH=../assets/data/data.json
29+
# LOG_PATH=../assets/data/log.json
30+
# DATA_BASE_PATH=../assets/data
31+
# CONFIG_BACKUP_PATH=../../configBackup.json

src/server/app.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require('dotenv').config()
12
const http = require('http')
23
const jsonfile = require('jsonfile')
34
const url = require('url')
@@ -10,12 +11,16 @@ const app = express()
1011
const proxy = require('http-proxy-middleware')
1112
const path = require('path')
1213

13-
const configPath = './config.json'
14-
const admindataPath = './admindata.json'
15-
const dataPath = '../assets/data/data.json'
16-
const logPath = '../assets/data/log.json'
17-
const port = jsonfile.readFileSync(configPath).serverPort
18-
const configBackupPath = '../../configBackup.json'
14+
const configPath = process.env.CONFIG_PATH || './config.json'
15+
const admindataPath = process.env.ADMINDATA_PATH || './admindata.json'
16+
const dataPath = process.env.DATA_PATH || '../assets/data/data.json'
17+
const logPath = process.env.LOG_PATH || '../assets/data/log.json'
18+
const port = process.env.SERVER_PORT || 62050
19+
const configBackupPath = process.env.CONFIG_BACKUP_PATH || '../../configBackup.json'
20+
const organization = process.env.ORGANIZATION
21+
const organizationHomepage = process.env.ORGANIZATION_HOMEPAGE
22+
const organizationGithubUrl = process.env.ORGANIZATION_GITHUB_URL
23+
const adminPassword = process.env.ADMIN_PASSWORD
1924
const proxyOption = {
2025
target: 'http://localhost:' + port + '/',
2126
pathRewrite: { '^/api': '' },
@@ -66,7 +71,6 @@ process.on('exit', () => {
6671
const server = http
6772
.createServer((req, res) => {
6873
const route = url.parse(req.url).pathname
69-
const { adminPassword } = jsonfile.readFileSync(configPath)
7074

7175
switch (route) {
7276
case '/data':
@@ -84,12 +88,11 @@ const server = http
8488
})
8589
break
8690
case '/config':
87-
var Config = jsonfile.readFileSync(configPath)
8891
res.end(
8992
JSON.stringify({
90-
organization: Config.organization,
91-
organizationHomepage: Config.organizationHomepage,
92-
organizationGithubUrl: Config.organizationGithubUrl,
93+
organization: organization,
94+
organizationHomepage: organizationHomepage,
95+
organizationGithubUrl: organizationGithubUrl,
9396
})
9497
)
9598
break
@@ -154,7 +157,7 @@ const server = http
154157
res.end('Permission denied\n')
155158
return
156159
}
157-
var { organization, includedRepositories } = jsonfile.readFileSync(
160+
var { includedRepositories } = jsonfile.readFileSync(
158161
configPath
159162
)
160163
API.getRepositories(organization).then((repositories) => {
@@ -297,9 +300,10 @@ const server = http
297300
// Add this contributor in the data.json
298301
const data = jsonfile.readFileSync(dataPath)
299302
API.getContributorInfo(
300-
Config.organization,
303+
organization,
301304
username,
302-
Config.includedRepositories
305+
Config.includedRepositories,
306+
Config.startDate
303307
).then((result) => {
304308
if (
305309
result.avatarUrl !== '' &&

src/server/config-example.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
{
2-
"organization": "RocketChat",
3-
"organizationHomepage": "https://rocket.chat/",
4-
"organizationGithubUrl": "https://github.com/RocketChat",
5-
"authToken": "",
6-
"adminPassword": "123456",
72
"delay": "10",
8-
"serverPort": "62050",
93
"contributors": [
104
"HusseinElFeky",
115
"rodriq",

src/server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"axios": "^0.18.0",
44
"bluebird": "^3.5.4",
55
"chalk": "^2.4.2",
6+
"dotenv": "^16.6.1",
67
"express": "^4.16.4",
78
"http-proxy-middleware": "^0.19.1",
89
"jsonfile": "^5.0.0",

src/server/refresh.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1+
require('dotenv').config()
12
const Promise = require('bluebird')
23
const API = require('./util/API')
34
const jsonfile = require('jsonfile')
45
const fs = require('fs')
56

6-
const dataBasePath = '../assets/data'
7-
const dataPath = '../assets/data/data.json'
8-
const logPath = '../assets/data/log.json'
9-
const configPath = './config.json'
7+
const dataBasePath = process.env.DATA_BASE_PATH || '../assets/data'
8+
const dataPath = process.env.DATA_PATH || '../assets/data/data.json'
9+
const logPath = process.env.LOG_PATH || '../assets/data/log.json'
10+
const configPath = process.env.CONFIG_PATH || './config.json'
1011

1112
let interval = 150
1213
let dataBuffer = {}
@@ -27,9 +28,9 @@ if (fs.existsSync(logPath)) {
2728

2829
async function getAllContributorsInfo() {
2930
let Config = jsonfile.readFileSync(configPath)
30-
let organization = Config.organization
3131
let contributors = Config.contributors
3232
let includedRepositories = Config.includedRepositories
33+
let startDate = Config.startDate
3334

3435
interval = contributors.length < 150 ? 150 : (contributors.length + 10) // update interval
3536

@@ -40,7 +41,7 @@ async function getAllContributorsInfo() {
4041

4142
await Promise.delay(delay * 1000)
4243

43-
API.getContributorInfo(organization, contributor, includedRepositories).then( res => {
44+
API.getContributorInfo(process.env.ORGANIZATION, contributor, includedRepositories, startDate).then( res => {
4445
Config = jsonfile.readFileSync(configPath) // update Config
4546
delay = Config.delay // update delay
4647

src/server/util/API.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const axios = require('axios')
2-
const Config = require('../config.json')
32
const chalk = require('chalk')
43

54
const BASEURL = 'https://github.com'
@@ -11,7 +10,7 @@ async function get(url, _authToken) {
1110
headers: {
1211
Accept: 'application/vnd.github.v3+json',
1312
'User-Agent': 'GSoC-Contribution-Leaderboard',
14-
Authorization: 'token ' + Config.authToken,
13+
Authorization: 'token ' + process.env.AUTH_TOKEN,
1514
},
1615
})
1716
return new Promise((resolve) => {
@@ -32,7 +31,7 @@ async function get(url, _authToken) {
3231
case 'Bad credentials':
3332
console.log(
3433
chalk.red(
35-
'[ERROR] Your GitHub Token is not correct! Please check it in the config.json.'
34+
'[ERROR] Your GitHub Token is not correct! Please check the AUTH_TOKEN env variable.'
3635
)
3736
)
3837
process.exit()
@@ -128,16 +127,17 @@ async function getIssuesNumber(IssuesURL) {
128127
async function getContributorInfo(
129128
organization,
130129
contributor,
131-
includedRepositories
130+
includedRepositories,
131+
startDate
132132
) {
133133
const home = BASEURL + '/' + contributor
134134
const avatarUrl = await getContributorAvatar(contributor)
135-
let OpenPRsURL = `/search/issues?q=is:pr+author:${contributor}+is:Open+created:>=${Config.startDate}`
136-
let openPRsLink = `${BASEURL}/search?q=type:pr+author:${contributor}+is:open+created:>=${Config.startDate}`
137-
let MergedPRsURL = `/search/issues?q=is:pr+author:${contributor}+is:Merged+created:>=${Config.startDate}`
138-
let mergedPRsLink = `${BASEURL}/search?q=type:pr+author:${contributor}+is:merged+created:>=${Config.startDate}`
139-
let IssuesURL = `/search/issues?q=is:issue+author:${contributor}+created:>=${Config.startDate}`
140-
let issuesLink = `${BASEURL}/search?q=type:issue+author:${contributor}+created:>=${Config.startDate}`
135+
let OpenPRsURL = `/search/issues?q=is:pr+author:${contributor}+is:Open+created:>=${startDate}`
136+
let openPRsLink = `${BASEURL}/search?q=type:pr+author:${contributor}+is:open+created:>=${startDate}`
137+
let MergedPRsURL = `/search/issues?q=is:pr+author:${contributor}+is:Merged+created:>=${startDate}`
138+
let mergedPRsLink = `${BASEURL}/search?q=type:pr+author:${contributor}+is:merged+created:>=${startDate}`
139+
let IssuesURL = `/search/issues?q=is:issue+author:${contributor}+created:>=${startDate}`
140+
let issuesLink = `${BASEURL}/search?q=type:issue+author:${contributor}+created:>=${startDate}`
141141
includedRepositories.forEach((repository) => {
142142
openPRsLink += `+repo:${organization}/${repository}`
143143
mergedPRsLink += `+repo:${organization}/${repository}`

0 commit comments

Comments
 (0)