|
| 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 | + |
| 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 | + |
| 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 | + |
| 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 | + |
| 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 | +  |
| 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 | +  |
| 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 | + |
| 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 | + |
| 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 | + |
| 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 |
0 commit comments