# Makefile for Blogex library MISE_BIN ?= mise MISE_EXEC = $(MISE_BIN) exec -- .PHONY: check precommit deps compile test test.coverage coverage crap format verify # Cheap static gate for this library (no database, no coverage). Runs from the # root `make check`, which loops app then blogex. # # Sensor slot: add `credo` once the "credo strict, tuned config (both apps)" # yak lands blogex's credo dep + .credo.exs. Do not list it before then — a # gate that claims a check it did not run is worse than no gate. check: compile format # Per-app precommit loop: same target name and alias shape as app/, so both # apps share one habit (compile --warning-as-errors, format, test). precommit: $(MISE_EXEC) mix precommit # Sync dependencies deps: $(MISE_EXEC) mix deps.get # Compile the project (warnings are fatal) compile: $(MISE_EXEC) mix compile --warnings-as-errors # Run the test suite test: deps $(MISE_EXEC) mix test # Run the suite once with coverage exported, so the coverage floor check (and, # later, CRAP) reads the export instead of running the suite a second time. # Stale exports are removed first: the floor must never report an older run. test.coverage: deps rm -f cover/*.coverdata $(MISE_EXEC) mix test --cover --export-coverage default # Enforce the coverage floor (mix.exs -> test_coverage) from that export. No # test run. `mix test.coverage` exits 0 when it finds no export at all, so the # guard below is load-bearing: a floor check that did not run must not pass. coverage: @test -f cover/default.coverdata || { echo "coverage: no exported coverage - run 'make test.coverage' first" >&2; exit 1; } $(MISE_EXEC) mix test.coverage # Enforce CRAP scores (complexity x coverage) from the same export. No test run. # Runs in MIX_ENV=test because that is the environment the export came from: # running in dev would compile a second build and score against a different set # of modules. The guard is load-bearing for the same reason as `coverage`: # ex_crap falls back to 0% coverage when the export is missing, which would # report scores that are pessimistic instead of absent - a false failure is # less bad than a false pass, but neither is acceptable in the Lens pass. # # Threshold is ex_crap's default (30); whether to ratchet it down is an open # decision tracked in the "ratchets, deferred decisions, handoff practice" yak. crap: @test -f cover/default.coverdata || { echo "crap: no exported coverage - run 'make test.coverage' first" >&2; exit 1; } MIX_ENV=test $(MISE_EXEC) mix crap # Format code format: $(MISE_EXEC) mix format # ============================================================================= # Lens pass (heavy checks; root `make verify` runs this before every push) # ============================================================================= # # Sensor slots, cheapest first. Add a sensor as its yak lands, both as a target # and to VERIFY_SENSORS — that variable is the single source of truth for what # this target reports as wired. # - coverage (yak: coverage floors + framework-glue exclusions policy) # WIRED below (test.coverage + coverage). # - crap (yak: CRAP scores with ex_crap (both apps)) # WIRED below (crap); reuses the coverage export. # - mutation (yak: mutation testing with muex — a sensor, not a gate) # # The suite runs FIRST: it is what produces the coverage export that the # sensors below read. (Sampling and reporting are separate steps because # `mix test --cover --export-coverage` exports instead of printing a summary, # and reporting through `mix test.coverage` keeps it to one suite run.) Sensors # still run cheapest-first among themselves. VERIFY_SENSORS := coverage crap verify: @echo "==> verify(blogex): suite (coverage exported), then sensors [$(VERIFY_SENSORS)]" @$(MAKE) test.coverage @for sensor in $(VERIFY_SENSORS); do echo "==> verify(blogex): sensor $$sensor"; $(MAKE) $$sensor || exit 1; done