#!/usr/bin/env bash # Pre-push gate. Blocks the push if any quality check fails. # # Install: `make hooks` (points git core.hooksPath at .githooks/). # Bypass: never. If this runs slowly, fix the slow test; don't reach # for --no-verify (git-commit skill forbids it). # # Fast path (T-348): run the full ~2min test suite only when the push touches # lib/ (app + runtime Dart source) or pubspec.* (deps / version). test/, # assets/, docs, and tooling changes ride along with a lib change in practice, # and an otherwise-skipped push is covered by the next one that does touch lib. # The full suite is always available via `make push-check`, and the release CI # runs it forced on a tagged version. So a lib/pubspec-free push runs just the # instant decisions + changelog gates. A state we can't classify (unfetched # remote, new branch) runs the full gate. set -euo pipefail cd "$(git rev-parse --show-toplevel)" z40=0000000000000000000000000000000000000000 # Collect every file changed across the commits being pushed. git feeds the # hook one line per ref on stdin: . changed="" force_full=0 while read -r _local_ref local_sha _remote_ref remote_sha; do [[ "$local_sha" == "$z40" ]] && continue # branch deletion — nothing to test if [[ "$remote_sha" == "$z40" ]]; then # New remote branch: diff from its merge-base with main, else play it safe. base="$(git merge-base "$local_sha" origin/main 2>/dev/null || true)" else base="$remote_sha" fi # If we can't resolve a base locally (e.g. the remote advanced and we haven't # fetched its objects), we can't classify the diff — run the full gate. if [[ -z "$base" ]] || ! git cat-file -e "$base^{commit}" 2>/dev/null; then force_full=1 break fi changed+=$'\n'"$(git diff --name-only "$base" "$local_sha")" done # Run the full gate when lib/ (app + runtime source) or pubspec.* (deps / # version) is touched, or when we couldn't classify above. needs_gate=1 if [[ "$force_full" -eq 0 ]]; then trigger_files="$(printf '%s\n' "$changed" | grep -E '^(lib/|pubspec\.)' || true)" [[ -z "$trigger_files" ]] && needs_gate=0 fi if [[ "$needs_gate" -eq 0 ]]; then echo "==> pre-push: no lib/ or pubspec change — decisions + changelog gates, skipping tests" make decisions-validate changelog-gate else echo "==> pre-push: make push-check" make push-check fi