Files
jellytau/scripts/hooks/pre-commit
dtourolle 8f5c9023d0 ci: make the frontend gates real, and fix the coverage script
The repo configured four frontend gates and enforced one of them. eslint
and prettier ran in no workflow and no hook; `bun run check` ran only in
build-release.yml, so a type error could sit on master until somebody cut
a tag; and `bun run test:coverage` had been dead for months.

CI (build-and-test.yml) now runs format:check, lint, check and coverage
alongside the existing boundary and doc-link tripwires.

The coverage script failure was a version mismatch, not a config problem:
@vitest/coverage-v8 resolved to 4.1.10, whose peer range pins vitest
exactly, while package.json asked for ">=1.0.0 <5.0.0" and got 4.0.16 --
every run died on a missing BaseCoverageProvider export. The loose range
is what allowed the pair to drift, so it is now ^4.1.10.

Two ratchets, same policy as MIN_THRESHOLD in traceability-check.yml:

  eslint  --max-warnings=159   (0 errors; 159 is today's backlog, only
                                ever lower it)
  vitest  thresholds           (statements 51 / branches 45 /
                                functions 46 / lines 52, measured at
                                54.6 / 48.7 / 49.6 / 55.1)

no-console is promoted from "off" to "error": the logger-facade
migration it was waiting on is finished -- 8 calls remained, 2 of them
real stragglers in the settings page, now on the facade the file already
imported. The sink itself, tests, and scripts/ are exempted; a CLI whose
stdout is the product is not a stray debug statement.

The threshold was verified to bite by raising it to 99 and watching the
run go red, not by assuming an unfailed gate works.

DR-205 moves to Done; the coverage gate is DR-215.
2026-08-21 18:11:26 +02:00

94 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# JellyTau pre-commit hook — the fast half of CLAUDE.md's "Before Committing"
# list, enforced instead of remembered.
#
# TRACES: | DR-207
#
# Install with: bun run hooks:install (sets core.hooksPath=scripts/hooks)
# Skip once with: git commit --no-verify
#
# What runs here is deliberately limited to gates that finish in seconds:
#
# bun run check svelte-check (types)
# bun run test vitest, single pass
# scripts/check-frontend-boundary.sh domain-taxonomy tripwire (DR-094)
# bun run format:check prettier
# bun run lint --max-warnings=N eslint, warning-count ratchet
# cargo fmt --all -- --check only when src-tauri/ is staged
#
# NOT here, on purpose: `cargo clippy` and `cargo test`. Both take minutes on a
# cold target dir, which turns every commit into a coffee break and trains
# people to reach for --no-verify. CI (.gitea/workflows/build-and-test.yml) is
# where those run; `bun run test:all` is the local equivalent.
set -uo pipefail
# Merge and rebase commits carry someone else's changes, and conflict resolution
# is exactly when a slow gate is least welcome. Let them through — CI still
# gates the merge result.
GIT_DIR_PATH="$(git rev-parse --git-dir 2>/dev/null)" || exit 0
if [ -e "$GIT_DIR_PATH/MERGE_HEAD" ] ||
[ -d "$GIT_DIR_PATH/rebase-merge" ] ||
[ -d "$GIT_DIR_PATH/rebase-apply" ] ||
[ -e "$GIT_DIR_PATH/CHERRY_PICK_HEAD" ]; then
echo "pre-commit: merge/rebase in progress — skipping checks (CI still gates the result)."
exit 0
fi
# Nothing staged (e.g. `git commit --amend` that only edits the message): nothing
# to check.
STAGED="$(git diff --cached --name-only --diff-filter=ACMR)"
if [ -z "$STAGED" ]; then
exit 0
fi
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT" || exit 1
FAILED=0
run_gate() {
label="$1"
shift
echo ""
echo "🔎 pre-commit: $label"
if ! "$@"; then
echo "❌ pre-commit: $label failed"
FAILED=1
fi
}
run_gate "svelte-check (bun run check)" bun run check
run_gate "frontend tests (bun run test)" bun run test
run_gate "frontend/backend boundary" bash scripts/check-frontend-boundary.sh
run_gate "formatting (bun run format:check)" bun run format:check
# Same ratchet as the CI step in build-and-test.yml — keep the two numbers equal,
# or a commit passes here and fails there.
run_gate "lint (bun run lint)" bun run lint --max-warnings=159
# rustfmt only matters when Rust actually changed, and `cargo fmt --check` is
# cheap (no compilation) whenever it does.
if printf '%s\n' "$STAGED" | grep -q '^src-tauri/'; then
if command -v cargo >/dev/null 2>&1; then
echo ""
echo "🔎 pre-commit: rustfmt (src-tauri/ is staged)"
if ! (cd src-tauri && cargo fmt --all -- --check); then
echo "❌ pre-commit: cargo fmt --all -- --check failed"
echo " fix with: cd src-tauri && cargo fmt"
FAILED=1
fi
else
echo "⚠️ pre-commit: src-tauri/ staged but cargo is not on PATH — skipping rustfmt."
fi
fi
if [ "$FAILED" -ne 0 ]; then
echo ""
echo "🛑 pre-commit checks failed. Fix them, or bypass deliberately with:"
echo " git commit --no-verify"
exit 1
fi
echo ""
echo "✅ pre-commit checks passed."