main
md 125 lines 6.06 KB
Rendered Raw
1 # siGit Code GitHub App — automatic PR reviews
2
3 The hosted GitHub App behind siGit Code's pull-request reviews. Users install
4 the App on their github.com repos; every reviewable pull-request event hits
5 `POST /github/webhooks`, and `GithubPrReviewJob` posts a walkthrough summary
6 plus line-level comments on the PR, powered by Onde Cloud inference.
7
8 ## How it fits together
9
10 ```
11 github.com ──webhook──▶ GithubWebhooksController ──enqueue──▶ GithubPrReviewJob (Solid Queue)
12 │ verifies X-Hub-Signature-256 │ fetch PR + diff (GithubAppService)
13 │ syncs github_app_installations │ budget/annotate (GithubReviewPrompt)
14 └ <10s, never does slow work │ inference (OndeCloudService)
15 │ validate lines (GithubPatchIndex)
16 └ POST one review back to GitHub
17 ```
18
19 State lives in two tables: `github_app_installations` (mirror of App
20 installations, plus the cached 1-hour installation token) and
21 `github_pr_reviews` (one row per repo + PR + head SHA — the idempotency
22 guard, and the audit trail: status, skip reason, error, comment count).
23
24 ## The registered App
25
26 The App already exists — do not create a second one. It lives under the
27 `getsigit` org:
28
29 - Name: **siGit Code**, App ID **3492899**
30 - Public page: <https://github.com/apps/sigit-code>
31 - Settings: `github.com/organizations/getsigit/settings/apps/sigit-code`
32 - Homepage URL: `https://sigit.si/code`
33 - Webhook URL: `https://sigit.si/github/webhooks`
34 - Permissions: **Pull requests: Read and write**, **Contents: Read-only**,
35 **Metadata: Read-only** (mandatory)
36 - Subscribed events: **Pull request**. (`installation` and
37 `installation_repositories` are always delivered to App webhooks; there is
38 no checkbox for them.)
39
40 If you ever register a replacement from scratch, mirror those settings, set
41 "Where can this App be installed" to **Any account**, generate a webhook
42 secret (`bin/rails secret | head -c 48`), and **Generate a private key**
43 (downloads a `.pem`) for the config below.
44
45 ## Configuration
46
47 Preferred in production: Rails credentials, because the private key is
48 multi-line and prod env vars live in the `git` user's `~/.profile`, which is
49 single-line only:
50
51 ```yaml
52 github_app:
53 app_id: "3492899"
54 webhook_secret: "..."
55 private_key: |
56 -----BEGIN RSA PRIVATE KEY-----
57 ...
58 -----END RSA PRIVATE KEY-----
59 ```
60
61 **Edit credentials locally and commit — never on the server.**
62 `config/credentials.yml.enc` is git-tracked, and the deploy hook's
63 `git checkout -f main` overwrites the server copy, silently deleting any
64 server-side edit on the next deploy (this happened: the App credentials were
65 added on the server on 2026-07-05 and wiped by the v1.1.0 deploy the next
66 day). Run `EDITOR=nano bin/rails credentials:edit` in a local checkout that
67 has `config/master.key`, paste the block, commit the `.enc`, and deploy.
68
69 When pasting the key, indent every PEM line to match `private_key: |`.
70 Verify after deploy without booting the app:
71 `bin/rails runner 'puts GithubAppService.configured?'` on the server — and
72 expect `POST /github/webhooks` to return 401 (not 503) to an unsigned probe.
73 Note the `webhook_secret` here is separate from `stripe.webhook_secret`; they
74 share a key name but live under different top-level parents.
75
76 ENV fallback (dev, or single-line values): `GITHUB_APP_ID`,
77 `GITHUB_APP_WEBHOOK_SECRET`, and `GITHUB_APP_PRIVATE_KEY` with literal `\n`
78 escapes in place of newlines. See `.env.example`.
79
80 Switches:
81
82 - `SIGIT_GITHUB_REVIEWS_ENABLED=false` — the kill switch. Stops new enqueues
83 *and* drains already-queued jobs as no-ops (the job re-checks). Reviews are
84 on by default whenever the App credentials are configured.
85 - `SIGIT_GITHUB_REVIEWS_MODEL` — Onde tier for reviews (default `onde-large`).
86
87 ## Production rollout
88
89 The deploy is the usual `git push` to the bare-repo hook (see
90 `.agents/skills/deployment/SKILL.md`), but this feature is the first user of
91 Solid Queue, so on the first deploy:
92
93 1. Verify the queue schema loaded: the hook's `db:prepare` should load
94 `db/queue_schema.rb` into `sigitsi_production_queue` (that DB exists but
95 was empty — a known issue in the deployment skill). Check with
96 `psql sigitsi_production_queue -c '\dt'` — expect `solid_queue_*` tables.
97 2. **Amend the post-receive hook before relying on `bin/jobs start`**: the
98 hook starts a jobs supervisor on every deploy but never stops the previous
99 one, so supervisors stack. Add something like
100 `pkill -f 'solid_queue' || true` before the `bin/jobs start` line.
101 3. App credentials are already in production credentials (see Configuration);
102 restart Puma after the deploy so the new code picks them up.
103 4. Install the App on a test repo, open a PR, and watch:
104 - the jobs log (`output-jobs.log`) for the review run,
105 - the `github_pr_reviews` row (`status`, `skip_reason`, `error_message`),
106 - the PR itself for the summary + line comments.
107
108 ## Operational notes
109
110 - **GitHub never retries webhook deliveries** (10-second timeout, fire-once).
111 A missed delivery is recovered by the next push (`synchronize`) or manually:
112 App settings → Advanced → Recent Deliveries → Redeliver (kept 30 days).
113 - **Skipped, not failed**: drafts, closed/superseded PRs, PRs over the size
114 budget (> `GithubReviewPrompt::MAX_FILES` files or nothing reviewable) are
115 recorded as `skipped` with a `skip_reason`; over-budget PRs get a short
116 explanatory comment instead of a review.
117 - **422 from GitHub on review creation** means a comment targeted a line
118 outside the diff. The job degrades automatically: full review →
119 summary-only review → plain issue comment. If these show up in the logs,
120 look at `GithubPatchIndex` first.
121 - **One review per head SHA**: redelivered webhooks and rapid pushes collapse
122 via the unique `(repo_full_name, pr_number, head_sha)` index; a newer push
123 supersedes queued reviews of older SHAs.
124 - Reviews are **ungated** in v1 — any installation gets them. Billing or
125 account linking is future work.