diff --git a/README.md b/README.md index b6d6e85..73e73d6 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,34 @@ Troutbot is the final solution to protecting the trout population. It's environmental protection incarnate! -Well in reality, it's a GitHub webhook bot that analyzes issues and pull -requests using real signals such as CI check results, diff quality, and body -structure and then posts trout-themed comments about the findings. Now you know -whether your changes hurt or help the trout population. +Well, in reality, it is a GitHub bot that analyzes issues and pull requests +using real signals such as CI check results, diff quality, and body structure +and then posts trout-themed comments about the findings. Now you know whether +your changes hurt or help the trout population. + +## Operation Modes + +Troutbot supports two operation modes: + +### Webhook Mode (Real-time) + +GitHub sends webhook events to troutbot when issues/PRs are opened or updated. +Troutbot responds immediately. Best for: + +- Single or few repositories +- You have admin access to configure webhooks +- You can expose a public endpoint + +### Polling Mode (Periodic) + +Troutbot periodically polls configured repositories for `@troutbot` mentions in +comments. Best for: + +- Monitoring dozens of repositories without webhook setup +- Running behind a firewall or on dynamic IPs +- Simplified deployment without webhook secrets + +Both modes use the same analysis engine and produce the same results. ## Quick Start @@ -17,18 +41,17 @@ $ npm install # Populate the environment config $ cp .env.example .env -# Set up application confg +# Set up application config cp config.example.ts config.ts -# Edit .env and config.ts, then to start: -npm run build && npm start +# Edit .env and config.ts, then build and start. +# If `.env` is not populated, Troutbot will start in dry-run mode. +pnpm run build && pnpm start ``` ## How It Works -Troutbot has three analysis backends ran against each incoming webhook event. -They are the primary decisionmaking logic behind whether your changes affect the -trout population negatively, or positively. +Troutbot has three analysis backends that analyze issues and PRs: ### `checks` @@ -71,6 +94,75 @@ checks 0.4, diff 0.3, quality 0.3). Backends that return zero confidence (e.g., no CI checks found yet) are excluded from the average. If combined confidence falls below `confidenceThreshold`, the result is forced to neutral. +## Webhook Mode + +In webhook mode, troutbot receives real-time events from GitHub. + +### GitHub Webhook Setup + +1. Go to your repository's **Settings > Webhooks > Add webhook** +2. **Payload URL**: `https://your-host/webhook` +3. **Content type**: `application/json` +4. **Secret**: Generate with `openssl rand -hex 32` and set as `WEBHOOK_SECRET` +5. **Events**: Select **Issues**, **Pull requests**, and optionally **Check + suites** (for re-analysis when CI finishes) + +If you enable **Check suites** and set `response.allowUpdates: true` in your +config, troutbot will update its comment on a PR once CI results are available. + +### Webhook Security + +- **`WEBHOOK_SECRET` is strongly recommended.** Without it, anyone who can reach + the `/webhook` endpoint can trigger analysis and post comments. Always set a + secret and configure the same value in your GitHub webhook settings. + +## Polling Mode + +In polling mode, troutbot periodically checks configured repositories for +`@troutbot` mentions in comments. + +### Configuration + +Enable polling in your `config.ts`: + +```typescript +polling: { + enabled: true, + intervalMinutes: 5, // Check every 5 minutes + lookbackMinutes: 10, // Look back 10 minutes for new comments +} +``` + +### How It Works + +1. On startup, troutbot fetches recent comments from all configured repositories +2. It scans each comment for `@troutbot` mentions +3. When found, it analyzes the associated issue/PR and posts a response +4. Processed comments are tracked to avoid duplicate responses +5. The cycle repeats every `intervalMinutes` + +### On-Demand Analysis + +Users can trigger analysis by mentioning `@troutbot` in any comment: + +```plaintext +Hey @troutbot, can you take a look at this? +``` + +The bot will analyze the issue/PR and respond with a trout-themed assessment. + +### Rate Limiting + +Polling uses the GitHub REST API and respects rate limits. The default settings +(5 min interval, 10 min lookback) are conservative and work well within GitHub's +5000 requests/hour limit for personal access tokens. + +### Requirements + +- `GITHUB_TOKEN` with read access to all watched repositories +- Repositories configured in `config.repositories` +- Write access to post comments + ## GitHub Account & Token Setup Troutbot is designed to run as a dedicated bot account on GitHub. Create a @@ -89,8 +181,7 @@ The bot account needs access to every repository it will comment on: - **For organization repos**: Invite the bot account as a collaborator with **Write** access, or add it to a team with write permissions. - **For personal repos**: Add the bot account as a collaborator under - \*\*Settings - > Collaborators\*\*. + `Settings > Collaborators`. The bot needs write access to post comments. Read access alone is not enough. @@ -98,10 +189,10 @@ The bot needs write access to post comments. Read access alone is not enough. Log in as the bot account and create a fine-grained PAT: -1. Go to **Settings > Developer settings > Personal access tokens > Fine-grained - tokens** +1. Go to + `Settings > Developer settings > Personal access tokens > Fine-grained tokens` 2. Click **Generate new token** -3. Set a descriptive name (e.g., `troutbot-webhook`) +3. Set a descriptive name (e.g., `troutbot-production`) 4. Set **Expiration** - pick a long-lived duration or no expiration, since this runs unattended 5. Under **Repository access**, select the specific repositories the bot will @@ -121,19 +212,7 @@ Set this as the `GITHUB_TOKEN` environment variable. > `repo` scope. Fine-grained tokens are recommended because they follow the > principle of least privilege. -### 4. Generate a webhook secret - -Generate a random secret to verify webhook payloads: - -```bash -openssl rand -hex 32 -``` - -Set this as the `WEBHOOK_SECRET` environment variable, and use the same value -when configuring the webhook in GitHub (see -[GitHub Webhook Setup](#github-webhook-setup)). - -## Configuration +## Configuring Troutbot ### Environment Variables @@ -142,7 +221,7 @@ when configuring the webhook in GitHub (see | Variable | Description | Required | | ---------------- | ----------------------------------------------------- | ---------------------------- | | `GITHUB_TOKEN` | Fine-grained PAT from the bot account (see above) | No (dry-run without it) | -| `WEBHOOK_SECRET` | Secret for verifying webhook signatures | No (skips verification) | +| `WEBHOOK_SECRET` | Secret for verifying webhook signatures | No (only for webhook mode) | | `PORT` | Server port (overrides `server.port` in config) | No | | `CONFIG_PATH` | Path to config file | No (defaults to `config.ts`) | | `LOG_LEVEL` | Log level override (`debug`, `info`, `warn`, `error`) | No | @@ -156,10 +235,11 @@ default-exports a `Config` object - full type checking and autocompletion in your editor. ```typescript -import type { Config } from "./src/types"; +import type { Config } from './src/types'; const config: Config = { server: { port: 3000 }, + repositories: [{ owner: 'myorg', repo: 'myrepo' }], engine: { backends: { checks: { enabled: true }, @@ -169,6 +249,11 @@ const config: Config = { weights: { checks: 0.4, diff: 0.3, quality: 0.3 }, confidenceThreshold: 0.1, }, + polling: { + enabled: true, + intervalMinutes: 5, + lookbackMinutes: 10, + }, // ... }; @@ -180,28 +265,13 @@ pre-compilation needed. See `config.example.ts` for the full annotated reference. -## GitHub Webhook Setup - -1. Go to your repository's **Settings > Webhooks > Add webhook** -2. **Payload URL**: `https://your-host/webhook` -3. **Content type**: `application/json` -4. **Secret**: Must match your `WEBHOOK_SECRET` env var -5. **Events**: Select **Issues**, **Pull requests**, and optionally **Check - suites** (for re-analysis when CI finishes) - -If you enable **Check suites** and set `response.allowUpdates: true` in your -config, troutbot will update its comment on a PR once CI results are available. - ## Production Configuration When deploying troutbot to production, keep the following in mind: -- **`WEBHOOK_SECRET` is strongly recommended.** Without it, anyone who can reach - the `/webhook` endpoint can trigger analysis and post comments. Always set a - secret and configure the same value in your GitHub webhook settings. -- **Use a reverse proxy with TLS.** GitHub sends webhook payloads over HTTPS. - Put nginx, Caddy, or a cloud load balancer in front of troutbot and terminate - TLS there. +- **Use a reverse proxy with TLS.** If using webhook mode, GitHub sends payloads + over HTTPS. Put nginx, Caddy, or a cloud load balancer in front of troutbot + and terminate TLS there. Polling mode doesn't require a public endpoint. - **Set `NODE_ENV=production`.** This is set automatically in the Docker image. For standalone deployments, export it in your environment. Express uses this to enable performance optimizations. @@ -218,22 +288,19 @@ When deploying troutbot to production, keep the following in mind: ## Deployment -
-Standalone (Node.js) +### Standalone (Node.js) ```bash npm ci npm run build export NODE_ENV=production export GITHUB_TOKEN="ghp_..." -export WEBHOOK_SECRET="your-secret" +# Only needed for webhook mode: +# export WEBHOOK_SECRET="your-secret" npm start ``` -
- -
-Nix +### Nix **Flake** (NixOS or flake-enabled systems): @@ -248,8 +315,8 @@ npm start { services.troutbot = { enable = true; - environmentFile = "/path/to/.env"; # use Agenix if possible - configPath = "/path/to/config.ts" # use Agenix if possible + environmentFile = "/path/to/.env"; + configPath = "/path/to/config.ts"; }; } ]; @@ -264,10 +331,7 @@ npm start nix run github:notashelf/troutbot ``` -
- -
-Docker +### Docker ```bash docker build -t troutbot . @@ -275,7 +339,6 @@ docker run -d \ --name troutbot \ -p 127.0.0.1:3000:3000 \ -e GITHUB_TOKEN="ghp_..." \ - -e WEBHOOK_SECRET="your-secret" \ -v $(pwd)/config.ts:/app/config.ts:ro \ --restart unless-stopped \ troutbot @@ -283,17 +346,14 @@ docker run -d \ Multi-stage build, non-root user, built-in health check, `STOPSIGNAL SIGTERM`. -
- -
-Docker Compose +### Docker Compose ```yaml services: troutbot: build: . ports: - - "127.0.0.1:3000:3000" + - '127.0.0.1:3000:3000' env_file: .env volumes: - ./config.ts:/app/config.ts:ro @@ -305,20 +365,17 @@ services: logging: driver: json-file options: - max-size: "10m" - max-file: "3" + max-size: '10m' + max-file: '3' ``` -
- -
-systemd +### Systemd Create `/etc/systemd/system/troutbot.service`: ```ini [Unit] -Description=Troutbot GitHub Webhook Bot +Description=Troutbot GitHub Bot After=network.target [Service] @@ -345,10 +402,9 @@ sudo systemctl daemon-reload sudo systemctl enable --now troutbot ``` -
+### Reverse Proxy (nginx) -
-Reverse Proxy (nginx) +Only needed for webhook mode: ```nginx server { @@ -369,7 +425,7 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # Optional: nginx-level rate limiting + # Optional: nginx-level rate limiting for webhooks # limit_req_zone $binary_remote_addr zone=webhook:10m rate=10r/s; # location /webhook { # limit_req zone=webhook burst=20 nodelay; @@ -378,21 +434,23 @@ server { } ``` -
- ## API Endpoints + + | Method | Path | Description | | -------- | ------------- | ---------------------------------------------------------------------------------------- | | `GET` | `/health` | Health check - returns `status`, `uptime` (seconds), `version`, `dryRun`, and `backends` | -| `POST` | `/webhook` | GitHub webhook receiver (rate limited) | +| `POST` | `/webhook` | GitHub webhook receiver (rate limited, webhook mode only) | | `GET` | `/dashboard` | Web UI dashboard with status, events, and config editor | | `GET` | `/api/status` | JSON status: uptime, version, dry-run, backends, repo count | -| `GET` | `/api/events` | Recent webhook events from the in-memory ring buffer | +| `GET` | `/api/events` | Recent events from the in-memory ring buffer | | `DELETE` | `/api/events` | Clear the event ring buffer | | `GET` | `/api/config` | Current runtime configuration as JSON | | `PUT` | `/api/config` | Partial config update: deep-merges, validates, and applies in-place | + + ## Dashboard & Runtime API Troutbot ships with a built-in web dashboard and JSON API for monitoring and @@ -405,9 +463,8 @@ running). The dashboard provides: - **Status card** - uptime, version, dry-run state, active backends, and repo count. Auto-refreshes every 30 seconds. -- **Event log** - table of recent webhook events showing repo, PR/issue number, - action, impact rating, and confidence score. Keeps the last 100 events in - memory. +- **Event log** - table of recent events showing repo, PR/issue number, action, + impact rating, and confidence score. Keeps the last 100 events in memory. - **Config editor** - read-only JSON view of the current runtime config with an "Edit" toggle that lets you modify and save changes without restarting. @@ -441,8 +498,8 @@ original config remains unchanged if validation fails. ### Event Buffer API -The event buffer stores the last 100 processed webhook events in memory. Events -are lost on restart. +The event buffer stores the last 100 processed events in memory (from both +webhooks and polling). Events are lost on restart. ```bash # List recent events