diff --git a/README.md b/README.md index 4ef284c..3f8f6ed 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ provide: step's exit status, saying on stderr that it did and why. Every step of `make build` runs through it; `make build-debug` runs none of them through it. A step that succeeds removes nothing, and a removal that cannot be completed - is reported as loudly as one that was + is reported as loudly as one that was. - `script/test-verify-build` — exercise every failure mode of `script/verify-build` against a fixture tree in a temp dir, asserting the exit status and the message of each, assert the state of `dist/` on disk after a diff --git a/TODO.md b/TODO.md index c60adca..a986140 100644 --- a/TODO.md +++ b/TODO.md @@ -45,6 +45,17 @@ but the review is broader than any of them. # Completed Steps +- 2026-10-07: `script/discard-dist-on-failure` returns the failed step's own + exit status even when it cannot write its message, to a closed stderr or to a + pipe nobody reads any more + ([#342](https://git.eeqj.de/sneak/AutistMask/issues/342)); it used to return 2 + or 141 instead. Its header now states that an interrupt removes nothing and + says nothing, because an interrupt is not a build failure, and that a failed + `check-censored --require-dist` removes `dist/` like any other step, because a + `dist/` not cleared of the name `RULES.md` bars must not ship. Neither + behaviour changed. `script/test-verify-build` runs the wrapper with stderr + closed. + - 2026-10-06: `script/bootstrap` no longer reports success while node cannot find a package listed in `dependencies` or `devDependencies` of `package.json` ([#263](https://git.eeqj.de/sneak/AutistMask/issues/263)). After the install diff --git a/script/discard-dist-on-failure b/script/discard-dist-on-failure index 35d439c..b44ca14 100755 --- a/script/discard-dist-on-failure +++ b/script/discard-dist-on-failure @@ -3,22 +3,19 @@ # step fails, remove dist/ before returning its exit status. Our own extension # to scripts-to-rule-them-all, wrapped around every step of make build. # -# Why: with AUTISTMASK_DEBUG=1 exported in the calling shell, make build -# compiles a debug bundle and then fails on it in script/verify-build — but the -# bundle is already written. It is loadable, and every wallet it creates gets -# the publicly committed test recovery phrase from src/shared/constants.js. A -# failed release build that leaves that behind is a smaller version of the trap -# the verifier exists to close, and "the failure was loud" only works on an -# operator who does not load dist/chrome/ anyway. Removing the artifact does not -# depend on that. +# Why: with AUTISTMASK_DEBUG=1 exported, make build compiles a debug bundle and +# then fails on it in script/verify-build, after the bundle is written. It is +# loadable, and every wallet it creates gets the publicly committed test +# recovery phrase from src/shared/constants.js, so a failed build must not leave +# it behind. The removal is never silent: it says on stderr that dist/ is gone +# and why. # -# Two things this deliberately does not do. It does not wrap make build-debug: a -# debug build that failed is not a mistakable artifact, and its output is the -# evidence of what went wrong. And it never removes anything on a step that -# SUCCEEDS, including the final check-censored --require-dist pass. -# -# The removal is never silent: it says dist/ is gone and why, on stderr, above -# the build's own failure. +# A failed check-censored --require-dist removes dist/ like any other step: a +# dist/ not cleared of the name RULES.md bars must not ship either. A step that +# succeeds removes nothing. An interrupt (Ctrl-C) removes nothing and says +# nothing: it is not a build failure, and whoever interrupted the build knows it +# did not finish. make build-debug is not wrapped: a debug build that failed is +# not a mistakable artifact, and its output is the evidence of what went wrong. set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" @@ -71,7 +68,10 @@ main() { [ "$_status" -ne 0 ] || return 0 - discard_dist + # A message that cannot be written, to a closed stderr or to a pipe nobody + # reads any more, must not replace the step's status. + trap '' PIPE + discard_dist || true exit "$_status" } diff --git a/script/test-verify-build b/script/test-verify-build index 098ea8d..f7c8d30 100755 --- a/script/test-verify-build +++ b/script/test-verify-build @@ -915,6 +915,22 @@ run_cases() { discard_case "the wrapper given no command removes nothing" \ c_control 1 kept "no command given" "" + # With stderr closed the wrapper cannot write its message, and must still + # remove dist/ and return the step's own status. + build_fixture + _status=0 + (cd "$FIXTURE" && + "$FIXTURE/script/discard-dist-on-failure" sh -c 'exit 6' 2>&-) || + _status=$? + if [ "$_status" -eq 6 ] && [ ! -e "$FIXTURE/dist" ]; then + PASSED=$((PASSED + 1)) + echo " ok: a failed step's status survives a closed stderr" + else + FAILED=$((FAILED + 1)) + echo " FAIL: a failed step's status survives a closed stderr" + echo " exit status $_status, wanted 6, and dist/ must be gone" + fi + check_makefile_wiring }