pierre-review 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/api/routes/prs.js +6 -2
- package/dist/api/routes/threads.js +4 -2
- package/dist/config.js +12 -0
- package/dist/db/cleanup.js +9 -0
- package/dist/db/migrations/0009_lean_bodies.sql +13 -0
- package/dist/db/migrations/0010_lean_bodies_nullable.sql +56 -0
- package/dist/db/migrations/meta/_journal.json +14 -0
- package/dist/db/migrations-pg/0001_dizzy_mockingbird.sql +3 -0
- package/dist/db/migrations-pg/meta/0001_snapshot.json +2143 -0
- package/dist/db/migrations-pg/meta/_journal.json +7 -0
- package/dist/db/queries.js +10 -4
- package/dist/db/schema.pg.js +6 -2
- package/dist/db/schema.sqlite.js +8 -2
- package/dist/github/queries.js +97 -8
- package/dist/sync/hydrate-detail.js +163 -0
- package/dist/sync/upsert.js +36 -15
- package/package.json +1 -1
- package/public/assets/{index-C8dTw5tB.js → index-BfDmgajC.js} +87 -87
- package/public/assets/{index-D3qeT16k.css → index-C6QWfaSU.css} +1 -1
- package/public/index.html +2 -2
- package/public-landing/assets/{index-DyEMCLrl.js → index-C9adkFFb.js} +1 -1
- package/public-landing/index.html +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,7 @@ pierre-review [options]
|
|
|
55
55
|
| `--no-open` | `NO_OPEN` | — | Don't open the browser on start. |
|
|
56
56
|
| `--port <n>` | `PORT` | `4000` | Port to listen on. |
|
|
57
57
|
| `--db <path>` | `DATABASE_URL` | `~/.pierre-review/pierre-review.sqlite` | SQLite DB path. |
|
|
58
|
+
| — | `PERSIST_BODIES` | `false` | Store full comment/PR text in the DB instead of loading it on demand (larger DB, but PR detail works fully offline). |
|
|
58
59
|
| `-h`, `--help` | — | — | Show usage. |
|
|
59
60
|
|
|
60
61
|
Examples:
|
|
@@ -83,6 +84,12 @@ repositories you want to watch from the in-app picker; the app syncs their PR
|
|
|
83
84
|
activity (full backfill on first sync, incremental every few minutes thereafter)
|
|
84
85
|
into your local DB and renders it as a timeline.
|
|
85
86
|
|
|
87
|
+
To keep the database small and backfills fast, bulky text — PR/comment/review
|
|
88
|
+
bodies and diff hunks — isn't stored; it's fetched from GitHub (using your `gh`
|
|
89
|
+
token) the first time you open a PR and cached in your browser, so re-opening an
|
|
90
|
+
unchanged PR is instant and offline. Set `PERSIST_BODIES=true` to store that text
|
|
91
|
+
locally instead — a larger database, but PR detail then works fully offline.
|
|
92
|
+
|
|
86
93
|
## License
|
|
87
94
|
|
|
88
95
|
MIT
|
package/dist/api/routes/prs.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getPrDetail, markPrViewed } from '../../db/queries.js';
|
|
2
|
+
import { hydratePrDetail } from '../../sync/hydrate-detail.js';
|
|
2
3
|
import { accountIdOf } from '../plugins/auth.js';
|
|
3
4
|
const idParamSchema = {
|
|
4
5
|
params: {
|
|
@@ -18,12 +19,15 @@ const markViewedSchema = {
|
|
|
18
19
|
export async function prRoutes(app) {
|
|
19
20
|
app.get('/api/prs/:id', { schema: idParamSchema }, async (req, reply) => {
|
|
20
21
|
const { id } = req.params;
|
|
21
|
-
const
|
|
22
|
+
const accountId = accountIdOf(req);
|
|
23
|
+
const pr = await getPrDetail(id, accountId);
|
|
22
24
|
if (!pr) {
|
|
23
25
|
reply.status(404);
|
|
24
26
|
return { error: 'NotFound', message: `PR ${id} not found` };
|
|
25
27
|
}
|
|
26
|
-
|
|
28
|
+
// Cloud lean mode: fill in bulky text from GitHub (no-op in local). The client
|
|
29
|
+
// caches the result in IndexedDB keyed by updatedAt so unchanged PRs don't refetch.
|
|
30
|
+
return hydratePrDetail(pr, accountId);
|
|
27
31
|
});
|
|
28
32
|
// Record that the local user has seen this PR up to `sha` (defaults to the
|
|
29
33
|
// current head). Clears "new since last viewed" badges.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getThreadDetail } from '../../db/queries.js';
|
|
2
|
+
import { hydrateThreadDetail } from '../../sync/hydrate-detail.js';
|
|
2
3
|
import { accountIdOf } from '../plugins/auth.js';
|
|
3
4
|
const idParamSchema = {
|
|
4
5
|
params: {
|
|
@@ -10,12 +11,13 @@ const idParamSchema = {
|
|
|
10
11
|
export async function threadRoutes(app) {
|
|
11
12
|
app.get('/api/threads/:id', { schema: idParamSchema }, async (req, reply) => {
|
|
12
13
|
const { id } = req.params;
|
|
13
|
-
const
|
|
14
|
+
const accountId = accountIdOf(req);
|
|
15
|
+
const thread = await getThreadDetail(id, accountId);
|
|
14
16
|
if (!thread) {
|
|
15
17
|
reply.status(404);
|
|
16
18
|
return { error: 'NotFound', message: `Thread ${id} not found` };
|
|
17
19
|
}
|
|
18
|
-
return thread;
|
|
20
|
+
return hydrateThreadDetail(thread, accountId);
|
|
19
21
|
});
|
|
20
22
|
}
|
|
21
23
|
//# sourceMappingURL=threads.js.map
|
package/dist/config.js
CHANGED
|
@@ -52,6 +52,18 @@ export const config = {
|
|
|
52
52
|
isCloud,
|
|
53
53
|
// Drives the DB driver + schema selection in client.ts.
|
|
54
54
|
dbDialect: (isCloud ? 'postgres' : 'sqlite'),
|
|
55
|
+
// ---- Lean storage (both modes by default) ----
|
|
56
|
+
// When false (the default), sync does NOT persist bulky user-authored text —
|
|
57
|
+
// comment / review / PR bodies, review-comment diff hunks, commit messages, and
|
|
58
|
+
// the per-job checkRuns JSON (the ci_status summary enum is kept). That text is
|
|
59
|
+
// regenerable from GitHub (and duplicated per tenant in cloud), so it's the
|
|
60
|
+
// dominant storage cost; it's hydrated on demand when a PR/thread is opened
|
|
61
|
+
// (sync/hydrate-detail.ts, via the gh-CLI token locally or the OAuth token in
|
|
62
|
+
// cloud) and cached in the browser. Not storing it also shrinks the DB and the
|
|
63
|
+
// sync payload, speeding up an initial backfill. Set PERSIST_BODIES=true to store
|
|
64
|
+
// full bodies instead — instant, fully-offline detail at the cost of a larger DB
|
|
65
|
+
// (e.g. a local instance used without network).
|
|
66
|
+
persistBodies: process.env.PERSIST_BODIES === 'true',
|
|
55
67
|
// Postgres connection string (cloud only). Empty in local mode.
|
|
56
68
|
databaseUrl: process.env.DATABASE_URL ?? '',
|
|
57
69
|
port: intFromEnv('PORT', 4000),
|
package/dist/db/cleanup.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { and, eq, inArray, isNull, or, sql } from 'drizzle-orm';
|
|
2
2
|
import { db, schema } from './client.js';
|
|
3
|
+
import { config } from '../config.js';
|
|
3
4
|
// One-time / idempotent maintenance run at startup.
|
|
4
5
|
//
|
|
5
6
|
// GitHub wraps inline-only reviews in an empty "commented" review, which the
|
|
@@ -12,6 +13,14 @@ import { db, schema } from './client.js';
|
|
|
12
13
|
// Written as portable drizzle (no raw better-sqlite3 / db.execute) so it runs on
|
|
13
14
|
// both dialects; the count comes from `.returning().length` (dialect-neutral).
|
|
14
15
|
export async function cleanupRedundantReviewEvents() {
|
|
16
|
+
// In cloud "lean storage" mode (config.persistBodies false) reviews.body is
|
|
17
|
+
// always null, so the body-emptiness signal this relies on is unavailable — and
|
|
18
|
+
// unnecessary: persistPr already only emits review_submitted for *substantive*
|
|
19
|
+
// reviews (it checks the in-memory GraphQL body, not the stored one), and cloud
|
|
20
|
+
// starts empty, so there are no wrapper events to remove. Skipping here avoids
|
|
21
|
+
// wrongly deleting markers for substantive "commented" reviews.
|
|
22
|
+
if (!config.persistBodies)
|
|
23
|
+
return 0;
|
|
15
24
|
const { events, reviews } = schema;
|
|
16
25
|
const emptyCommentedReviews = db
|
|
17
26
|
.select({ id: reviews.id })
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Lean cloud storage groundwork (additive).
|
|
2
|
+
--
|
|
3
|
+
-- Adds review_comments.excerpt: a short (~160 char) preview of the comment body,
|
|
4
|
+
-- kept even when the full body is dropped in cloud "lean storage" mode so the
|
|
5
|
+
-- triage path (getMyTurn / getThreadsAwaiting) and graceful UI degradation work
|
|
6
|
+
-- without a network round trip. Local mode still stores the full body.
|
|
7
|
+
--
|
|
8
|
+
-- Note: in the Drizzle schema review_comments.body / pr_comments.body are now
|
|
9
|
+
-- nullable (to allow cloud's lean Postgres rows to omit them). SQLite cannot drop
|
|
10
|
+
-- a NOT NULL via ALTER, but local mode always persists bodies (config.persistBodies
|
|
11
|
+
-- is true whenever the driver is sqlite), so the live NOT NULL constraint is never
|
|
12
|
+
-- exercised — only the additive column below is needed here.
|
|
13
|
+
ALTER TABLE `review_comments` ADD `excerpt` text;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
-- Make review_comments.body and pr_comments.body NULLABLE for lean storage
|
|
2
|
+
-- (config.persistBodies false, now the default in both modes): the full body is
|
|
3
|
+
-- dropped from storage and hydrated on demand, leaving review_comments.excerpt
|
|
4
|
+
-- (from 0009) as the kept preview. SQLite can't drop a NOT NULL via ALTER, so the
|
|
5
|
+
-- two tables are rebuilt. This is safe: no other table has a foreign key pointing
|
|
6
|
+
-- at them, and the copied data already satisfies their own (unchanged) outbound
|
|
7
|
+
-- FKs. The rebuilt tables keep the excerpt column and every current index.
|
|
8
|
+
CREATE TABLE `__new_review_comments` (
|
|
9
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
10
|
+
`github_node_id` text NOT NULL,
|
|
11
|
+
`thread_id` integer NOT NULL,
|
|
12
|
+
`pr_id` integer NOT NULL,
|
|
13
|
+
`author_id` integer,
|
|
14
|
+
`body` text,
|
|
15
|
+
`excerpt` text,
|
|
16
|
+
`diff_hunk` text,
|
|
17
|
+
`database_id` text,
|
|
18
|
+
`created_at` integer NOT NULL,
|
|
19
|
+
FOREIGN KEY (`thread_id`) REFERENCES `review_threads`(`id`) ON UPDATE no action ON DELETE no action,
|
|
20
|
+
FOREIGN KEY (`pr_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action,
|
|
21
|
+
FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
|
22
|
+
);
|
|
23
|
+
--> statement-breakpoint
|
|
24
|
+
INSERT INTO `__new_review_comments` (`id`, `github_node_id`, `thread_id`, `pr_id`, `author_id`, `body`, `excerpt`, `diff_hunk`, `database_id`, `created_at`)
|
|
25
|
+
SELECT `id`, `github_node_id`, `thread_id`, `pr_id`, `author_id`, `body`, `excerpt`, `diff_hunk`, `database_id`, `created_at` FROM `review_comments`;
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
DROP TABLE `review_comments`;
|
|
28
|
+
--> statement-breakpoint
|
|
29
|
+
ALTER TABLE `__new_review_comments` RENAME TO `review_comments`;
|
|
30
|
+
--> statement-breakpoint
|
|
31
|
+
CREATE INDEX `rc_thread_idx` ON `review_comments` (`thread_id`);
|
|
32
|
+
--> statement-breakpoint
|
|
33
|
+
CREATE UNIQUE INDEX `rc_pr_node` ON `review_comments` (`pr_id`,`github_node_id`);
|
|
34
|
+
--> statement-breakpoint
|
|
35
|
+
CREATE TABLE `__new_pr_comments` (
|
|
36
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
37
|
+
`github_node_id` text NOT NULL,
|
|
38
|
+
`pr_id` integer NOT NULL,
|
|
39
|
+
`author_id` integer,
|
|
40
|
+
`body` text,
|
|
41
|
+
`database_id` text,
|
|
42
|
+
`created_at` integer NOT NULL,
|
|
43
|
+
FOREIGN KEY (`pr_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action,
|
|
44
|
+
FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
|
45
|
+
);
|
|
46
|
+
--> statement-breakpoint
|
|
47
|
+
INSERT INTO `__new_pr_comments` (`id`, `github_node_id`, `pr_id`, `author_id`, `body`, `database_id`, `created_at`)
|
|
48
|
+
SELECT `id`, `github_node_id`, `pr_id`, `author_id`, `body`, `database_id`, `created_at` FROM `pr_comments`;
|
|
49
|
+
--> statement-breakpoint
|
|
50
|
+
DROP TABLE `pr_comments`;
|
|
51
|
+
--> statement-breakpoint
|
|
52
|
+
ALTER TABLE `__new_pr_comments` RENAME TO `pr_comments`;
|
|
53
|
+
--> statement-breakpoint
|
|
54
|
+
CREATE INDEX `prc_pr_idx` ON `pr_comments` (`pr_id`);
|
|
55
|
+
--> statement-breakpoint
|
|
56
|
+
CREATE UNIQUE INDEX `prc_pr_node` ON `pr_comments` (`pr_id`,`github_node_id`);
|
|
@@ -64,6 +64,20 @@
|
|
|
64
64
|
"when": 1780700000000,
|
|
65
65
|
"tag": "0008_multitenant_accounts",
|
|
66
66
|
"breakpoints": true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"idx": 9,
|
|
70
|
+
"version": "6",
|
|
71
|
+
"when": 1780800000000,
|
|
72
|
+
"tag": "0009_lean_bodies",
|
|
73
|
+
"breakpoints": true
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"idx": 10,
|
|
77
|
+
"version": "6",
|
|
78
|
+
"when": 1780800000001,
|
|
79
|
+
"tag": "0010_lean_bodies_nullable",
|
|
80
|
+
"breakpoints": true
|
|
67
81
|
}
|
|
68
82
|
]
|
|
69
83
|
}
|