mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
c318ef1fb8
* fix(typecheck): make a crashed typecheck report as crashed, not as clean
`pnpm run typecheck` was `cross-env NODE_OPTIONS="--max_old_space_size=8192"
tsc --noEmit`. When the heap cap is too small for the program graph, V8 aborts
part way through checking, so tsc emits ZERO diagnostics and dies. cross-env
normalises the SIGABRT to exit 1, and V8's explanation goes to stderr — so a
caller that captures stdout gets an empty log, a bare non-zero exit, and no
type errors anywhere in it.
That is indistinguishable from a clean pass to anything that judges the run by
its output, which is what people and scripts actually do (a clean run also
prints nothing). Reproduced with a deliberate `const x: number = 'nope'` in
`src/`: at a 4096 MB cap the run reported 0 errors and hid it completely; the
same tree at 8192 MB reported it.
Measured cold on a clean checkout, with that error in place as a visibility
control:
node 24.18.1 4096 -> OOM/0 diags 4608 -> OOM/0 diags
5120 -> pass/found 8192 -> pass/found
node 22.22.2 6144 -> pass/found 8192 -> pass/found
So the current 8192 is NOT at the cliff — the cliff is between 4608 and 5120,
and 8192 carries ~1.6x headroom. The number is left alone deliberately: the CI
runner has 16 GB, and a cap near that trades a self-describing V8 abort for a
kernel OOM-kill, which says less. Raising it would only move the cliff anyway.
What changes is that crossing the cliff becomes loud. `scripts/typecheck.mjs`
runs tsc and classifies the outcome:
- clean -> prints an explicit "typecheck: OK" line, so silence is
no longer what a pass looks like
- type errors -> passed through untouched, exit code preserved
- crashed -> a CRASHED banner naming the cause, on stdout AND stderr
(the original blind spot was a stdout-only capture),
plus a ::error:: annotation under Actions
- exit 0 w/ diags -> treated as a crash rather than trusted
Heap exhaustion, an outside kill (out of system RAM / a container limit) and an
unexplained abort are named separately, because the fix differs — an outside
kill wants a LOWER cap, not a higher one. The cap is passed as an argv flag
rather than via NODE_OPTIONS so an inherited NODE_OPTIONS cannot override it.
Override per-run with TYPECHECK_HEAP_MB=<mb>.
Covered by scripts/__tests__/typecheck.test.ts, which drives the classifier with
stub typecheckers (sub-second, vs minutes for a real run). Each of the five
cases was mutation-checked against the wrapper: 6/6 mutations killed, each by
its own test. One mutation initially SURVIVED and exposed a real gap in the
test — the crash banner is written to stderr, so asserting only on stdout let a
grep-poisoning regression through; both streams are asserted now.
CI already invoked this via `pnpm run typecheck` and so inherits the wrapper;
the step carries a comment against being "simplified" back to a bare tsc.
* fix(husky): stop the pre-push hook echoing success over a failed typecheck
The hook was:
npm run typecheck
echo "Typecheck successful"
`sh` without `set -e` runs the next line regardless of what the previous one
returned, and a script's exit status is its last command's — so the `echo`
became the hook's verdict. A failing typecheck on `main` printed "Typecheck
successful" and the push went through.
Measured against the real hook in a throwaway repo on `main`, with a stub `npm`
whose exit code is controlled:
npm exit hook exit (before) hook exit (after)
0 0 0
1 0 1
134 0 134
Before, all three printed "Typecheck successful". The 134 row is the case this
matters most for: that is V8 aborting on heap exhaustion, which emits no
diagnostics at all, so the hook was echoing success over a typecheck that had
not merely failed but never finished. The failure message points at
scripts/typecheck.mjs, which distinguishes the two.
The branch/username guard above is unchanged, and still makes the hook a no-op
off `main`.