1. Webhooks: GitHub Pushes (No Polling)
The orchestrator does not poll GitHub. GitHub pushes events to the orchestrator over HTTP when PR-related events happen.- No polling → no polling interval and no rate-limit risk from repeated API calls for “check for new PRs.”
- Rate limits that do apply are the normal GitHub API limits when the orchestrator calls back GitHub (e.g. post/update PR comments, list webhooks). Those are standard REST limits; webhook delivery is pushed by GitHub and doesn’t consume polling-style quota.
How the webhook URL gets on GitHub (you didn’t type it in)
You don’t manually add the webhook in GitHub’s UI. The CLI creates it via the GitHub API when you run:- Reads your config (including
github.repositoriesandgithub.webhook_secret). - For each repo, calls GitHub’s Create a webhook API.
- Sets the webhook URL to
http://<server-ip>/webhook/githuband the secret to your configuredwebhook_secret. - Subscribes to the
pull_requestevent.
admin:repo_hook) and the repo list during preview init.
End-to-end webhook path
- Nginx listens on port 80 and proxies
/webhook/githubto the orchestrator (e.g.localhost:3000). - Orchestrator (Express) serves
POST /webhook/github, verifies the signature withGITHUB_WEBHOOK_SECRET, then callsWebhookHandler.handleWebhook(payload).
2. High-level architecture
- Inbound: GitHub → Nginx (80) → Orchestrator (3000).
- Orchestrator drives clone/build/run via Docker and writes per-PR nginx configs.
- Traffic to previews: Browser → Nginx
/pr-{number}/→ proxy to container port (8000 + prNumber).
3. Spinning up containers per preview
When the webhook payload hasaction: opened or reopened, the orchestrator runs a deploy flow. When action: synchronize, it runs an update flow (same host, rebuild/restart). Containers are one set per PR (app + DB), not shared.
Deploy flow (new PR)
- Ports: App =
8000 + prNumber, DB =9000 + prNumber(see Implementation plan). - Work dir:
/opt/preview-deployments/pr-<number>/(orDEPLOYMENTS_DIR); clone lives there, anddocker composeruns in that directory. - Compose file: Generated from Handlebars templates (
docker-compose.nestjs.yml.hbsordocker-compose.go.yml.hbs) withprNumber,appPort,dbPort. - Health: Orchestrator polls
http://localhost:<appPort>/health(e.g. every 5s, up to 60s) before marking the preview as up and updating the PR comment.
Container layout per PR
- Each PR gets its own app and DB containers and ports; no sharing between PRs.
- Nginx includes configs from
/etc/nginx/preview-configs/; each file defineslocation /pr-<number>/andproxy_passto the corresponding app port.
Update and cleanup
- Update (synchronize): Same
pr-<number>work dir;git fetch+git reset --hard <commitSha>, thendocker compose up -d --build, health check, update deployment record and PR comment. - Cleanup (closed or TTL):
docker compose down -v, remove work dir, remove nginx config, reload nginx,tracker.deleteDeploymentand release ports.
4. Where it lives in the repo
| Concern | Where |
|---|---|
| Webhook HTTP endpoint | orchestrator/src/index.ts → POST /webhook/github |
| Signature check + routing by action | orchestrator/src/webhook-handler.ts |
| Clone, build, compose, health | orchestrator/src/docker-manager.ts |
| Per-PR nginx config + reload | orchestrator/src/nginx-manager.ts |
| Creating the webhook on GitHub | cli/src/commands/setup.ts + cli/src/utils/github.ts |
Nginx proxy for /webhook/github | ansible/roles/nginx/templates/nginx.conf.j2 (default server: location /webhook/ → orchestrator port) |