Operations

The build pipeline

Every build migrates, ensures the administrator, seeds an empty dataset and checks the sign-up posture — before the frontend is compiled, and idempotently.

The build command is two halves, written out rather than hidden in a lifecycle hook, because a reader has to be able to see both:

bash
node scripts/bootstrap-deployment.mjs && next build

What the first half does

  1. Migrations, under a Postgres advisory lock so two builds cannot interleave.
  2. The administrator, created and confirmed exactly once, its password never rewritten.
  3. The demo content, only into a dataset with no content documents, always with createIfNotExists on fixed ids.
  4. A sign-up posture assertion, reported loudly in the log.

With no database environment at all — a plain CI run — it prints a multi-line SKIPPED notice and exits successfully. It never degrades a deployed build to a skip, and there is no environment variable that turns it off. A deployment that silently shipped an unmigrated database would be the worst possible failure mode.

Idempotency, in two independent layers

  • The gate. The content seed runs only against a dataset with no content documents.
  • The mutation. Fixed ids plus createIfNotExists, so an existing document is returned untouched even when the gate opens.

The second is not redundancy. The gate is a read followed by writes in separate transactions, so two concurrent builds can both pass it; fixed ids make that race converge on one copy rather than two.

Keeping content in step with a repository

The default is to never overwrite an author's edit, which is right for a product. For a site whose content is source-controlled — this one — there is an explicit, operator-only mode that writes with createOrReplace and bypasses the gate:

bash
npm run seed:demo:replace

It is never part of a build. Running it means deciding that the repository is the source of truth for that dataset, which is a decision a build should not make on anyone's behalf.

Crons

A daily keep-alive query stops a free-tier database from pausing after a week of inactivity, and an orphan sweep reclaims asset bytes whose rows were deleted.