Skip to content

standard.mk: gates should accumulate, not abort — one red gate hides every gate behind it #18

Description

@hunterdsp

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions