Add Husky pre-commit + pre-push hooks and a CI coverage threshold for tighter feedback
**Context**
CI runs `yarn test` (lint + types + unit) on every push and PR, but there are no local hooks today. Lint, type, and test failures surface only after pushing — sometimes only after a PR review cycle reveals a red check. The feedback loop is workable but slow, and it's a routine source of "fix typo" / "fix lint" follow-up commits in PR history. Coverage is also tracked (vitest baseline captured in #130) but no threshold is enforced, so coverage can drift downward silently between refactors.
**Task**
Implement the mainstream three-tier check pattern. Pre-commit runs `lint-staged` on the staged files only (ESLint `--fix` + Prettier `--write`) so style and lint problems get caught before they enter history. Pre-push runs `yarn test` — the same lint + types + unit tests CI already runs, just shifted left so failures surface locally before the push lands. CI gains a coverage threshold check using vitest's built-in `coverage.thresholds`, with values seeded at the current baseline so coverage can only ratchet upward.
**Scope:**
* Add `husky` and `lint-staged` as dev dependencies. Run `yarn husky init` to set up the `.husky/` directory and the `prepare` script in `package.json`. The `prepare` script ensures hooks install automatically for anyone running `yarn install`.
* Add `.husky/pre-commit` containing `yarn lint-staged`.
* Add `.husky/pre-push` containing `yarn test`.
* Add a `lint-staged` block to `package.json` covering `*.{ts,tsx}` (ESLint `--fix` + Prettier `--write`) and `*.{md,json,yml,yaml}` (Prettier `--write` only).
* Add a `coverage.thresholds` block to the vitest config (lines, functions, branches, statements). Run `yarn test:coverage` once to capture the current baseline before setting thresholds 1–2 % below those numbers — the ratchet pattern. Coverage can only go up; if it drops, CI fails.
* Add a `yarn test:coverage` script to `package.json` if it doesn't already exist.
* Add a CI step in `.github/workflows/test-and-deploy.yml` that runs `yarn test:coverage` and fails on threshold violation — either a new job or an extra step in the existing `test` job.
* Add a `Hooks and feedback loop` subsection to `CONTRIBUTING.md` describing the three tiers, the `git push --no-verify` escape hatch, and how to debug a hook that's gating a legitimate push.
**Notes:**
Husky is the de-facto JavaScript standard (~30k stars, stable API). `simple-git-hooks` is a lighter alternative if dependency footprint matters more than ecosystem familiarity — flag this in the PR description so reviewers can express a preference. Coverage thresholds belong in CI only: running coverage in pre-push roughly doubles test runtime, slow enough that contributors will start reaching for `git push --no-verify` and the hook becomes functionally dead. Threshold values must be the current baseline, not aspirational — aspirational thresholds either fail constantly (and get bypassed) or hide a smaller gap than reality (and don't catch regressions). If pre-push grows beyond ~10 seconds for typical changes, switch the pre-push hook to `vitest --changed` so only tests for touched files run locally; CI continues to run the full suite.
Acceptance hinges on four measurable things: pre-commit completes sub-second on a 1–3 file change; pre-push runs the full `yarn test` and blocks on failure; CI fails on a coverage drop below baseline with a clear message naming the offending threshold; the `prepare` script installs hooks automatically on a fresh clone (verify in a clean container or `rm -rf node_modules .husky && yarn install`).
0 条评论