rvlib-shipkit 0.38.0 → 1.0.0
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/CHANGELOG.md +371 -16
- package/README.md +26 -14
- package/package.json +2 -2
- package/src/assets/gen/comfyts-gen.ts +27 -10
- package/src/assets/gen/generate-roll.ts +1 -1
- package/src/assets/kit-config.ts +6 -6
- package/src/assets/run-assets.ts +14 -10
- package/src/assets/server/studio-server.ts +6 -5
- package/src/check.ts +343 -0
- package/src/{rules/SK001-package-json-shape.ts → checks/SK001-package-json-shape.check.ts} +44 -46
- package/src/checks/SK002-contracts-fulfillness.check.ts +24 -0
- package/src/checks/SK003-claude-md-shape.check.ts +66 -0
- package/src/checks/SK004-gitignore-policy.check.ts +309 -0
- package/src/{rules/SK005-rvlib-versions.ts → checks/SK005-rvlib-versions.check.ts} +46 -32
- package/src/{rules/SK006-npm-publish-safety.ts → checks/SK006-npm-publish-safety.check.ts} +25 -24
- package/src/{rules/SK007-github-visibility.ts → checks/SK007-github-visibility.check.ts} +20 -14
- package/src/{rules/SK008-reviewer-agent.ts → checks/SK008-reviewer-agent.check.ts} +29 -14
- package/src/{rules/SK009-commit-ai-tells.ts → checks/SK009-commit-ai-tells.check.ts} +24 -21
- package/src/checks/SK010-undeclared-unit.check.ts +111 -0
- package/src/{rules/SK011-sibling-unit-ranges.ts → checks/SK011-sibling-unit-ranges.check.ts} +21 -20
- package/src/{rules/SK012-config-barrel-import.ts → checks/SK012-config-barrel-import.check.ts} +18 -23
- package/src/{rules/SK013-one-way-in-policy.ts → checks/SK013-one-way-in-policy.check.ts} +58 -50
- package/src/{rules/SK014-prose-ste.ts → checks/SK014-prose-ste.check.ts} +27 -13
- package/src/{rules/SK015-nested-repo.ts → checks/SK015-nested-repo.check.ts} +15 -12
- package/src/checks/SK016-legacy-check-shapes.check.ts +182 -0
- package/src/checks/SK017-vscode-hub-task.check.ts +197 -0
- package/src/checks/SK018-test-files-run.check.ts +28 -0
- package/src/checks/SK019-tests-run-in-units.check.ts +76 -0
- package/src/checks/SK020-self-spawning-check.check.ts +147 -0
- package/src/checks/contracts.check.ts +20 -0
- package/src/cli/cmd_Asks.ts +3 -2
- package/src/cli/cmd_Assets.ts +2 -2
- package/src/cli/cmd_Bump.ts +1 -1
- package/src/cli/cmd_Check.ts +6 -6
- package/src/cli/cmd_Checks.ts +95 -0
- package/src/cli/cmd_Ci.ts +48 -13
- package/src/cli/cmd_Commands.ts +2 -6
- package/src/cli/cmd_ConfigKeys.ts +69 -0
- package/src/cli/cmd_Contracts.ts +1 -1
- package/src/cli/cmd_Fleet.ts +6 -2
- package/src/cli/cmd_Hooks.ts +1 -1
- package/src/cli/cmd_Keys.ts +43 -18
- package/src/cli/cmd_Mv.ts +2 -2
- package/src/cli/cmd_Prose.ts +10 -1
- package/src/cli/cmd_Releases.ts +2 -2
- package/src/cli/cmd_Serve.ts +1 -1
- package/src/cli/cmd_Start.ts +6 -6
- package/src/cli/cmd_Status.ts +25 -0
- package/src/cli/cmd_Tui.ts +2 -2
- package/src/cli.ts +7 -4
- package/src/collections/actions.ts +4 -4
- package/src/collections/checks.ts +258 -0
- package/src/collections/collection-types.ts +254 -1
- package/src/collections/comfy.ts +23 -0
- package/src/collections/md-body.ts +14 -0
- package/src/collections/pings.ts +38 -12
- package/src/collections/posts.ts +19 -3
- package/src/collections/providers.ts +198 -91
- package/src/collections/reflections.ts +41 -0
- package/src/collections/state-rules.ts +73 -0
- package/src/collections/test-time.ts +288 -0
- package/src/collections/tests.ts +181 -0
- package/src/collections/wikis.ts +66 -0
- package/src/comfy/comfy-panel.ts +292 -0
- package/src/contracts/apply-contracts.ts +17 -12
- package/src/contracts/contract-types.ts +16 -4
- package/src/contracts/contracts-engine.ts +78 -42
- package/src/contracts/manifests/discord.contract.ts +6 -6
- package/src/contracts/manifests/identity.contract.ts +3 -3
- package/src/contracts/manifests/itch.contract.ts +12 -15
- package/src/contracts/manifests/social.contract.ts +5 -5
- package/src/contracts/manifests/steam.contract.ts +16 -16
- package/src/contracts/run-contracts.ts +4 -3
- package/src/core/attach.ts +96 -0
- package/src/core/canonical-scripts.ts +25 -0
- package/src/core/check-ctx.ts +204 -14
- package/src/core/check-file-names.ts +112 -0
- package/src/core/check-pass.ts +84 -0
- package/src/core/check-runner.ts +796 -0
- package/src/core/config-keys.ts +256 -0
- package/src/core/config-rewrite.ts +77 -0
- package/src/core/discover-checks.ts +166 -90
- package/src/core/discover-legacy-checks.ts +26 -0
- package/src/core/doc-block.ts +13 -0
- package/src/core/floor-resolve.ts +264 -0
- package/src/core/floor.ts +20 -137
- package/src/core/git-bin.ts +24 -0
- package/src/core/git-root.ts +108 -0
- package/src/core/instance.ts +195 -0
- package/src/core/jobs.ts +118 -0
- package/src/core/layout.ts +52 -0
- package/src/core/legacy-shapes.ts +207 -0
- package/src/core/lib-root.ts +5 -0
- package/src/core/locked-write.ts +110 -39
- package/src/core/machine-commit.ts +5 -4
- package/src/core/migrate-layout.ts +557 -0
- package/src/core/motherhome.ts +2 -8
- package/src/core/nesting.ts +4 -12
- package/src/core/one-way-in-policy.ts +73 -17
- package/src/core/outcome-words.ts +45 -0
- package/src/core/package-manager.ts +86 -0
- package/src/core/parallel.ts +21 -0
- package/src/core/process-tree.ts +44 -0
- package/src/core/raw-access.ts +93 -0
- package/src/core/read-set.ts +152 -0
- package/src/core/run-check.ts +155 -213
- package/src/core/run-ci.ts +153 -189
- package/src/core/run-fix.ts +96 -0
- package/src/core/run-fixers.ts +53 -30
- package/src/core/self-update.ts +9 -3
- package/src/core/slots.ts +83 -0
- package/src/core/spawn-timeout.ts +34 -3
- package/src/core/when-block.ts +401 -0
- package/src/core/when.ts +135 -0
- package/src/core/write-status.ts +5 -2
- package/src/hooks/leak-guard.ts +10 -8
- package/src/hooks/one-way-in-guard.ts +13 -5
- package/src/hooks/root-identity.ts +2 -1
- package/src/hooks/run-hooks.ts +35 -23
- package/src/init-repo.ts +39 -23
- package/src/ledger/check-stats.ts +155 -0
- package/src/ledger/check-table.ts +179 -0
- package/src/ledger/db.ts +255 -0
- package/src/ledger/gate-checks.ts +433 -0
- package/src/ledger/git-key.ts +116 -77
- package/src/ledger/git-scan.ts +96 -35
- package/src/ledger/ledger.ts +116 -42
- package/src/ledger/live-runs.ts +111 -0
- package/src/ledger/live-timeline.ts +97 -0
- package/src/ledger/repo-view.ts +73 -0
- package/src/ledger/run-log.ts +448 -0
- package/src/ledger/run-timeline.ts +173 -0
- package/src/ledger/store.ts +23 -12
- package/src/ledger/suite-log.ts +161 -0
- package/src/ledger/toolchain-identity.ts +7 -1
- package/src/ledger/world-cache.ts +111 -0
- package/src/mv/mv-md.ts +16 -6
- package/src/problems/extract.ts +182 -0
- package/src/problems/problem-board.ts +37 -0
- package/src/problems/problem-line.ts +77 -0
- package/src/prose/prose-view.ts +90 -0
- package/src/prose/run-prose.ts +60 -19
- package/src/prose/ste.ts +140 -24
- package/src/reflections/reflections-queue.ts +81 -0
- package/src/release/git-facts.ts +15 -9
- package/src/release/run-bump.ts +6 -5
- package/src/release/run-release.ts +25 -11
- package/src/release/run-releases.ts +2 -2
- package/src/release/unit-gate.ts +7 -54
- package/src/release/unit-resolve.ts +282 -0
- package/src/release/units.ts +33 -217
- package/src/repo-config.ts +155 -85
- package/src/rules/rule.ts +36 -96
- package/src/rules/untrack-generated.ts +5 -4
- package/src/scaffold/files/vscode-tasks.json +10 -31
- package/src/scaffold/jsonc-spans.ts +113 -0
- package/src/scaffold/project-scaffold.ts +16 -43
- package/src/scan.ts +98 -0
- package/src/serve/gate-trigger.ts +148 -0
- package/src/serve/health-schedule.ts +102 -0
- package/src/serve/hub-app/chart-view.ts +179 -0
- package/src/serve/hub-app/check-view.tsx +345 -0
- package/src/serve/hub-app/check-words.ts +164 -0
- package/src/serve/hub-app/checks-svc.ts +280 -0
- package/src/serve/hub-app/clock-svc.ts +33 -0
- package/src/serve/hub-app/col-card.tsx +23 -39
- package/src/serve/hub-app/cols-svc.ts +44 -20
- package/src/serve/hub-app/comfy-svc.ts +171 -0
- package/src/serve/hub-app/comfy-view.tsx +81 -0
- package/src/serve/hub-app/dur.ts +41 -0
- package/src/serve/hub-app/hash-route.ts +121 -0
- package/src/serve/hub-app/header-bar.tsx +117 -0
- package/src/serve/hub-app/health-svc.ts +23 -28
- package/src/serve/hub-app/help.tsx +29 -0
- package/src/serve/hub-app/hub-app.tsx +122 -119
- package/src/serve/hub-app/hub-st.ts +115 -42
- package/src/serve/hub-app/hub-types.ts +117 -2
- package/src/serve/hub-app/hub.css +534 -192
- package/src/serve/hub-app/index.html +2 -0
- package/src/serve/hub-app/live-svc.ts +174 -0
- package/src/serve/hub-app/md.ts +15 -0
- package/src/serve/hub-app/media-svc.ts +4 -1
- package/src/serve/hub-app/media-view.tsx +15 -0
- package/src/serve/hub-app/next-run.ts +68 -0
- package/src/serve/hub-app/project-gutter.css +65 -0
- package/src/serve/hub-app/project-gutter.tsx +128 -0
- package/src/serve/hub-app/projects-svc.ts +226 -0
- package/src/serve/hub-app/prose-hits-st.ts +124 -0
- package/src/serve/hub-app/prose-hits-view.tsx +176 -0
- package/src/serve/hub-app/prose-hits.css +21 -0
- package/src/serve/hub-app/run-strip.ts +47 -0
- package/src/serve/hub-app/runs-tabs.ts +24 -0
- package/src/serve/hub-app/test-view.tsx +107 -0
- package/src/serve/hub-app/tests-svc.ts +62 -0
- package/src/serve/hub-app/tests-view.tsx +238 -0
- package/src/serve/hub-app/time-ago.tsx +34 -0
- package/src/serve/hub-app/timeline-chart.tsx +274 -0
- package/src/serve/hub-app/timeline-geometry.ts +314 -0
- package/src/serve/hub-app/timeline-svc.ts +62 -0
- package/src/serve/hub-app/timeline-view.tsx +245 -0
- package/src/serve/hub-app/tree-build.ts +410 -0
- package/src/serve/hub-app/tree-row.tsx +42 -0
- package/src/serve/hub-app/tree-svc.ts +174 -0
- package/src/serve/hub-app/wikis-view.tsx +53 -0
- package/src/serve/other-repos.ts +79 -0
- package/src/serve/project-gutter-route.ts +350 -0
- package/src/serve/project-gutter.ts +449 -0
- package/src/serve/run-serve.ts +245 -16
- package/src/serve/same-origin.ts +14 -0
- package/src/serve/serve-hub.ts +213 -18
- package/src/shipkit.ts +19 -12
- package/src/smoke/run-smoke.ts +3 -2
- package/src/start-repo.ts +75 -36
- package/src/suite/judge-suites.ts +216 -18
- package/src/suite/kept-timings.ts +58 -0
- package/src/suite/runner-shapes.ts +88 -0
- package/src/suite/suite-cli.ts +147 -24
- package/src/suite/suite-cpu-preload.ts +92 -0
- package/src/suite/test-units.ts +243 -0
- package/src/ts/config.ts +4 -69
- package/src/ts/jsonc.ts +69 -0
- package/src/ts/lex.ts +143 -0
- package/src/ts/rename.ts +73 -10
- package/src/ts/source-graph.ts +28 -8
- package/src/ts/tsconfig-projects.ts +6 -97
- package/src/ts/tsconfig-resolve.ts +108 -0
- package/src/tui/build-steps-panel.ts +61 -0
- package/src/tui/checks-panel.ts +24 -16
- package/src/tui/run-tui.ts +2 -2
- package/src/tui/tui-panel.ts +5 -4
- package/src/wiki/graph-colors.ts +68 -0
- package/src/wiki/graph-mount.ts +7 -9
- package/src/wiki/graph-page.ts +42 -25
- package/src/wiki/graph-positions.ts +105 -0
- package/src/wiki/run-wiki.ts +1 -1
- package/src/wiki/serve-graph.ts +33 -3
- package/src/cli/cmd_Rules.ts +0 -51
- package/src/cli/cmd_Units.ts +0 -82
- package/src/collections/graphs.ts +0 -32
- package/src/contracts/contracts-once.ts +0 -43
- package/src/core/check-suite.ts +0 -302
- package/src/core/repair-contract-dirs.ts +0 -136
- package/src/core/task-runner.ts +0 -339
- package/src/ledger/gate-units.ts +0 -138
- package/src/ledger/unit-table.ts +0 -105
- package/src/rules/SK002-contracts-fulfillness.ts +0 -24
- package/src/rules/SK003-claude-md-shape.ts +0 -70
- package/src/rules/SK004-gitignore-policy.ts +0 -238
- package/src/rules/SK010-undeclared-release-unit.ts +0 -127
- package/src/rules/rule-registry.ts +0 -64
- package/src/rules/run-fix.ts +0 -72
- package/src/tui/tasks-panel.ts +0 -60
package/src/ledger/db.ts
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// THE ONE DB — the file the run record lives in, its schema, and the migration
|
|
2
|
+
// that creates it. `ledger/run-log.ts` owns what a row MEANS; this module owns
|
|
3
|
+
// the file around it.
|
|
4
|
+
//
|
|
5
|
+
// Sweeping is a delete by age, which no concurrent write can be caught
|
|
6
|
+
// inside of: a size sweep that REWRITES a file loses whatever was appended
|
|
7
|
+
// during the rewrite.
|
|
8
|
+
//
|
|
9
|
+
// THREE PROPERTIES, and every one is load bearing:
|
|
10
|
+
// 1. NEVER READ BY THE GATE. Nothing stored here can change what a check does
|
|
11
|
+
// or what it replays, so a corrupt, locked or missing db costs a run its
|
|
12
|
+
// HISTORY and never its verdict. Every failure path out of this module is
|
|
13
|
+
// a null the caller turns into one warn line.
|
|
14
|
+
// 2. NOT THE LEDGER. The keyed store beside it (`ledger/store.ts`) is still
|
|
15
|
+
// one immutable json file per key, which is what a replay reads. This db
|
|
16
|
+
// is the reporting record: what every check cost on every run, red runs
|
|
17
|
+
// included, and what each check last printed.
|
|
18
|
+
// 3. SEVERAL WRITERS AT ONCE is the normal case (three agents in one
|
|
19
|
+
// checkout). WAL, a busy timeout, and one immediate transaction per flush
|
|
20
|
+
// is what makes six gates land every row whole.
|
|
21
|
+
//
|
|
22
|
+
// WHERE. `<git-common-dir>/shipkit/shipkit.sqlite`, beside the store and
|
|
23
|
+
// outside the work tree, so nothing it writes can dirty a diff or reach a
|
|
24
|
+
// commit. Every linked worktree of the repo shares it, the same as the store.
|
|
25
|
+
import { Database } from 'bun:sqlite'
|
|
26
|
+
import { existsSync, mkdirSync } from 'fs'
|
|
27
|
+
import { dirname, join } from 'path'
|
|
28
|
+
import { shipkitRoot } from './store.ts'
|
|
29
|
+
|
|
30
|
+
export const DB_FILE = 'shipkit.sqlite'
|
|
31
|
+
|
|
32
|
+
/** the schema this build speaks, stored in `pragma user_version`. Bumped when
|
|
33
|
+
* a table changes, never when a row's meaning does: the migration is the one
|
|
34
|
+
* place an older file is brought forward.
|
|
35
|
+
*
|
|
36
|
+
* v2 dropped the `kind` column. `task`, `rule` and `check` stopped being a
|
|
37
|
+
* thing a row could carry, and every row is filed under its bare id now. The rows written under the old scheme are DROPPED rather
|
|
38
|
+
* than migrated: they are per checkout timings a fortnight deep at most, and
|
|
39
|
+
* re-keying them would invent a mapping nobody can verify.
|
|
40
|
+
*
|
|
41
|
+
* v3 added `invocations`, one row per command that wrote runs (what ran it,
|
|
42
|
+
* its label, when it started and ended), and `suites`, one row per test
|
|
43
|
+
* suite per run of the suite runner. Both are new tables, so a v2 file keeps
|
|
44
|
+
* every row it holds.
|
|
45
|
+
*
|
|
46
|
+
* v4 names the last lines table `check_last`, keyed by `id`. An older file's
|
|
47
|
+
* table is renamed in place, so its rows stay.
|
|
48
|
+
*
|
|
49
|
+
* v5 adds `live`, one row per run IN FLIGHT: what the runner is doing right
|
|
50
|
+
* now, rewritten as checks start and finish and deleted when the runner
|
|
51
|
+
* returns. A new table, so every older row stays.
|
|
52
|
+
*
|
|
53
|
+
* v6 adds `check_id` to `suites`: which check ran the suite runner that
|
|
54
|
+
* timed it (`test`, `test:<unit>`). An older row keeps '' there, and an older
|
|
55
|
+
* build still inserts, because the column has a default.
|
|
56
|
+
*
|
|
57
|
+
* v7 adds `unit` to `suites`: the unit whose test check timed it
|
|
58
|
+
* (`root`, `rvlib-shipkit`). Same shape as v6: '' on an older row, a default
|
|
59
|
+
* for an older writer.
|
|
60
|
+
*
|
|
61
|
+
* v8 adds three columns to `suites` (`cpu_ms`, null where no CPU figure is
|
|
62
|
+
* the file's own; `phase`, the runner phase that timed it; `fail_line`, the
|
|
63
|
+
* first failure bun printed for it) and the `suite_runs` table, one row per
|
|
64
|
+
* run of the suite runner: its wall time, its phases, its workers. Defaults
|
|
65
|
+
* again, so an older writer still inserts and every older row stays.
|
|
66
|
+
*
|
|
67
|
+
* v9 adds `phase_ms` to `suites`: the file's wall time in the phase that ran
|
|
68
|
+
* it, which `ms` is not once an alone rerun judged it. Same shape again. */
|
|
69
|
+
export const SCHEMA_VERSION = 9
|
|
70
|
+
|
|
71
|
+
/** how long a writer waits on another process's transaction before it gives
|
|
72
|
+
* up and the run says so. Long enough for a whole gate's flush, short enough
|
|
73
|
+
* that nothing waits on a dashboard. */
|
|
74
|
+
export const BUSY_MS = 5_000
|
|
75
|
+
|
|
76
|
+
export const dbPath = (root: string): string => join(shipkitRoot(root), DB_FILE)
|
|
77
|
+
|
|
78
|
+
export const INSERT_RUN = 'insert into runs (run_id, id, at, ms, status, replayed) values (?, ?, ?, ?, ?, ?)'
|
|
79
|
+
|
|
80
|
+
export const UPSERT_INVOCATION =
|
|
81
|
+
'insert into invocations (run_id, kind, label, by, narrowed, started, ended) values (?, ?, ?, ?, ?, ?, ?)' +
|
|
82
|
+
' on conflict(run_id) do update set ended = excluded.ended'
|
|
83
|
+
|
|
84
|
+
export const UPSERT_LIVE =
|
|
85
|
+
'insert into live (run_id, kind, label, by, pid, started, updated, state) values (?, ?, ?, ?, ?, ?, ?, ?)' +
|
|
86
|
+
' on conflict(run_id) do update set updated = excluded.updated, state = excluded.state'
|
|
87
|
+
|
|
88
|
+
export const INSERT_SUITE = 'insert into suites (at, file, ms, status, tier, budget_ms, check_id, unit, cpu_ms, phase, phase_ms, fail_line) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
|
89
|
+
|
|
90
|
+
export const INSERT_SUITE_RUN =
|
|
91
|
+
'insert into suite_runs (at, check_id, unit, tier, wall_ms, setup_ms, workers, files, parallel_ms, parallel_cpu_ms, serial_ms, serial_cpu_ms, serial_files, alone_ms, alone_files, load1, cores)' +
|
|
92
|
+
' values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
|
93
|
+
|
|
94
|
+
export const UPSERT_LAST =
|
|
95
|
+
'insert into check_last (id, at, status, ms, lines) values (?, ?, ?, ?, ?)' +
|
|
96
|
+
' on conflict(id) do update set at = excluded.at, status = excluded.status, ms = excluded.ms, lines = excluded.lines'
|
|
97
|
+
|
|
98
|
+
const SCHEMA = `
|
|
99
|
+
create table if not exists runs (
|
|
100
|
+
run_id text not null,
|
|
101
|
+
id text not null,
|
|
102
|
+
at integer not null,
|
|
103
|
+
ms integer not null,
|
|
104
|
+
status text not null,
|
|
105
|
+
replayed integer not null
|
|
106
|
+
);
|
|
107
|
+
create index if not exists runs_at on runs (at);
|
|
108
|
+
create index if not exists runs_id on runs (id, at);
|
|
109
|
+
create table if not exists check_last (
|
|
110
|
+
id text primary key,
|
|
111
|
+
at integer not null,
|
|
112
|
+
status text not null,
|
|
113
|
+
ms integer not null,
|
|
114
|
+
lines text not null
|
|
115
|
+
);
|
|
116
|
+
create table if not exists invocations (
|
|
117
|
+
run_id text primary key,
|
|
118
|
+
kind text not null,
|
|
119
|
+
label text not null,
|
|
120
|
+
by text not null,
|
|
121
|
+
narrowed integer not null,
|
|
122
|
+
started integer not null,
|
|
123
|
+
ended integer not null
|
|
124
|
+
);
|
|
125
|
+
create index if not exists invocations_started on invocations (started);
|
|
126
|
+
create table if not exists suites (
|
|
127
|
+
at integer not null,
|
|
128
|
+
file text not null,
|
|
129
|
+
ms integer not null,
|
|
130
|
+
status text not null,
|
|
131
|
+
tier text not null,
|
|
132
|
+
budget_ms integer not null,
|
|
133
|
+
check_id text not null default '',
|
|
134
|
+
unit text not null default '',
|
|
135
|
+
cpu_ms integer default null,
|
|
136
|
+
phase text not null default '',
|
|
137
|
+
phase_ms integer default null,
|
|
138
|
+
fail_line text not null default ''
|
|
139
|
+
);
|
|
140
|
+
create index if not exists suites_file on suites (file, at);
|
|
141
|
+
create table if not exists suite_runs (
|
|
142
|
+
at integer not null,
|
|
143
|
+
check_id text not null,
|
|
144
|
+
unit text not null,
|
|
145
|
+
tier text not null,
|
|
146
|
+
wall_ms integer not null,
|
|
147
|
+
setup_ms integer not null,
|
|
148
|
+
workers integer not null,
|
|
149
|
+
files integer not null,
|
|
150
|
+
parallel_ms integer not null,
|
|
151
|
+
parallel_cpu_ms integer default null,
|
|
152
|
+
serial_ms integer default null,
|
|
153
|
+
serial_cpu_ms integer default null,
|
|
154
|
+
serial_files integer not null default 0,
|
|
155
|
+
alone_ms integer not null default 0,
|
|
156
|
+
alone_files integer not null default 0,
|
|
157
|
+
load1 real default null,
|
|
158
|
+
cores integer default null
|
|
159
|
+
);
|
|
160
|
+
create index if not exists suite_runs_check on suite_runs (check_id, at);
|
|
161
|
+
create table if not exists live (
|
|
162
|
+
run_id text primary key,
|
|
163
|
+
kind text not null,
|
|
164
|
+
label text not null,
|
|
165
|
+
by text not null,
|
|
166
|
+
pid integer not null,
|
|
167
|
+
started integer not null,
|
|
168
|
+
updated integer not null,
|
|
169
|
+
state text not null
|
|
170
|
+
);
|
|
171
|
+
`
|
|
172
|
+
|
|
173
|
+
export type Opened = { db: Database }
|
|
174
|
+
|
|
175
|
+
/** open the record, create the schema if this is its first open, and hand back
|
|
176
|
+
* a handle the caller CLOSES. Null for every failure there is: no path, no
|
|
177
|
+
* db and `create: false`, a corrupt file, a locked one, a read only disk. */
|
|
178
|
+
export function openDb(path: string, o: { create?: boolean; busyMs?: number } = {}): Opened | null {
|
|
179
|
+
if (path === '') return null
|
|
180
|
+
const create = o.create !== false
|
|
181
|
+
if (!create && !existsSync(path)) return null
|
|
182
|
+
let db: Database | null = null
|
|
183
|
+
try {
|
|
184
|
+
if (create) mkdirSync(dirname(path), { recursive: true })
|
|
185
|
+
db = new Database(path, { create, readwrite: true })
|
|
186
|
+
const busyMs = Math.max(0, Math.round(o.busyMs ?? BUSY_MS))
|
|
187
|
+
// the timeout goes on FIRST, so every ordinary statement waits on
|
|
188
|
+
// another process's transaction instead of failing
|
|
189
|
+
db.exec(`pragma busy_timeout = ${busyMs}`)
|
|
190
|
+
ensureWal(db, busyMs)
|
|
191
|
+
db.exec('pragma synchronous = NORMAL')
|
|
192
|
+
migrate(db)
|
|
193
|
+
return { db }
|
|
194
|
+
} catch {
|
|
195
|
+
try { db?.close() } catch { /* never opened, or already gone */ }
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** WAL is what lets readers and a writer share the file. Switching INTO it
|
|
201
|
+
* takes an exclusive lock that sqlite refuses WITHOUT consulting the busy
|
|
202
|
+
* handler, so `busy_timeout` does not cover it: six processes opening a fresh
|
|
203
|
+
* db together, one of them loses and throws `database is locked`. Reproduced.
|
|
204
|
+
* So the mode is read first (after any one process has switched it, the
|
|
205
|
+
* switch is not attempted at all), retried briefly, and never fatal: a db
|
|
206
|
+
* left on the rollback journal still writes every row correctly under the
|
|
207
|
+
* busy timeout, it only serialises readers against the writer. */
|
|
208
|
+
function ensureWal(db: Database, busyMs: number): void {
|
|
209
|
+
const mode = (): string => String((db.query('pragma journal_mode').get() as { journal_mode?: string } | null)?.journal_mode ?? '')
|
|
210
|
+
const deadline = Date.now() + Math.min(busyMs, 2_000)
|
|
211
|
+
for (;;) {
|
|
212
|
+
if (mode() === 'wal') return
|
|
213
|
+
try {
|
|
214
|
+
db.exec('pragma journal_mode = WAL')
|
|
215
|
+
if (mode() === 'wal') return
|
|
216
|
+
} catch { /* another process holds the exclusive lock: look again */ }
|
|
217
|
+
if (Date.now() >= deadline) return
|
|
218
|
+
Bun.sleepSync(5 + Math.floor(Math.random() * 15))
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const userVersion = (db: Database): number => Number((db.query('pragma user_version').get() as { user_version?: number } | null)?.user_version ?? 0)
|
|
223
|
+
|
|
224
|
+
/** create the schema, once. The version is read a SECOND time inside the
|
|
225
|
+
* write lock: two processes can open a fresh db at the same moment, and the
|
|
226
|
+
* loser must find the schema already there rather than drop it. */
|
|
227
|
+
function migrate(db: Database): void {
|
|
228
|
+
if (userVersion(db) >= SCHEMA_VERSION) return
|
|
229
|
+
db.exec('begin immediate')
|
|
230
|
+
try {
|
|
231
|
+
const was = userVersion(db)
|
|
232
|
+
if (was < SCHEMA_VERSION) {
|
|
233
|
+
// the pre-v2 tables carry a `kind` column and keys of the form
|
|
234
|
+
// `<kind>:<id>`, which no reader speaks any more. v2 to v3 only
|
|
235
|
+
// adds tables, v3 to v4 renames one, v4 to v5 adds one and v5 to
|
|
236
|
+
// v6 and every step to v9 add columns (v8 a table too), so
|
|
237
|
+
// their rows stay
|
|
238
|
+
if (was < 2) db.exec('drop table if exists runs; drop table if exists unit_last;')
|
|
239
|
+
else if (was < 4 && db.query("select 1 from sqlite_master where type = 'table' and name = 'unit_last'").get() != null) db.exec('alter table unit_last rename to check_last; alter table check_last rename column unit to id; drop index if exists runs_unit;')
|
|
240
|
+
const suites = db.query("select name from pragma_table_info('suites')").all() as { name: string }[]
|
|
241
|
+
if (suites.length > 0 && !suites.some((c) => c.name === 'check_id')) db.exec("alter table suites add column check_id text not null default ''")
|
|
242
|
+
if (suites.length > 0 && !suites.some((c) => c.name === 'unit')) db.exec("alter table suites add column unit text not null default ''")
|
|
243
|
+
if (suites.length > 0 && !suites.some((c) => c.name === 'cpu_ms')) db.exec('alter table suites add column cpu_ms integer default null')
|
|
244
|
+
if (suites.length > 0 && !suites.some((c) => c.name === 'phase')) db.exec("alter table suites add column phase text not null default ''")
|
|
245
|
+
if (suites.length > 0 && !suites.some((c) => c.name === 'phase_ms')) db.exec('alter table suites add column phase_ms integer default null')
|
|
246
|
+
if (suites.length > 0 && !suites.some((c) => c.name === 'fail_line')) db.exec("alter table suites add column fail_line text not null default ''")
|
|
247
|
+
db.exec(SCHEMA)
|
|
248
|
+
db.exec(`pragma user_version = ${SCHEMA_VERSION}`)
|
|
249
|
+
}
|
|
250
|
+
db.exec('commit')
|
|
251
|
+
} catch (e) {
|
|
252
|
+
try { db.exec('rollback') } catch { /* the transaction is already gone */ }
|
|
253
|
+
throw e
|
|
254
|
+
}
|
|
255
|
+
}
|