build(make): prove setup converged instead of trusting build_runner's exit code
`make setup` ran build_runner and reported success whether it produced the 39 files a fresh clone needs or almost nothing. `flutter test`'s only guard checked a single sentinel file, which is why the 2026-08-09 4-of-46 gap still read as 26 passed / 17 failed instead of a missing build step. ci/check_codegen.sh walks every `part` directive under lib/ and confirms the sibling file it names exists, then wires into both `setup` (fail loud right after codegen if it under-produced) and `test` (fail loud, exit 69, if nobody ran setup at all). Replaces the one-file guard, which would have missed 44 of the 45 directives that exist today.
This commit is contained in:
@@ -16,15 +16,20 @@ help: ## Show this help
|
|||||||
setup: ## Fetch dependencies and generate code (run this after a fresh clone)
|
setup: ## Fetch dependencies and generate code (run this after a fresh clone)
|
||||||
flutter pub get
|
flutter pub get
|
||||||
$(MAKE) generate
|
$(MAKE) generate
|
||||||
|
@ci/check_codegen.sh
|
||||||
|
|
||||||
.PHONY: generate
|
.PHONY: generate
|
||||||
generate: ## Regenerate freezed/json_serializable/riverpod sources
|
generate: ## Regenerate freezed/json_serializable/riverpod sources
|
||||||
dart run build_runner build --delete-conflicting-outputs
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
|
.PHONY: check-codegen
|
||||||
|
check-codegen: ## Prove every part directive has a generated file on disk
|
||||||
|
@ci/check_codegen.sh
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: ## Run the widget and unit tests
|
test: ## Run the widget and unit tests
|
||||||
@test -f lib/core/auth/user_preferences.freezed.dart \
|
@ci/check_codegen.sh \
|
||||||
|| { echo "FAIL — generated sources missing; run: make setup"; exit 69; }
|
|| { echo "FAIL — generated sources missing or stale; run: make setup"; exit 69; }
|
||||||
flutter test
|
flutter test
|
||||||
|
|
||||||
# Why the guard above: *.freezed.dart and lib/**/*.g.dart are gitignored, so a
|
# Why the guard above: *.freezed.dart and lib/**/*.g.dart are gitignored, so a
|
||||||
@@ -33,6 +38,23 @@ test: ## Run the widget and unit tests
|
|||||||
# looks like broken tests and is actually a missing build step. After
|
# looks like broken tests and is actually a missing build step. After
|
||||||
# generating, the same suite is 452 passed. A test run that cannot compile
|
# generating, the same suite is 452 passed. A test run that cannot compile
|
||||||
# should say so in those words.
|
# should say so in those words.
|
||||||
|
#
|
||||||
|
# `setup` and `test` both call ci/check_codegen.sh rather than one calling
|
||||||
|
# the other's target, because `setup`'s job is "make the tree usable" (fails
|
||||||
|
# loud if codegen silently produced less than the tree needs) and `test`'s
|
||||||
|
# job is "is the tree usable right now" (fails loud if nobody ran setup at
|
||||||
|
# all, or ran it before a source file changed). Same check, two different
|
||||||
|
# questions, so a shared script rather than a shared Make target — a Make
|
||||||
|
# target can only be reused by depending on it, which would make `test`
|
||||||
|
# imply `flutter pub get` and `build_runner`, both slow, every run.
|
||||||
|
#
|
||||||
|
# The check walks every `part '<name>.g.dart'`/`part '<name>.freezed.dart'`
|
||||||
|
# directive under lib/ and confirms the named sibling file exists — not one
|
||||||
|
# sentinel file (the previous guard checked only
|
||||||
|
# user_preferences.freezed.dart, which would have missed 44 of the 45
|
||||||
|
# directives that exist today). See ci/check_codegen.sh for why this is
|
||||||
|
# preferred over `flutter analyze`: cheaper, and it targets exactly the
|
||||||
|
# generated/ungenerated distinction rather than static analysis in general.
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: ## Static analysis (analysis_options.yaml at the repo root)
|
lint: ## Static analysis (analysis_options.yaml at the repo root)
|
||||||
|
|||||||
Executable
+50
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Prove that generated sources exist for every part directive that requires
|
||||||
|
# one, after `make generate` has run. Lives here rather than inline in the
|
||||||
|
# Makefile so it can be read, run by hand (`make check-codegen`), and changed
|
||||||
|
# under review — same reasoning as ci/secrets.sh (D-27).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
# *.freezed.dart and lib/**/*.g.dart are gitignored, so a fresh or stale
|
||||||
|
# checkout can silently have some but not all of the files a `part`
|
||||||
|
# directive names. `dart run build_runner build` exits 0 whether it produced
|
||||||
|
# everything the tree needs or almost nothing — exit code is not evidence
|
||||||
|
# (T-47). What IS evidence: every `part '<name>';` directive in lib/ names a
|
||||||
|
# sibling file, and that file either exists or it doesn't. This walks every
|
||||||
|
# directive and checks its target directly, rather than trusting a single
|
||||||
|
# sentinel file (the old `test` guard checked one file,
|
||||||
|
# user_preferences.freezed.dart, and would have missed 44 other gaps).
|
||||||
|
#
|
||||||
|
# On 2026-08-09 this exact condition was 4 generated files present where 46
|
||||||
|
# were needed. `flutter analyze` would also catch it, but slower and later —
|
||||||
|
# this check is the cheapest thing that proves the same fact.
|
||||||
|
|
||||||
|
missing=0
|
||||||
|
checked=0
|
||||||
|
|
||||||
|
while IFS=: read -r file part_line; do
|
||||||
|
# grep -H prefixes exactly one "file:" — no line numbers, so a colon
|
||||||
|
# inside the match (there is none here, but be safe) can't split wrong.
|
||||||
|
# part_line looks like: part 'auth_state.freezed.dart';
|
||||||
|
target=$(printf '%s' "$part_line" | sed -E "s/^part '([^']+)';.*/\1/")
|
||||||
|
dir=$(dirname "$file")
|
||||||
|
checked=$((checked + 1))
|
||||||
|
if [ ! -f "$dir/$target" ]; then
|
||||||
|
echo "MISSING generated file: $dir/$target (required by 'part' directive in $file)" >&2
|
||||||
|
missing=$((missing + 1))
|
||||||
|
fi
|
||||||
|
done < <(grep -rH "^part '" lib --include='*.dart')
|
||||||
|
|
||||||
|
if [ "$checked" -eq 0 ]; then
|
||||||
|
echo "FAIL check-codegen — found zero 'part' directives under lib/; the check itself is broken, not the tree." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$missing" -gt 0 ]; then
|
||||||
|
echo "FAIL check-codegen — $missing of $checked generated files are missing. Run: make generate" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "check-codegen — $checked/$checked generated files present."
|
||||||
Reference in New Issue
Block a user