130 lines
6.2 KiB
Markdown
130 lines
6.2 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/**
|
|
Review trigger: Any script added to, removed from or repurposed in scripts/; any
|
|
change to which of them gates a commit or a release
|
|
```
|
|
|
|
> 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
|
|
this project has and what each script 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. So this file answers the questions that
|
|
table does not, and points at it for everything else.
|
|
|
|
## This project has seven of them, and that is deliberate
|
|
|
|
The template this repository adopted ships around twenty scripts. Privacy:
|
|
Period Tracker took six of them and wrote one of its own. `scaffold.sh` copies
|
|
none of them on purpose — *"an unconfigured `release.sh` landing in every new
|
|
repository is a loaded gun, not a head start"* — so each one is taken having been
|
|
read and configured.
|
|
|
|
**The ones not taken were not forgotten.** `release.sh`, `deploy.py`,
|
|
`backup.sh`, `restore-check.sh`, `preflight.sh`, `healthcheck.sh`, `migrate.sh`,
|
|
`status.sh`, `dev.sh`, `audit-gate.mjs` and `release-notes.mjs` all assume a
|
|
deployed Node or Postgres service with an npm dependency tree and a URL.
|
|
Privacy: Period Tracker is an Android app that ships through Google Play and has
|
|
no server at all. A release here is an AAB and a Play Console submission, so
|
|
[`security/SECURITY_CHECKLIST.md`](security/SECURITY_CHECKLIST.md) carries what
|
|
`release.sh` would have gated.
|
|
|
|
`duplication.py` and `dead-code.py` need no server, but both read a JavaScript or
|
|
TypeScript source tree: `dead-code.py` walks an export graph and excludes
|
|
Next.js entry points, and `duplication.py` does not scan `.kt` at all. Either
|
|
would have to be adapted before it said anything true about Kotlin, so neither is
|
|
taken yet.
|
|
|
|
`controls.sh` reports which operational controls a project has — backups, a
|
|
proven restore, somewhere errors go, an environment that is not production — and
|
|
exits `2` until some are declared expected. An app with nothing running has none
|
|
to declare, so taking it would install a check that could only report that it did
|
|
not run.
|
|
|
|
Two were deferred rather than declined:
|
|
|
|
- **`verify.sh`** — worth having once there is a real check suite to aggregate.
|
|
Today `./gradlew check` is the whole answer and wrapping it would add a layer
|
|
that could only be wrong.
|
|
- **`check-env.sh`** — its SPEC ships empty and exits `2` until it has entries,
|
|
and this project has no environment variables yet. It gets taken with the first
|
|
signing or Play credential, which is exactly the moment it becomes worth
|
|
running.
|
|
|
|
**The seventh is `schema-guard.sh`, and it is this project's own.** It clears
|
|
the bar in *Adding one* below the hard way: it exists because the check it
|
|
replaces was proved green over exactly the failure it claimed to catch. Its
|
|
header carries that proof.
|
|
|
|
If this project later grows a script the template already has, take that one
|
|
rather than writing a new one — the arguments in its header are the part that
|
|
took the longest.
|
|
|
|
## 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 hook or a CI step that treats a
|
|
`2` as success has quietly turned the check off. Each script states its codes at
|
|
the top; read them there.
|
|
|
|
**And a `2` is not automatically a failure either — it is a question.** The
|
|
pre-commit hook learned this the hard way: `secrets.sh` exits `2` on a commit
|
|
that only *deletes* files, because a deletion has no added lines to scan, and
|
|
that is the correct answer. The hook treated every non-zero as a refusal and
|
|
printed *"possible credential in the staged changes"* over a plain `git rm`, so
|
|
deletion-only commits were impossible and the reason given was a security
|
|
problem that did not exist.
|
|
|
|
The rule that came out of it: **on a `2`, ask whether the check could have had
|
|
anything to look at.** The hook now refuses a `2` whenever the staged diff adds
|
|
lines — because then the scanner did have input and checking nothing is exactly
|
|
the failure `2` reports — and accepts it, saying so, when the diff only removes
|
|
content. Both directions are proved with `prove-guard.sh`-style injections
|
|
before either is believed.
|
|
|
|
The hooks are the other place work gets stopped:
|
|
[`architecture/githooks/README.md`](architecture/githooks/README.md) has the one
|
|
install command and the table of what each hook runs. Note that **`post-commit`
|
|
pushes**, and that `pre-commit` here runs Gradle rather than the template's
|
|
TypeScript typecheck.
|
|
|
|
## Where to start in a fresh clone
|
|
|
|
1. `git config core.hooksPath .githooks` — per clone, every time, and an
|
|
uninstalled hook fails silently.
|
|
2. `bash scripts/secrets.sh --tracked` — the one-time audit of what is already
|
|
committed. The staged-diff mode is for the hook; this mode is for the day you
|
|
clone.
|
|
3. `./gradlew test` — the prediction acceptance tests run on the JVM, so this
|
|
needs no emulator and should be fast.
|
|
4. [`architecture/GUARDS.md`](architecture/GUARDS.md) — how to write a check that
|
|
can actually fail, before you write one. `scripts/prove-guard.sh` performs its
|
|
first rule.
|
|
|
|
## Adding one
|
|
|
|
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`](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.
|