144 lines
6.5 KiB
Markdown
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.
|