diff --git a/AGENTS.md b/AGENTS.md index 820d56b..cb634f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,6 +70,11 @@ This is a Phoenix Liveview monorepo, the blogging library is in blogex, the appl Main make targets: +- `make setup` - one-time clone setup (and after any mix.exs gains a dep): + fetches Hex deps for both apps and the app's JS packages. Needs network - + run it yourself; agent sandboxes refuse installs. check/test/verify never + touch the network: they run a local `deps.check` guard that fails with a + pointer here when deps are missing. - `make check` - cheap static gate for both apps (no DB): app `credo --strict` + format **check** (`mix format --check-formatted`), blogex compile (warnings fatal) + format **check**, then `scripts/check-pins.sh`. It never rewrites diff --git a/Makefile b/Makefile index 02bdd37..510b357 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MISE_BIN ?= mise -.PHONY: check precommit verify hooks deps compile test test-elisp format +.PHONY: check precommit verify hooks deps setup compile test test-elisp format # Cheap static gate over both apps, cheapest first. No database, no coverage: # this is the loop you run freely while working and before every commit. @@ -47,9 +47,18 @@ hooks: echo "installed $$hooks_dir/$$name -> $$hook"; \ done -# Sync dependencies -deps: - @make -C app deps +# One-time setup for this clone (and after any mix.exs gains a dependency): +# fetches Hex deps for both apps and the app's JS packages. Needs network, and +# is deliberately NOT wired into check/test/verify: `mix deps.get` contacts +# hex.pm even when everything is already installed (the advisory check), which +# fails offline/sandboxed. The everyday loops run the per-app `deps.check` +# guard instead and point here when deps are missing. +setup: + @$(MAKE) -C app MISE_BIN=$(MISE_BIN) deps + @$(MAKE) -C blogex MISE_BIN=$(MISE_BIN) deps + +# Kept for muscle memory; `setup` is the canonical entry point. +deps: setup # Compile the project compile: diff --git a/app/Makefile b/app/Makefile index d9eb780..106fe10 100644 --- a/app/Makefile +++ b/app/Makefile @@ -3,12 +3,13 @@ MISE_BIN ?= mise MISE_EXEC = $(MISE_BIN) exec -- -.PHONY: check precommit deps assets compile test test.coverage sobelow coverage crap format format.check credo verify +.PHONY: check precommit deps deps.check assets compile test test.coverage sobelow coverage crap format format.check credo verify -# Run all static analysis checks (no database required). `format.check` only -# reports: a gate that rewrites the files it judges can never fail. Fix -# formatting with `make format`. -check: credo format.check +# Run all static analysis checks (no database, no network required). The +# deps.check guard fails loudly with a pointer at `make setup` when Hex deps +# are missing. `format.check` only reports: a gate that rewrites the files it +# judges can never fail. Fix formatting with `make format`. +check: deps.check credo format.check # Precommit target for CI/pre-commit hooks precommit: check compile @@ -27,19 +28,33 @@ assets: deps: assets $(MISE_EXEC) mix deps.get +# Local, network-free guard: every package pinned in mix.lock must be present +# in deps/. check, test and test.coverage depend on this instead of `deps`, so +# the everyday loops never touch the network: `mix deps.get` contacts hex.pm +# even when everything is already installed (the advisory check), which fails +# in offline/sandboxed environments. Fetching is `make setup`'s explicit job, +# run from the repo root. +deps.check: + @test -f mix.lock || { echo "deps.check: no mix.lock - run 'make setup' at the repo root" >&2; exit 1; } + @missing=""; \ + for dep in $$(sed -n 's/^[[:space:]]*"\([^"]*\)".*/\1/p' mix.lock); do \ + [ -d "deps/$$dep" ] || missing="$$missing $$dep"; \ + done; \ + test -z "$$missing" || { echo "deps.check: missing:$$missing - run 'make setup' at the repo root" >&2; exit 1; } + # Compile the project compile: $(MISE_EXEC) mix compile --warnings-as-errors # Run tests (requires PostgreSQL running on localhost:5432) # Note: If you don't have PostgreSQL, you can skip tests with `make check` -test: deps compile +test: deps.check compile $(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 compile +test.coverage: deps.check compile rm -f cover/*.coverdata $(MISE_EXEC) mix test --cover --export-coverage default diff --git a/blogex/Makefile b/blogex/Makefile index 00a20c3..914d050 100644 --- a/blogex/Makefile +++ b/blogex/Makefile @@ -3,17 +3,19 @@ MISE_BIN ?= mise MISE_EXEC = $(MISE_BIN) exec -- -.PHONY: check precommit deps compile test test.coverage coverage crap format format.check verify +.PHONY: check precommit deps deps.check compile test test.coverage coverage crap format format.check verify -# Cheap static gate for this library (no database, no coverage). Runs from the -# root `make check`, which loops app then blogex. `format.check` only reports: +# Cheap static gate for this library (no database, no network, no coverage). +# The deps.check guard fails loudly with a pointer at `make setup` when Hex +# deps are missing. Runs from the root `make check`, which loops app then +# blogex. `format.check` only reports: # a gate that rewrites the files it judges can never fail. Fix formatting with # `make format`. # # 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.check +check: deps.check compile format.check # Per-app precommit loop: same target name and alias shape as app/, so both # apps share one habit (compile --warning-as-errors, format, test). @@ -24,18 +26,32 @@ precommit: deps: $(MISE_EXEC) mix deps.get +# Local, network-free guard: every package pinned in mix.lock must be present +# in deps/. check, test and test.coverage depend on this instead of `deps`, so +# the everyday loops never touch the network: `mix deps.get` contacts hex.pm +# even when everything is already installed (the advisory check), which fails +# in offline/sandboxed environments. Fetching is `make setup`'s explicit job, +# run from the repo root. +deps.check: + @test -f mix.lock || { echo "deps.check: no mix.lock - run 'make setup' at the repo root" >&2; exit 1; } + @missing=""; \ + for dep in $$(sed -n 's/^[[:space:]]*"\([^"]*\)".*/\1/p' mix.lock); do \ + [ -d "deps/$$dep" ] || missing="$$missing $$dep"; \ + done; \ + test -z "$$missing" || { echo "deps.check: missing:$$missing - run 'make setup' at the repo root" >&2; exit 1; } + # Compile the project (warnings are fatal) compile: $(MISE_EXEC) mix compile --warnings-as-errors # Run the test suite -test: deps +test: deps.check $(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 +test.coverage: deps.check rm -f cover/*.coverdata $(MISE_EXEC) mix test --cover --export-coverage default