Skip to content

Commit 31eb6fd

Browse files
docs: add end-to-end setup walkthrough with screenshots and 6 runtime fixes (LU-31) (#62)
Walked the full ClearPR setup top-to-bottom from a clean machine, captured screenshots at every GitHub UI step, and fixed every blocker the live run hit. The result is a single PR that turns the project from "impossible to set up by following the docs" to "noob can copy-paste through it". ## Documentation - New `docs-site/guide/setup-walkthrough.md` with steps 1-5 (Docker stack, GitHub App creation, install, demo PR) plus troubleshooting for the missed-installation-event case. - 10 screenshots in `docs-site/public/setup/` covering every GitHub UI step (form, permissions, events, App ID, generate key, install, choose repos, posted review). - Section on running with LM Studio or Ollama as a free local LLM, including the host.docker.internal trick for reaching the Mac host from a Docker container. - Fix `getting-started.md` and `github-app-setup.md` GITHUB_PRIVATE_KEY guidance (was wrong: said "file path", actually wants PEM contents). These were missed by PR #59. ## Runtime fixes (real bugs in v0.1.2 that surfaced during the walkthrough) 1. IndexingConsumer injected RepositoryRepositoryPort directly - cross-module leak that broke DI at boot. Added indexRepositoryById to RepositoryIndexerPort so the consumer goes through its proper port. 2. DiffEngineModule didn't export FileContentProviderPort - LoadGuidelinesUseCase couldn't resolve it at boot. Added to module exports. 3. DB SSL forced when NODE_ENV=production - app refused to connect to the bundled local pgvector. New DATABASE_SSL env var, false overrides default. 4. Redis TLS forced when NODE_ENV=production - same root cause as #3. New REDIS_TLS env var. 5. GlobalExceptionFilter discarded HttpException response detail - "Bad Request" with no clue why. Now logs getResponse() so validation error arrays surface in logs. 6. Global ValidationPipe forbidNonWhitelisted: true rejected real GitHub webhook payloads (~120 extra fields). Dropped the global setting; per-DTO @IsOptional + whitelist already strips unknown fields. 158 unit tests + 5 e2e tests still pass. End-to-end pipeline verified with LM Studio: webhook -> dispatch -> enqueue -> consumer -> diff (15 raw -> 4 semantic, 73% noise removed) -> LLM -> review posted to clearpr-quickstart#1.
1 parent 6fc11ec commit 31eb6fd

22 files changed

Lines changed: 396 additions & 28 deletions

docs-site/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default defineConfig({
2626
{
2727
text: 'Setup',
2828
items: [
29+
{ text: 'Setup Walkthrough', link: '/guide/setup-walkthrough' },
2930
{ text: 'GitHub App Setup', link: '/guide/github-app-setup' },
3031
{ text: 'Docker Deployment', link: '/guide/docker-deployment' },
3132
{ text: 'LLM Providers', link: '/guide/llm-providers' },

docs-site/guide/getting-started.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,29 @@ Create a `.env` file next to your compose file:
2828

2929
```env
3030
# Required
31-
GITHUB_APP_ID=your_app_id
32-
GITHUB_PRIVATE_KEY=your_private_key.pem
33-
GITHUB_WEBHOOK_SECRET=your_webhook_secret
31+
GITHUB_APP_ID=123456
32+
GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...PEM contents on one line, escape newlines as \\n...\n-----END RSA PRIVATE KEY-----"
33+
GITHUB_WEBHOOK_SECRET=a-strong-random-secret-you-set-when-creating-the-app
3434
3535
# LLM Provider (choose one)
3636
LLM_PROVIDER=anthropic
3737
LLM_API_KEY=sk-ant-...
3838
39+
# Embedding (required for PR memory - use Voyage)
40+
VOYAGE_API_KEY=pa-...
41+
3942
# Database (defaults work with the compose snippet below)
4043
DATABASE_URL=postgresql://clearpr:clearpr@db:5432/clearpr
4144
REDIS_URL=redis://redis:6379
4245
```
4346

47+
`GITHUB_PRIVATE_KEY` holds the **contents** of the `.pem` file you downloaded from GitHub, not a path to it. The easiest way to get it into `.env`:
48+
49+
```bash
50+
# Bash: read the file and inject as a single env var (newlines preserved by shell)
51+
echo "GITHUB_PRIVATE_KEY=\"$(cat path/to/clearpr.private-key.pem)\"" >> .env
52+
```
53+
4454
### 3. Run with Docker Compose
4555

4656
Save as `docker-compose.yml` alongside your `.env`:

docs-site/guide/github-app-setup.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ Check these event subscriptions:
3333
1. Scroll to the bottom of the app settings
3434
2. Click **Generate a private key**
3535
3. Save the downloaded `.pem` file
36-
4. Set `GITHUB_PRIVATE_KEY` in `.env` to the file path or the key content
36+
4. Set `GITHUB_PRIVATE_KEY` in `.env` to the **contents** of the file (not the path):
37+
38+
```bash
39+
echo "GITHUB_PRIVATE_KEY=\"$(cat path/to/clearpr.private-key.pem)\"" >> .env
40+
```
3741

3842
## Install the App
3943

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
# Setup Walkthrough
2+
3+
Follow this guide to install ClearPR on a real repo and watch it review a pull request. No prior NestJS or self-hosting experience needed.
4+
5+
By the end you'll have:
6+
7+
- A running ClearPR instance (Docker)
8+
- A GitHub App configured to send webhooks to it
9+
- A demo repo (`clearpr-quickstart`) with a sample PR that ClearPR reviews
10+
11+
Estimated time: 20-30 minutes.
12+
13+
## What you'll need
14+
15+
| | |
16+
|---|---|
17+
| **GitHub account** | Required. Free tier is fine. |
18+
| **A machine to run Docker** | Local laptop is fine for testing. For production, a small VM with a public IP. |
19+
| **Anthropic API key** | Or OpenAI / Mistral / Gemini key. [Get one from console.anthropic.com](https://console.anthropic.com). Ollama or LM Studio also work; no key needed for those. |
20+
| **Voyage AI API key** | For PR memory (similarity search on past comments). [Get one from dash.voyageai.com](https://dash.voyageai.com). Optional: leave unset and the memory feature is silently skipped, the rest of the review still works. |
21+
22+
## Step 1: Run ClearPR with Docker
23+
24+
Pull the released image and stand up the stack:
25+
26+
```bash
27+
mkdir clearpr && cd clearpr
28+
29+
curl -O https://raw.githubusercontent.com/vineethkrishnan/clearpr/main/docker-compose.yml
30+
curl -o .env https://raw.githubusercontent.com/vineethkrishnan/clearpr/main/.env.example
31+
```
32+
33+
Open `.env` in an editor. Don't fill in `GITHUB_*` yet - we get those from the GitHub App in Step 2. For now just set the LLM keys:
34+
35+
```env
36+
LLM_PROVIDER=anthropic
37+
LLM_API_KEY=sk-ant-...
38+
39+
VOYAGE_API_KEY=pa-...
40+
```
41+
42+
Pin the image version (skip `:latest` for production):
43+
44+
```bash
45+
sed -i.bak 's|build: \.|image: ghcr.io/vineethkrishnan/clearpr:0.1.2|' docker-compose.yml
46+
```
47+
48+
Bring up only the database and Redis for now (the app needs the GitHub App secrets to start cleanly):
49+
50+
```bash
51+
docker compose up -d db redis
52+
docker compose ps
53+
```
54+
55+
You should see both services as `healthy`. If `db` is unhealthy, give it 20 seconds - pgvector init takes a moment on first start.
56+
57+
## Step 2: Create the GitHub App
58+
59+
A GitHub App is GitHub's way of giving an external service permission to read PR diffs and post review comments. We'll create one that points at your ClearPR instance.
60+
61+
### 2.1 Open the New App page
62+
63+
Go to **GitHub Settings → Developer settings → GitHub Apps → New GitHub App**.
64+
65+
Direct link: <https://github.com/settings/apps/new>
66+
67+
![New GitHub App page](/setup/02-new-app-page.png)
68+
69+
### 2.2 Fill in the basics
70+
71+
- **App name**: `clearpr-<your-username>` (must be globally unique on GitHub)
72+
- **Homepage URL**: `https://github.com/vineethkrishnan/clearpr` (or your fork)
73+
- **Webhook URL**: this depends on whether your ClearPR is reachable from the internet:
74+
- **Public server**: `https://your-domain.com/webhook`
75+
- **Local laptop**: use a smee.io tunnel - see the [Local testing with smee.io](#local-testing-with-smee-io) section below first, then come back
76+
- **Webhook secret**: click "Generate" or paste a long random string. **Copy it now**, you'll need it in `.env`.
77+
78+
![App basics filled in](/setup/03-app-basics.png)
79+
80+
### 2.3 Set permissions
81+
82+
Scroll down to **Repository permissions**:
83+
84+
| Permission | Access |
85+
|---|---|
86+
| Pull requests | Read and write |
87+
| Contents | Read-only |
88+
| Metadata | Read-only |
89+
| Issues | Read-only |
90+
91+
![Repository permissions](/setup/04-permissions.png)
92+
93+
### 2.4 Subscribe to events
94+
95+
Scroll down to **Subscribe to events** and check:
96+
97+
- Pull request
98+
- Pull request review comment
99+
- Issue comment
100+
101+
GitHub auto-delivers `installation` and `installation_repositories` events to every App, so they're not in the subscribable list. ClearPR handles them when they arrive.
102+
103+
![Event subscriptions](/setup/05-events.png)
104+
105+
### 2.5 Where the App can be installed
106+
107+
Under **Where can this GitHub App be installed?**, choose **Only on this account** (you can change this later if you want others to use it).
108+
109+
Click **Create GitHub App** at the bottom.
110+
111+
### 2.6 Generate the private key
112+
113+
After creation you land on the app's settings page. Two things to grab:
114+
115+
1. **App ID** at the top of the page (a 6-7 digit number). Save it.
116+
117+
![App ID](/setup/06-app-id.png)
118+
119+
2. Scroll to the bottom and click **Generate a private key**. A `.pem` file downloads. Save the path - you need its contents in `.env`.
120+
121+
![Generate private key](/setup/07-generate-key.png)
122+
123+
### 2.7 Update `.env`
124+
125+
Back in your terminal:
126+
127+
```bash
128+
# In the clearpr/ directory
129+
cat >> .env <<EOF
130+
GITHUB_APP_ID=123456
131+
GITHUB_WEBHOOK_SECRET=the-secret-you-set-in-2.2
132+
EOF
133+
134+
# Inject the private key contents
135+
echo "GITHUB_PRIVATE_KEY=\"$(cat ~/Downloads/clearpr-yourname.*.private-key.pem)\"" >> .env
136+
```
137+
138+
Replace `123456` with your actual App ID and the path with where the `.pem` actually downloaded.
139+
140+
## Step 3: Start ClearPR
141+
142+
```bash
143+
docker compose up -d app
144+
docker compose logs app --tail 30
145+
```
146+
147+
You should see:
148+
149+
```
150+
[entrypoint] Running TypeORM migrations...
151+
... migration: InitialSchema1712700000000 ...
152+
[entrypoint] Starting application...
153+
[Nest] ... Nest application successfully started
154+
```
155+
156+
Verify it's healthy:
157+
158+
```bash
159+
curl http://localhost:3000/health/live
160+
# {"status":"ok"}
161+
162+
curl http://localhost:3000/health/ready | jq .
163+
# Should show database, redis, queues all "up"
164+
```
165+
166+
If `health/ready` shows `down` for redis or database, check `docker compose ps` - those services need to be `healthy` first.
167+
168+
## Step 4: Install the App
169+
170+
Back on your GitHub App's settings page, click **Install App** in the left sidebar.
171+
172+
![Install App link](/setup/08-install-app.png)
173+
174+
Click **Install** next to your account, then choose **Only select repositories** and pick `clearpr-quickstart` (or any repo you want reviewed). Click **Install**.
175+
176+
![Choose repos to install on](/setup/09-choose-repos.png)
177+
178+
## Step 5: Fork the demo repo and open a PR
179+
180+
Fork `clearpr-quickstart` to your account: <https://github.com/vineethkrishnan/clearpr-quickstart/fork>
181+
182+
Then locally:
183+
184+
```bash
185+
git clone https://github.com/<your-username>/clearpr-quickstart.git
186+
cd clearpr-quickstart
187+
git checkout -b demo-prettier-noise
188+
```
189+
190+
Make a deliberately noisy change - swap quote style and add semicolons, plus one real behavior change buried in there:
191+
192+
```bash
193+
cat > src/discount.ts <<'EOF'
194+
export type Tier = 'bronze' | 'silver' | 'gold';
195+
196+
export function priceAfterDiscount(price: number, tier: Tier): number {
197+
if (price < 0) return 0;
198+
switch (tier) {
199+
case 'bronze': return price * 0.95;
200+
case 'silver': return price * 0.9;
201+
case 'gold': return price * 0.75;
202+
}
203+
}
204+
EOF
205+
206+
git add -A
207+
git commit -m "refactor: tidy discount module"
208+
git push -u origin demo-prettier-noise
209+
```
210+
211+
Open the PR on GitHub. The diff GitHub shows is **8 changed lines** (every line of the file changed - quote style + the gold-tier discount went from 0.8 to 0.75 + the `<= 0` became `< 0`).
212+
213+
ClearPR sees the noise (quote style is identical AST), strips it, and tells you the **2 real changes**:
214+
215+
- `gold` discount changed (`* 0.8``* 0.75`)
216+
- The empty-price guard widened (`price <= 0``price < 0`, now charges $0 for $0 instead of returning 0)
217+
218+
![ClearPR review on the demo PR](/setup/10-review-comment.png)
219+
220+
## Using LM Studio or Ollama instead of a paid LLM
221+
222+
Don't have an Anthropic/OpenAI key? Both LM Studio and Ollama expose an OpenAI-compatible API locally and work as drop-in replacements.
223+
224+
### LM Studio
225+
226+
1. Open LM Studio, load any chat model (a small one like `google/gemma-4-e4b` or `qwen2.5-7b-instruct` is plenty for review prompts)
227+
2. Switch to the **Developer / Local Server** tab and click **Start Server** (default port 1234)
228+
3. In your `.env`:
229+
```env
230+
LLM_PROVIDER=openai
231+
LLM_BASE_URL=http://host.docker.internal:1234/v1
232+
LLM_MODEL=google/gemma-4-e4b # whatever id LM Studio shows for your model
233+
LLM_API_KEY=lm-studio # any non-empty string; LM Studio doesn't validate it
234+
```
235+
4. `docker compose restart app`
236+
237+
`host.docker.internal` is how the app container reaches your Mac's localhost. On Linux Docker, use `--add-host=host.docker.internal:host-gateway` in your compose config.
238+
239+
Reviews will be slower than a hosted LLM (15-60 seconds per review on a small model) but everything works end-to-end.
240+
241+
### Ollama
242+
243+
Same idea:
244+
245+
```env
246+
LLM_PROVIDER=ollama
247+
LLM_BASE_URL=http://host.docker.internal:11434
248+
LLM_MODEL=llama3
249+
```
250+
251+
No API key needed; Ollama doesn't authenticate.
252+
253+
## Local testing with smee.io
254+
255+
If you're running ClearPR on your laptop (not behind a public domain), GitHub can't reach `localhost:3000`. Use [smee.io](https://smee.io) as a forwarder.
256+
257+
```bash
258+
# Get a fresh channel URL
259+
curl -s -o /dev/null -w '%{redirect_url}\n' https://smee.io/new
260+
# -> https://smee.io/aBcDeFgHiJ123
261+
262+
# Forward webhooks to your local server
263+
npx smee-client --url https://smee.io/aBcDeFgHiJ123 --target http://localhost:3000/webhook
264+
```
265+
266+
Use the `https://smee.io/aBcDeFgHiJ123` URL as the webhook URL when creating the GitHub App in Step 2.2. Leave the `smee-client` running while you test.
267+
268+
## Troubleshooting
269+
270+
### `health/ready` returns 503
271+
272+
Check which subsystem is down:
273+
274+
```bash
275+
curl http://localhost:3000/health/ready | jq .
276+
```
277+
278+
- `database: down`: pgvector container isn't ready. `docker compose ps db` should be `healthy`.
279+
- `redis: down`: same for redis.
280+
- `queues: down`: more than 100 jobs failed; `docker compose logs app --tail 200 | grep -i error` and `redis-cli -h localhost LLEN bull:reviews:failed` will show why.
281+
282+
### Webhook signature invalid
283+
284+
`GITHUB_WEBHOOK_SECRET` in `.env` must match what you set when creating the GitHub App. If you regenerated the secret on GitHub, update `.env` and `docker compose restart app`.
285+
286+
### Webhook not arriving at all
287+
288+
Check the GitHub App's **Advanced** tab. Recent deliveries should be listed with a green check or red X. Click into a failing one to see the response. If GitHub got a 5xx, look at `docker compose logs app`.
289+
290+
### Webhook arrives but ClearPR says it doesn't know the repo
291+
292+
If you installed the App **before** the smee tunnel (or your public domain) was reachable, the `installation.created` event was sent into the void, and ClearPR's database has no record of your installation. Subsequent PR webhooks succeed at HMAC + dispatch but the per-action use cases bail out because `repository_repo.findByGithubId(...)` returns null.
293+
294+
Fix: replay the missed delivery.
295+
296+
1. Go to your GitHub App's **Advanced** tab: `https://github.com/settings/apps/<your-app>/advanced`
297+
2. Find the `installation.created` delivery in **Recent deliveries**
298+
3. Click into it, then click **Redeliver**
299+
300+
ClearPR receives it, registers the installation, and triggers a bulk index of past PR comments. Re-fire the PR webhook (or push another commit) and the review will run.
301+
302+
### Review never appears
303+
304+
Walk the pipeline:
305+
306+
```bash
307+
# Did the webhook arrive?
308+
docker compose logs app | grep "Webhook dispatched"
309+
310+
# Was a job enqueued?
311+
redis-cli -h localhost LLEN bull:reviews:waiting
312+
313+
# Was it processed?
314+
docker compose logs app | grep "Review completed"
315+
```
316+
317+
If the LLM call fails, you'll see it in the logs - usually a missing or wrong API key.
318+
319+
## Next steps
320+
321+
- [Choose a different LLM provider](./llm-providers)
322+
- [Configure project guidelines](./project-config) so ClearPR reviews against your team's rules
323+
- [Use PR commands](./pr-commands) to trigger manual reviews or change behavior per-PR
324+
- [Production deployment](./docker-deployment) for going beyond local testing
76.5 KB
Loading
94.5 KB
Loading
128 KB
Loading
129 KB
Loading
102 KB
Loading
62.9 KB
Loading

0 commit comments

Comments
 (0)