build: assert DEBUG is off in every emitted bundle (closes #170)
All checks were successful
check / check (push) Successful in 19s

PR #169 made DEBUG a build-time flag defaulting off, but nothing guarded
the wiring. The tests load src/shared/constants.js outside a bundle and
take the jest fallback branch, so deleting the __BUILD_DEBUG__ define
from build.js left all tests passing and make check green while silently
restoring the drainable-wallet vulnerability in every shipped artifact.
The property only exists in the emitted output, so it is now asserted
against the emitted output.

script/verify-build reads two independent facts per bundle. Which
bundles must be inspected comes from esbuild's metafile: build.js writes
dist/constants-bundles.txt naming every emitted JS output whose input
set includes constants.js, so the set is derived from the real
dependency graph rather than a hardcoded count or filenames. What each
bundle's DEBUG state is comes from BUILD_DEBUG_MARKER, a new constant
derived from DEBUG itself that the bundler folds to exactly one of two
string literals. Deriving the bundle set from the marker would be the
silent-pass hole: a bundle with no marker would be indistinguishable
from content/index.js, which legitimately contains none.

The marker is a plain string rather than a match on minified `DEBUG:!1`,
because minifier output is not a contract across esbuild versions. When
DEBUG is not known at build time the fold cannot happen and both
literals survive, which is exactly the shape of the regression this
guards against. Every way of failing to determine a bundle's state is a
hard failure: missing manifest, empty manifest, a listed file that does
not exist, both markers, neither marker, the wrong marker, or a bundle
carrying a marker while absent from the manifest. There is no path on
which the script exits 0 without positively identifying the expected
marker in at least one bundle.

It runs on the build path only. make build and make build-debug both
invoke it, the latter asserting the inverse, and Dockerfile:17 runs a
bare make build, so CI fails on a release build with a live debug
branch. It is deliberately not in script/check: that would make check
depend on dist/ existing and pull a full build into its time budget, and
the obvious workaround -- skip when dist/ is absent -- is precisely the
silently-green behaviour this exists to prevent.
This commit is contained in:
2026-08-09 05:06:50 +00:00
parent ad9162d057
commit 627c0c158e
7 changed files with 281 additions and 45 deletions

View File

@@ -59,6 +59,13 @@ behavior. The build prints which mode it used. See the
distribute a debug build** — every wallet it creates gets the same publicly
known test recovery phrase.
Both builds end by running `script/verify-build`, which reads the compiled
`DEBUG` state back out of the emitted bundles and fails the build if it is not
the one that was asked for. The test suite cannot check this: it loads
`src/shared/constants.js` outside a bundle, so it only ever sees the fallback
value. The assertion is on the artifacts because that is where the property
lives.
## Entrypoints
This repository adheres to the
@@ -79,6 +86,12 @@ provide:
- `script/fmt` — format all files (writes)
- `script/fmt-check` — check formatting (read-only)
- `script/check` — run test, lint, and fmt-check
- `script/verify-build` — assert the compiled `DEBUG` state of the bundles in
`dist/`: every bundle containing `src/shared/constants.js` must have `DEBUG`
off, or on when `AUTISTMASK_DEBUG=1`. Run automatically at the end of
`make build` and `make build-debug`; fails loudly rather than passing if it
cannot determine a bundle's state. Not part of `make check`, which does not
depend on build artifacts existing.
- `script/docker` — build the Docker image tagged via `script/projectname`
- `script/cibuild` — CI entrypoint: plain `docker build .`
- `script/precommit` — run by the git pre-commit hook; runs `script/check`