Skip to content

Commit f1eaf47

Browse files
authored
feat: vicinae extension reviewer (#3)
* feat: start working on AI reviewer * feat: add extension reviewing agent * refactor: cleanup * fix: race conditions * refactor: code quality pass
1 parent a4af88d commit f1eaf47

29 files changed

Lines changed: 2219 additions & 66 deletions

File tree

.dockerignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ dev.db
3232
dev.db-journal
3333

3434
# Prisma
35-
prisma/migrations/
3635
*.db
3736
*.db-journal
3837

.env.example

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@ API_SECRET="your-secret-key-here"
99
# Create at: https://github.com/settings/tokens (no scopes needed for public user info)
1010
GITHUB_TOKEN=
1111

12+
# GitHub AI review (optional)
13+
# Fine-grained PAT for the reviewer account. Grant Contents: read,
14+
# Pull requests: read/write, and Issues: read/write on the review repository.
15+
CODEX_REVIEW_ENABLED=false
16+
GITHUB_REVIEW_REPOSITORY=vicinaehq/extensions
17+
GITHUB_REVIEW_MAINTAINER=aurelleb
18+
GITHUB_PAT=
19+
# Secret configured on the repository webhook pointing to /webhooks/github.
20+
GITHUB_WEBHOOK_SECRET=
21+
# Persistent directory containing the subscription login created by `codex login`.
22+
CODEX_REVIEW_HOME=/app/data/codex
23+
# Optional; leave unset to use the subscription's default model.
24+
CODEX_REVIEW_MODEL=
25+
# Defaults to high. Use medium for faster reviews with the same policy context.
26+
CODEX_REVIEW_REASONING_EFFORT=high
27+
# Maximum duration of one Codex turn. Defaults to 15 minutes.
28+
CODEX_REVIEW_TIMEOUT_MS=900000
29+
1230
# Upload Configuration
1331
MAX_UPLOAD_SIZE=10485760 # 10MB in bytes
1432

.github/workflows/ci.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@ jobs:
2929
- name: Generate Prisma Client
3030
run: bun prisma generate
3131

32+
- name: Validate database migrations
33+
run: bun prisma migrate deploy
34+
3235
- name: Type check
3336
run: bun run type-check
3437

35-
- name: Lint
36-
run: bun run lint || echo "No lint script found, skipping..."
37-
continue-on-error: true
38+
- name: Check formatting and lint
39+
run: bun run check
3840

3941
- name: Test
40-
run: bun test || echo "No tests found, skipping..."
41-
continue-on-error: true
42+
run: bun test

.github/workflows/deploy.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
deploy:
1313
name: Deploy to Server
1414
runs-on: ubuntu-latest
15-
if: ${{ github.event.workflow_run.conclusion == 'success' }}
15+
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
1616
environment:
1717
name: production
1818
url: https://${{ vars.DOMAIN }}
@@ -43,13 +43,17 @@ jobs:
4343
# Run migrations
4444
bun run prisma-deploy
4545
46+
# Keep the deployed systemd unit in sync with the repository.
47+
sudo install -m 0644 extra/vicinae.service /etc/systemd/system/vicinae.service
48+
sudo systemctl daemon-reload
4649
sudo systemctl restart vicinae
4750
4851
# Wait for service to be healthy
4952
echo "Waiting for service to start..."
5053
sleep 5
5154
52-
# Verify service is running
53-
sudo systemctl is-active --quiet vicinae && echo "✅ Service is running" || echo "❌ Service failed to start"
55+
# Verify both the process and HTTP server.
56+
sudo systemctl is-active --quiet vicinae
57+
curl --fail --silent --show-error http://127.0.0.1:3000/ >/dev/null
5458
5559
echo "Deployment completed successfully!"

Dockerfile

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,23 @@ COPY package.json bun.lock* ./
1515
RUN bun install --frozen-lockfile
1616

1717
COPY . .
18-
COPY --from=deps /app/node_modules ./node_modules
1918

20-
# Generate Prisma client
21-
RUN bun prisma generate
19+
# Generate Prisma client (generation validates the configured datasource but does
20+
# not create or access this temporary database).
21+
RUN DATABASE_URL=file:/tmp/build.db bun prisma generate
2222

2323
# Production stage - minimal runtime image
2424
FROM oven/bun:1-alpine AS production
2525
WORKDIR /app
2626

27-
# Install sqlite3 for runtime
28-
RUN apk add --no-cache sqlite
29-
3027
# Copy dependencies and built artifacts
3128
COPY --from=deps /app/node_modules ./node_modules
3229
COPY --from=build /app/src ./src
3330
COPY --from=build /app/prisma ./prisma
3431
COPY --from=build /app/package.json ./package.json
3532

3633
# Create storage and analytics data directories
37-
RUN mkdir -p /app/storage /app/data
34+
RUN mkdir -p /app/storage /app/data/codex
3835
ENV ANALYTICS_DB_PATH=/app/data/analytics.duckdb
3936

4037
# Expose port

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,54 @@ bun prisma migrate dev
4444
```sh
4545
bun run dev
4646
```
47+
48+
## AI-assisted pull request reviews
49+
50+
The backend welcomes extension contributors and automatically runs a Codex review when a non-draft pull request is opened, marked ready, reopened, or updated. Blocking findings produce `REQUEST_CHANGES`; a clean review produces `APPROVE` and marks the PR `human-reviewable`. An organization member or repository collaborator can retry by mentioning the reviewer account with a comment containing only `@<reviewer> review`.
51+
52+
The intended repository rule requires two approvals: the reviewer's automated extension-policy approval and a final Code Owner approval from a Vicinae maintainer. Enable stale-approval dismissal so every new commit must pass both reviewers again.
53+
54+
### GitHub reviewer account
55+
56+
Create a fine-grained personal access token for the dedicated reviewer account, limited to `vicinaehq/extensions`, with:
57+
58+
- Contents: read
59+
- Pull requests: read and write
60+
- Issues: read and write
61+
- Metadata: read (automatically granted)
62+
63+
Add a repository webhook for Pull request and Issue comment events pointing to `https://store.vicinae.dev/webhooks/github`. Configure the same secret as `GITHUB_WEBHOOK_SECRET`.
64+
65+
Set `GITHUB_PAT`, `GITHUB_WEBHOOK_SECRET`, `GITHUB_REVIEW_REPOSITORY`, and `GITHUB_REVIEW_MAINTAINER`. The backend discovers the reviewer login from the PAT, verifies every webhook delivery, and accepts the strict `@<reviewer> review` command only from an organization member or repository collaborator.
66+
67+
The reviewer maintains one welcome/status comment and the following labels:
68+
69+
- `ai-reviewing`
70+
- `ai-changes-requested`
71+
- `human-reviewable`
72+
- `ai-review-failed`
73+
74+
It mentions `GITHUB_REVIEW_MAINTAINER` once per commit when the automated review transitions to approved.
75+
76+
### Codex subscription
77+
78+
Keep a dedicated, persistent Codex home and authenticate it with the Codex for OSS account:
79+
80+
```sh
81+
CODEX_HOME=/app/data/codex bun node_modules/@openai/codex/bin/codex.js login --device-auth
82+
```
83+
84+
In Docker, run that command inside the backend container and persist `/app/data`. Then deploy the database migration and enable the worker:
85+
86+
```sh
87+
bun prisma migrate deploy
88+
```
89+
90+
```env
91+
CODEX_REVIEW_ENABLED=true
92+
CODEX_REVIEW_HOME=/app/data/codex
93+
CODEX_REVIEW_REASONING_EFFORT=high
94+
CODEX_REVIEW_TIMEOUT_MS=900000
95+
```
96+
97+
Each job uses an ephemeral directory containing only the trusted extension-reviewer skill, PR diff, changed extension files, and the pinned `@vicinae/api` TypeScript declarations. Package runtime code is not exposed or executed. The reviewer verifies API recommendations against those declarations, recommends compatible upgrades, and can attach one-click GitHub suggested changes for small exact replacements. The Codex SDK receives a sanitized environment and a least-privilege permission profile: model-generated commands can read only minimal runtime paths and the ephemeral review workspace, with no filesystem writes, approvals, command network access, or web search. The private Codex state directory remains outside that profile. The Docker image includes Bubblewrap for Linux enforcement.

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.5.6/schema.json",
33
"vcs": {
44
"enabled": true,
55
"clientKind": "git",

0 commit comments

Comments
 (0)