Queue-North-Website/docs/TOOLS.md

144 lines
6.5 KiB
Markdown

# Tools — where the scripts are, and which ones can stop you
```
Status: Current
Owner: _null
Last reviewed: 2026-08-18
Governs: scripts/**, .githooks/**, package.json — the tooling, and which of
it can stop you
Review trigger: Any script added to, removed from or repurposed in scripts/; any
change to which of them gates; any change to the npm scripts
```
> A signpost, deliberately. Every project that adopts this template has a
> `docs/TOOLS.md`, so "read `docs/TOOLS.md` first" is an instruction that works
> without knowing anything about the project — which is the whole reason this
> file exists at a fixed path.
## The list is not here
**[`architecture/README.md`](architecture/README.md)** holds the table of what
ships in `scripts/` and what each one is. That is the one copy.
A second table here would be two records of one fact, and the other one would
never hear that a script was renamed — the failure `DOC_TRUST_MAP.md` exists to
prevent, applied to the tooling instead of the documents. So this file answers
the questions that table does not, and points at it for everything else.
## If this project has fewer scripts than the template lists
That is the intended state, not a broken copy. The template's `scaffold.sh`
writes the documents and deliberately leaves the scripts behind — *"an
unconfigured `release.sh` landing in every new repository is a loaded gun, not a
head start"* — so they are taken one at a time, having been read.
This project took ten of them on 2026-08-18 and declined the rest. What it
declined, and why, is in `docs/history/DEVELOPMENT_LOG.md` under that date. The
short version: no `release.sh` or `deploy.py` until the roll-forward path to
nebula is written down, and no `controls.sh` until there are backups for it to
report on.
## Which ones can stop you
Not in a table, because the honest answer lives in each script's own header and
would go stale here. The rule that matters:
**Exit code `2` is never a pass.** These scripts distinguish "the check ran and
found nothing" from "the check did not run", because those look identical from
the outside and only one of them is evidence. A CI step or a hook that treats a
`2` as success has quietly turned the check off. Each script states its codes at
the top; read them there.
In this repository specifically, `check-env.sh` and `verify.sh` will both exit 2
if you gut their configuration, and `preflight.sh` exits 2 when the site is
simply unreachable — which is the case you most want to tell apart from a pass.
The hooks are the other place work gets stopped. See below.
## Where to start in a fresh clone
```bash
npm install
git config core.hooksPath .githooks # per clone. Not optional. See below
bash scripts/check-env.sh --file .env # what is configured, before anything reads it
bash scripts/secrets.sh --tracked # what is already committed
```
Then [`architecture/GUARDS.md`](architecture/GUARDS.md) before you write a check
of your own — how to write one that can actually fail.
## The hooks
Three, in `.githooks/`, because `.git/hooks` is not versioned and a hook living
there protects exactly one clone.
| Hook | What it runs here |
| --- | --- |
| `pre-commit` | `scripts/secrets.sh` on the staged diff, then `npm run build` when source is staged |
| `commit-msg` | refuses a message with no conventional type |
| `post-commit` | **pushes to `origin`** |
`git config core.hooksPath .githooks` is per clone, so every checkout runs it
once. An uninstalled hook fails silently, which is the same class of problem the
hooks exist to prevent.
**Two things worth knowing before you rely on them.**
`pre-commit` is **not** the template's version. That one runs `npx tsc --noEmit`
and `npx vitest run`; this project has neither TypeScript nor a test runner, so
installing it unchanged would have refused every commit. It runs the secret scan
— which is the reason the hook earns its place here at all, given the Zoho form
tokens that reached four commits before anyone noticed — and then `npm run build`
when `src/`, `server/`, `index.html`, `vite.config.js` or `package.json` is
staged. **That is a build, not a test.** It catches a broken import and will not
catch a broken behaviour.
`post-commit` pushes, and that is the intent — but it has a consequence worth
holding on to: whatever documentation was not in that commit is now behind the
code by one push. That is the mechanical reason `docs/WORK_CYCLE.md` asks for doc
edits in the same commit rather than in a tidy-up afterwards. With this hook
installed, *"I will document it next commit"* means the site has already
published the version without it.
Escape hatches, both loud on purpose: `SKIP_GUARDS=1 git commit …` and
`SKIP_PUSH=1 git commit …`.
## This project's npm scripts
Run from the repository root.
| Command | What it does |
| --- | --- |
| `npm install` | dependencies |
| `npm run dev` | Vite and the Express API together, via `concurrently`. Frontend on 5173, API on 3001 |
| `npm run build` | **three steps**: the client bundle, then an SSR bundle from `src/entry-server.jsx`, then `scripts/prerender.js`, which writes static HTML for every route. This is the only real gate this project has |
| `npm run build:client` | the client bundle alone. Does **not** prerender — do not use it to produce a release |
| `npm run preview` | serve the built client |
| `npm start` / `npm run server` | the Express server alone, serving `dist/` |
| `npm run docker:build` / `docker:run` | build and run the image locally |
| `npm run docker:compose:up` / `:down` / `:logs` | the compose stack |
| `npm run docker:push` | build, tag and push `queue-north-website:dev` to the Forgejo registry |
| `npm run docker:test` | build the image and smoke-test it on 3001 |
**There is no `npm test`, and that is not an omission in this table.** There is
no test runner in the project. `docs/qa/ClaudeQACoverage.md` carries it as a
standing gap.
A liveness check by hand, when you want one without the script:
```bash
curl -s https://qn.isnull.dev/api/health # {"status":"ok","db":"ok","timestamp":"…"}
```
## Adding a script
Put it in `scripts/`, give it a header saying what it does and **which incident
motivated it**, state its exit codes, and add a row to
`architecture/README.md`'s table — this file's `Review trigger` fires on exactly
that.
The bar, from the scripts that are already here: **done by hand three times, or
once with a consequence.** A script written before either of those has no failure
to describe in its header, which is the part that stops the next person deleting
it.