gates aborts at the first failing prerequisite, so one red gate hides every gate ordered behind it. The set that claims "every gate that guards a merge" reports one verdict per run instead of all of them.
The shape is already settled elsewhere in this file
docs-check solves exactly this, one level down, and says so in its own comment:
The docs gate: every check runs, every failure is reported in one pass.
It accumulates across DOCS_CHECK_PRE_CMDS, runs the build, then check_site_links, and reports the lot. gates is the same problem one level up and does the opposite.
What it cost, concretely
Measured in doppler this week, on a branch being prepared for merge.
GATES_DEPS there is:
lint changelog-check drift-check doxygen-check docs-check validate-check
test-all test-stubs test-api-docs test-snippets test-rust
abi-check link-check consumer-faces-check glibc-check
specan-check check-isotime-parity coverage coverage-gate docker-examples
glibc-check sits mid-list and cannot pass on a modern dev box by design — its own comment says so:
Only meaningful against a build made on that glibc — CI runs this in a Debian 10 container, and running it on a modern distro will fail on the local build's newer symbols, which is the check working, not a bug.
So on any dev machine make gates stops there, and the six targets behind it — specan-check, check-isotime-parity, coverage, coverage-gate, docker-examples — are structurally unreachable. Not slow, not flaky: never run.
That is worse than an ordinary abort because the failure reads as expected. A developer sees a gate whose comment says failing here is correct, shrugs, and never learns that a third of the set did not execute.
What was hiding behind it: make coverage was broken on that machine for a missing package (compiler-rt, which clang does not depend on, so -fprofile-instr-generate had no runtime and every link failed). Stepping over glibc-check with a GLIBC_MAX override is what finally surfaced it. With -k it would have been reported on the first run, next to the glibc red, weeks earlier.
Why a consumer cannot fix this locally
The contract in this file's own header:
Per-repo variation is expressed as CONFIGURATION — the variables below, set in the repo's own Makefile before include standard.mk — never as a local edit, because a local edit is the fork this exists to prevent.
and standard-check enforces it. local.mk "may only ADD targets", so it cannot redefine gates either. A consumer's only options are to remember make -k gates every time, or to add a differently-named target — which splits the SSOT that gates-check exists to protect.
So it has to change here.
Suggested change
Make gates re-invoke itself over its dependency list with -k, so the accumulate-don't-abort behaviour is the default and no one has to remember a flag:
gates: ## Run every gate that guards a merge
@$(MAKE) -k $(GATES_DEPS)
@echo ""
@echo "gates: ALL PASS"
Properties worth checking against, since they are what makes the current form safe:
- a failing gate still fails the run (recursive make propagates the status), so this is not a weakening;
gates: ALL PASS still cannot print unless everything passed;
gates-check is unaffected — it walks the closure from gates over make's database, and $(GATES_DEPS) is still the list.
The one behaviour change is that $(GATES_DEPS) becomes a recursive invocation rather than a prerequisite list, which affects how a -j run schedules them. Worth confirming against a repo that runs gates in parallel before merging.
Related, not proposed here
glibc-check failing by design on every dev box is its own question — it could derive whether the build is old-glibc and skip with a printed reason, leaving CI's Debian 10 container to enforce it for real. That would be a genuine fix rather than a workaround. But it is separable: with -k, the gate can stay honestly red without taking six others down with it, which is the part that matters for this issue.
Filed from doppler 0.42.0.
gatesaborts at the first failing prerequisite, so one red gate hides every gate ordered behind it. The set that claims "every gate that guards a merge" reports one verdict per run instead of all of them.The shape is already settled elsewhere in this file
docs-checksolves exactly this, one level down, and says so in its own comment:It accumulates across
DOCS_CHECK_PRE_CMDS, runs the build, thencheck_site_links, and reports the lot.gatesis the same problem one level up and does the opposite.What it cost, concretely
Measured in doppler this week, on a branch being prepared for merge.
GATES_DEPSthere is:glibc-checksits mid-list and cannot pass on a modern dev box by design — its own comment says so:So on any dev machine
make gatesstops there, and the six targets behind it —specan-check,check-isotime-parity,coverage,coverage-gate,docker-examples— are structurally unreachable. Not slow, not flaky: never run.That is worse than an ordinary abort because the failure reads as expected. A developer sees a gate whose comment says failing here is correct, shrugs, and never learns that a third of the set did not execute.
What was hiding behind it:
make coveragewas broken on that machine for a missing package (compiler-rt, whichclangdoes not depend on, so-fprofile-instr-generatehad no runtime and every link failed). Stepping overglibc-checkwith aGLIBC_MAXoverride is what finally surfaced it. With-kit would have been reported on the first run, next to the glibc red, weeks earlier.Why a consumer cannot fix this locally
The contract in this file's own header:
and
standard-checkenforces it.local.mk"may only ADD targets", so it cannot redefinegateseither. A consumer's only options are to remembermake -k gatesevery time, or to add a differently-named target — which splits the SSOT thatgates-checkexists to protect.So it has to change here.
Suggested change
Make
gatesre-invoke itself over its dependency list with-k, so the accumulate-don't-abort behaviour is the default and no one has to remember a flag:Properties worth checking against, since they are what makes the current form safe:
gates: ALL PASSstill cannot print unless everything passed;gates-checkis unaffected — it walks the closure fromgatesover make's database, and$(GATES_DEPS)is still the list.The one behaviour change is that
$(GATES_DEPS)becomes a recursive invocation rather than a prerequisite list, which affects how a-jrun schedules them. Worth confirming against a repo that runsgatesin parallel before merging.Related, not proposed here
glibc-checkfailing by design on every dev box is its own question — it could derive whether the build is old-glibc and skip with a printed reason, leaving CI's Debian 10 container to enforce it for real. That would be a genuine fix rather than a workaround. But it is separable: with-k, the gate can stay honestly red without taking six others down with it, which is the part that matters for this issue.Filed from doppler 0.42.0.