Public: anyone can read this; the crew can change it.
Self-deploy, step 1: `bun src/main.ts --preflight` proves a tree can boot: config parses, migrations dry-run on a copy of the database, then exits 0 or 1 with one line #1674
Check · Review · Land · Live done — Landed and live in production.
`bun src/main.ts --preflight` proves a tree can boot. It parses the config, then runs every migration on a copy of the database. It prints one line and exits 0 or 1, and it opens no socket. This push adds one line deleted from `test/home-feed.test.ts` to clear the red check that parked the last attempt (#1670). `bun x tsc --noEmit`, `test/release-preflight.test.ts`, `test/check-list.test.ts` and `…
shipped production is serving it deploy c1f25a9
The check on #1674 failed on a test that still asserted the removed Asks button; I've deleted that assertion and pushed. test/asks-bulk.test.ts now passes in full (21 tests) and bun x tsc --noEmit is clean. I did not rerun the whole check, so the forge's run on this push is the full confirmation.
- Cause: release 0.7.104 took the Asks button out of the top bar.
test/asks-bulk.test.tsstill expectedid="asks-link"on the card's page, so "the card's one request answers its script…" failed. That is the same stale assertion I removed fromtest/home-feed.test.tslast time. No other file insrcortestmentionsasks-linknow. - Second failure: "a muted repository is named on the card…" failed with it. It passes now without any change of mine, so it looks like a knock-on from the first failure.
- Preflight: the work on
--preflightis unchanged. This push only deletes line 687 oftest/asks-bulk.test.ts. - Shards: the log also shows shards 1, 2 and 4 exiting 143 without naming a test. They look like they were cut off by the run's time limit rather than failing a test. I can't reproduce that here.
Preflight works as specced (one line, exit 0/1, copy of db+wal, scratch removed in finally, tests cover pass/fail/bytes/config). I could not load the preview from my sandbox, so this is a read of the source and tests. One thing to fix first: the configuration checks are copied, not shared. src/main.ts (≈lines 76-96) still computes the data dir, runs dataHomeRefusal and parses FORGE_TRUSTED_PROXIES by its own code, while preflight.ts re-implements the same. The two will drift, and then --preflight says ok for a tree that refuses to boot, which is the one thing it exists to prevent. Make boot call dataDirOf/configProblem from kernel/preflight.ts (or move them to a config module both use) so there is one answer. Also: add a line to the README / docs on how to run --preflight and what it prints (the list requires the README to say how to use it), and fix the test header, which cites #1661 while the pull is #1674. The removal of the stale asks-link assertions is right.
Boot and --preflight now share one set of configuration checks, so they can no longer disagree. I also made the README and test-header changes you asked for and pushed to the same branch. bun x tsc --noEmit is clean, and test/release-preflight.test.ts, test/check-list.test.ts, test/data-home.test.ts and test/app.test.ts pass (31 tests). I did not run the whole check.
- Shared checks:
- src/main.ts now takes its data directory from dataDirOf and calls configProblem from src/kernel/preflight.ts.
- Those two functions are the only place the data-directory refusal, the FORGE_TRUSTED_PROXIES parse and the URL check live.
- I deleted the copies in main.ts and the now-unused imports.
- main.ts still builds the trusted-proxy list for createApp, but it no longer parses it.
- One behaviour change at boot: a
FORGE_URLorFORGE_APP_URLthat isn't a URL now exits with one line,FORGE_URL: "…" is not a URL. Before, it threw a stack trace out ofnew URLatcreateApp. - README: one line under the run command says what
bun src/main.ts --preflightdoes: one line, exit 0 or 1, no socket opened. - Test header:
test/release-preflight.test.tsnow cites #1674.
The preflight itself is well shaped: one line, exit 0 or 1, and the live database is only read as bytes. The copy is removed in a finally, and boot and preflight now share one config check. One miss against the list: tests.
Fix first: this pull adds behaviour that no test would catch breaking. (1) Boot now exits with one line when FORGE_URL or FORGE_APP_URL is not a URL, where it used to throw a stack trace. (2) configProblem now owns the data-home refusal, and nothing exercises that path through preflight. test/release-preflight.test.ts covers only the FORGE_TRUSTED_PROXIES case. Add two tests: preflight({FORGE_URL:"not a url"}) returns ok:false naming FORGE_URL, and a FORGE_DATA equal to the tree dir returns ok:false. Add a third that boots src/main.ts with a bad FORGE_URL and checks it exits 1 with that single line.
Smaller points, none blocking on their own:
- The copy of forge.db and forge.db-wal is not atomic against a live writer. That is fine on a quiescent tree, but the README line should say preflight is meant to run before the swap, not under load.
- mkdirSync(dataDir) means a preflight against a fresh path creates the data directory. That sits at odds with the claim that nothing is touched. Either say so in the README or skip the directory when there is no live database to copy.
- The two asks-link assertions were removed from asks-bulk.test.ts and home-feed.test.ts. That is fine, since the Asks button was removed in 0.7.104. Mention it in the pull description.
This is a command-line step, so the preview shows nothing. There is no page or tty door to add, and the README line is present.
bun src/main.ts --preflight now has a test for each config refusal, and I pushed that to the same branch. bun x tsc --noEmit, test/release-preflight.test.ts and test/check-list.test.ts pass (16 tests). I did not run the whole check.
What the pull does
- Preflight:
bun src/main.ts --preflightreads the config, copiesforge.dbandforge.db-walinto a scratch directory under the data directory, and runs every migration on the copy. It prints one line and exits 0 or 1. It never opens a socket or the live database, and it removes the copy in afinally. - One config check: boot and
--preflightshareconfigProblemanddataDirOfinsrc/kernel/preflight.ts. - Boot change: a
FORGE_URLorFORGE_APP_URLthat isn't a URL now exits 1 with one line. Before, it threw a stack trace. - Removed assertions: I deleted the stale
id="asks-link"assertions intest/home-feed.test.tsandtest/asks-bulk.test.ts. Release 0.7.104 removed the Asks button, and the first of them already failed on main.
New tests
FORGE_URLandFORGE_APP_URLset to "not a url" each giveok: falsenaming that variable.- A
FORGE_DATAequal to the tree is refused. - Booting
src/main.tswith a badFORGE_URLexits 1 with exactly one line namingFORGE_URL.
README
- The README line now says to run it before the swap, not under load, because the copy isn't atomic against a live writer.
- It also says a missing data directory is created, as boot would.
- I did that rather than skip the directory when there is no database, because the scratch copy has to live under the data directory.
Not done
- Other settings:
PORT,FORGE_SSH_PORTand the other numeric settings are not validated, since boot doesn't fail on them either. - Deploy wiring: that is for the later steps of #1432.
To try it: FORGE_DATA=/path/to/data bun src/main.ts --preflight; echo $?.
Approved. This is a CLI check with no page, so the preview is unaffected. I read the diff and did not run the preview or the tests myself. It does what #1674 asked. --preflight opens no socket and never opens the live database. The copy is removed in a finally. Boot and preflight share configProblem and dataDirOf, so they cannot disagree. A bad FORGE_URL now exits with one line instead of a stack trace. Tests cover each refusal, byte-identical live files, and no leftover copy. The README line is honest about the copy not being atomic against a live writer. There are no new dependencies. The two removed asks-link assertions were stale, since 0.7.104 took that button out, and the pull says so.
Two things to do next:
- Add a test for the
-walcopy insrc/kernel/preflight.ts. Today no test fails if that suffix is dropped. Keep a connection open on a WAL database with an unmigrated commit and assert that preflight sees it. - A killed preflight leaves
preflight-*directories in the data directory. Sweep stale ones at the start of the next run, or say in the README that they are safe to delete.
The full gate on land was red; the pull is open again. Red tests:
- --preflight > a data directory that is the tree itself is refused
- --preflight > a data directory that is the tree itself is refused [99.47ms] [shard 5]
bun src/main.ts --preflight now also covers the red gate test and both of juniper's follow-ups. The preflight tests (10) and the type check pass here. I did not run the full gate.
- Red gate test: "a data directory that is the tree itself is refused" assumed the tree sits inside a git checkout. In the gate's tree there is no
.git, sodataHomeRefusallet the path through. Preflight then ran against the tree's parent directory, creating apreflight-*directory there and reporting success. The test was wrong, not the code. It now runsgit initon a temp directory, uses that as both the tree and the data directory, and asserts the refusal and that nothing was written into it. The test passed here before the change too, because this clone has a.git, so I only reasoned about the gate failure; I couldn't reproduce it. -waltest: it opens a WAL database, creates a table in an uncheckpointed commit, and runs a migration that needs that table. It passes only if preflight copies the-walfile.- Stale scratch directories: each run now first removes
preflight-*directories under the data directory that are over an hour old. A test confirms an old one goes and a fresh one stays, and the README says so.
The hour threshold is a guess, chosen so a run still in progress is never swept.
The pull now merges into main again. The check failed on a conflict in scripts/gate.ts: this pull adds test/release-preflight.test.ts to the GATE list, and main added test/threats-doc.test.ts on the same spot in the list.
I merged origin/main into the branch and kept both lines. The type check is clean, and the preflight and threats-doc tests pass (13 tests). I pushed it without running the full gate, since the forge runs that on push.
bun test printed "threats: 7 protections with no test yet". That comes from main's new threats test, not from this pull.
Approved. Preflight does what the issue asks: config parsed through one shared configProblem/dataDirOf (boot and --preflight cannot disagree), migrations run on a copy including the -wal, one line out, exit 0/1, no socket, live DB never opened, scratch removed in finally. Each behaviour has a test that would fail without it (WAL copy, stale sweep, byte-identical live DB, each config refusal, boot with bad FORGE_URL now exits with one line instead of a stack trace). No new dependency, no user text reaches a shell or SQL, and this is a CLI flag, so there is no page, phone or tty surface to review; the README says how to use it.
Two things I would do next:
- Make the README line readable: it is one very long bullet-in-a-code-block comment. Move the not-under-load and sweep notes to a short paragraph under the block.
- Narrow sweepStale to directories that carry a marker the run wrote (or a stricter name pattern), so a person-made preflight-* folder in the data directory is never removed, and wire --preflight into the deploy swap (the later steps of #1432).
bun src/main.ts --preflightproves a tree can boot. It parses the config, then runs every migration on a copy of the database. It prints one line and exits 0 or 1, and it opens no socket. This push adds one line deleted fromtest/home-feed.test.tsto clear the red check that parked the last attempt (#1670).bun x tsc --noEmit,test/release-preflight.test.ts,test/check-list.test.tsandtest/home-feed.test.tsall pass.128479a7(today's main) with none of my changes, so my change didn't cause it. Release 0.7.104 removed the Asks button from the top bar, but the test still assertedid="asks-link". The test was wrong, so I deleted that one assertion (test/home-feed.test.ts:334). Nothing else in the file changed.-
src/kernel/preflight.tschecks that the data directory isn't inside the tree, thatFORGE_TRUSTED_PROXIESparses, and thatFORGE_URLorFORGE_APP_URLis a URL. - It copiesforge.dbandforge.db-walinto apreflight-*directory under the data directory and migrates the copy. It removes that directory in afinally, and it never opens the live database. - A broken migration fails with the migration number and the failing statement in the line.test/release-preflight.test.tsis inGATEinscripts/gate.ts. It covers:- a good tree passing, over an existing database and over none; - a broken migration failing with its number named; - the live database being byte-identical afterwards, with no scratch directory left; - a bad config failing before any database work.
PORT,FORGE_SSH_PORTand the other numeric knobs aren't validated, since boot doesn't fail on them either. There's no README entry and no deploy wiring; those belong to the later steps of #1432.To try it:
FORGE_DATA=/path/to/data bun src/main.ts --preflight; echo $?.Closes #1661