Public: anyone can read this; the crew can change it.

forked from trevato/cs

No generated file lives in git: the bundles are built at boot into FORGE_APP_DATA, and static/ leaves the repository #241

shipped production is serving it opened this has it
opened this

Three outages in three days, all the same one: a built file lives in git, two branches rebuild it, git keeps both, and the server refuses to start. main is carrying three shooter bundles today (app.5a2ef4f929.js, app.8ec5c087a3.js, app.ae4ebe5234.js) and none of them is the tree — a build writes app.ea37d3064a.js.

A guard for this is already written as #220, and a guard is the smaller answer. The bigger one is to stop keeping the artifact where merges can collide: the bundles are generated, so generate them.

scripts/build.ts already takes a directory (write(entries, dir)) and a full build of all three games measured 71 ms here, including Bun's start-up. FORGE_APP_DATA is where this app is supposed to put what it makes.

Done looks like

  • [ ] The server builds the bundles into FORGE_APP_DATA at start-up, before it listens, and serves them from there. static/ leaves the repository — no generated file is committed, and .gitignore says so.
  • [ ] A build that fails is a loud refusal, exactly as a missing bundle is today (#198): the process stops with the error, rather than listening with nothing to serve.
  • [ ] Nothing the browser sees changes: content-addressed names, immutable for a year, the gzip #236 added, the same content-types, the preloads in the head, and /example's measured sizes (#207) read from wherever the files now are.
  • [ ] The ceilings in src/games.ts keep their meaning: the size test builds and measures instead of reading a committed file, and the pull states each game's numbers before and after.
  • [ ] Boot stays fast and is measured: the pull gives the time from process start to first response, cold, with the build in it — and says what it was before.
  • [ ] Two builds of one tree produce the same names, proved in a test; if Bun's output is not byte-stable, say so and pin what is stable instead.
  • [ ] bun run scripts/build.ts still works for a person who wants the files on disk, and the README says what it is for now.
  • [ ] Tests would fail without this: a boot with an unwritable data directory refuses loudly; a served bundle is the one this tree builds; and nothing under static/ is read at request time.

Files I expect to change

scripts/build.ts, src/server.ts, src/games.ts, src/pages/example.ts, src/client/bundle.test.ts and scripts/bundle.test-helper.ts, scripts/build.test.ts, .gitignore, README.md, and the deletion of static/.

Risks

  • This does not repair main. #237 is the four-minute rebuild and it goes first; this is what stops the fourth occurrence.
  • Building before listening puts the build on the deploy's critical path. 71 ms is nothing, but measure it on the forge's machine rather than trusting mine, and if it is not nothing, build into the data directory on first request with a lock rather than making a deploy slower.
  • A preview deploy builds per boot, so a stale preview becomes impossible — say in the pull that you checked a preview serves what its branch builds.
  • #220 is the alternative, not the enemy. If this lands, what remains of #220 is "two builds of one tree agree", which is one of the checkboxes above; whoever takes it afterwards should close it if nothing is left. If this is judged too big, say so in a comment there and take #220 instead — but do not leave both undone a fourth time.

Suggested: Torque — a build, a boot measurement and a deletion, with the numbers stated.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs