Local Development
This guide walks from a clean checkout to a fully working local environment.
Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Node.js | 20+ | CI runs Node 24; 20 is the local minimum |
| pnpm | 10.33.1 | npm install -g pnpm@10.33.1 |
| Git | any | — |
| A GitHub account | — | Must be able to create GitHub Apps |
| Supabase project | — | Free tier works; or local Docker |
| ngrok (optional) | any | Only needed to receive GitHub webhook events locally |
1. Clone and install
git clone https://github.com/upayanmazumder/gh-org-tool.git
cd gh-org-tool
pnpm install2. Create a GitHub App
The application authenticates as a GitHub App — a plain OAuth app is not sufficient. You need both the app-level credentials and an installation on a target org.
-
Go to GitHub Settings → Developer settings → GitHub Apps → New GitHub App.
-
Fill in:
Field Value App Name Any unique name (e.g. my-gh-org-tool-dev)Homepage URL http://localhost:3000Callback URL http://localhost:3000/api/auth← must match exactlyWebhook URL http://localhost:3000/api/install/webhookWebhook Secret A random string — save it -
Set Permissions:
Category Permission Level Organization Members Read Organization Teams Read Repository Metadata Read Repository Contents Read Repository Pull requests Read Repository Issues Read Account Email addresses Read -
Subscribe to events: Installation, Installation repositories.
-
Click Create GitHub App.
-
On the app page, note down:
- App ID →
GITHUB_APP_ID - Client ID →
GITHUB_CLIENT_ID - Generate a Client secret →
GITHUB_CLIENT_SECRET - Note the App slug (in the URL
https://github.com/apps/<slug>) →GITHUB_APP_NAME
- App ID →
-
Scroll to Private keys → Generate a private key → a
.pemfile downloads.
The entire file contents (including-----BEGIN RSA PRIVATE KEY-----and-----END RSA PRIVATE KEY-----lines) becomeGITHUB_PRIVATE_KEY. -
Install the app on a target GitHub organization. Go to the app page → Install App → pick an org.
Webhooks in local dev
GitHub requires an HTTPS endpoint for webhooks. For local development, use ngrok :
ngrok http 3000Copy the HTTPS forwarding URL and use it as the Webhook URL in your GitHub App settings (e.g. https://abc123.ngrok.io/api/install/webhook).
If you skip this, webhooks will not reach your local instance — installation events won’t be processed. You can still trigger a manual sync from the app UI.
3. Set up Supabase
The app uses Supabase (Postgres) for all persistent data. There are 11 migrations in supabase/migrations/ that must be applied in order.
Option A: Supabase cloud (simplest)
-
Create a project at supabase.com .
-
From the project dashboard, copy:
- Project URL →
NEXT_PUBLIC_SUPABASE_URL - anon / public key →
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY - service_role key →
SUPABASE_SERVICE_ROLE_KEY - Project reference (the random string in
https://<ref>.supabase.co)
- Project URL →
-
Apply migrations via the Supabase CLI:
npx supabase login npx supabase link --project-ref <your-project-ref> npx supabase db push -
Verify:
npx supabase migration listAll 11 migrations (001 through 011) should show as applied.
Option B: Local Supabase with Docker
npx supabase start
npx supabase db resetThis starts a local Postgres instance and runs all migrations. The CLI prints local credentials on start. Use those values for the env vars.
Option C: Manual (no CLI)
Open the Supabase SQL Editor for your project and run each file in supabase/migrations/ in order: 001_enums_and_utilities.sql through 011_computed_scores.sql. Do not skip or reorder them.
4. Configure environment variables
Copy the example file:
cp example.env .env.localOpen .env.local and fill in the required variables. See the Environment Variables reference for the full table — at minimum you need these to boot:
GITHUB_APP_ID=
GITHUB_PRIVATE_KEY=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_APP_NAME=
GITHUB_WEBHOOK_SECRET=
SUPABASE_SERVICE_ROLE_KEY=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=
NEXT_PUBLIC_APP_URL=http://localhost:3000For GITHUB_PRIVATE_KEY, the value is the full PEM content. In .env.local this means putting the entire multi-line value in one env entry. One way to do this:
GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
...rest of key...
-----END RSA PRIVATE KEY-----"5. Start the development server
pnpm devThis runs Next.js with Turbopack on port 3000. Open http://localhost:3000 .
You should see the landing page. Click Sign in with GitHub — this redirects to GitHub OAuth and back to /api/auth. After sign-in, if your GitHub account is a member of the org where the app is installed, you will see the dashboard.
Development commands
| Command | What it does |
|---|---|
pnpm dev | Turbopack dev server on port 3000 (fast refresh) |
pnpm build | Production build (standard Next.js bundler) |
pnpm start | Start production server (run pnpm build first) |
pnpm lint | ESLint with flat config (eslint.config.mjs) |
pnpm format | Prettier over all files |
pnpm test | Vitest in interactive watch mode |
pnpm test:ci | Vitest with v8 coverage (used in CI) |
Pre-commit hooks
The repo uses husky + lint-staged. On git commit, prettier runs over staged files only (prettier --write --ignore-unknown). There is no lint or typecheck on commit — those run in CI.
Redis (optional)
If you want to test with Redis caching, provision an Upstash Redis database and add:
UPSTASH_REDIS_REST_URL=https://...
UPSTASH_REDIS_REST_TOKEN=...Without these vars, the app skips Redis and uses the in-memory LRU cache only.
Troubleshooting checklist
| Symptom | Likely cause |
|---|---|
| OAuth redirect returns 400 | Callback URL mismatch — check the GitHub App settings match exactly http://localhost:3000/api/auth |
| Sign-in works but no org data | App not installed on the org; or the org is not linked to your Supabase installation |
| Webhook events not arriving | GitHub requires HTTPS — set up ngrok and update the Webhook URL in GitHub App settings |
| Build fails at start | Missing required env vars — check lib/env/index.ts for which are required |
supabase migration list shows missing migrations | Run npx supabase db push again; or apply SQL manually in order |