mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-10 20:27:01 +01:00
Moving bump_patch_version.sh into .pre-commit-config.yaml introduced three bugs, all reproduced in clean clones of HEAD before fixing and re-verified after: 1. DATA LOSS. pre-commit stashes unstaged changes and restores them with `git apply`. The bump rewrites pubspec.yaml inside that window, so an uncommitted edit near the `version:` line made the restore conflict: the commit aborted AND the developer's edit vanished from the working tree, recoverable only from $PRE_COMMIT_HOME. Only non-mutating checks belong in that file. 2. A commit rejected by the file-length gate still had its version bumped and staged, riding into the next successful commit. `always_run: true` with no `fail_fast` meant "ordered last" did not mean "runs last only on success" -- which is exactly what the comment above it claimed. 3. `files: ^pubspec\.yaml$` narrowed the crdt_sync override check from unconditional to staged-only, so a poisoned pubspec arriving by pull or merge was invisible to every later commit that did not touch pubspec. The hand-written hook this replaced checked it every time; that narrowing quietly widened the hole the check exists to close. The fix keeps the framework for the read-only gates and puts the mutation back outside it: install_hooks.sh now writes a wrapper that runs pre-commit's generated hook first and bump_patch_version.sh only if it passed. Plus fail_fast, and always_run on the crdt check. Also fixes three stale references this refactor created: a load-bearing layout comment citing the renamed _buildRaw, bin/todo_desktop.dart naming two split-away test files, and CLAUDE.md giving two different suite runtimes. Verified: unstaged pubspec edit preserved through a commit; a rejected commit leaves nothing staged; a merged-in override is caught on an unrelated commit. 313 tests, 100% coverage, gate exits 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
2.4 KiB
YAML
53 lines
2.4 KiB
YAML
# Pre-commit hooks for todo.
|
|
#
|
|
# This repo previously used a hand-written .git/hooks/pre-commit installed by
|
|
# scripts/install_hooks.sh. It moved to the pre-commit framework when the
|
|
# 250-line file-length gate was added, so the *checks* live in one place with
|
|
# one install path.
|
|
#
|
|
# Only non-mutating checks belong here. pre-commit stashes unstaged changes
|
|
# for the duration of the run and restores them with `git apply` afterwards;
|
|
# a hook that rewrites a tracked file inside that window makes the restore
|
|
# conflict, which aborts the commit AND drops the developer's uncommitted
|
|
# edit. scripts/bump_patch_version.sh rewrites pubspec.yaml, so it stays out
|
|
# of this file and runs from the wrapper hook instead -- see
|
|
# scripts/install_hooks.sh.
|
|
#
|
|
# fail_fast so a later check cannot run after an earlier one has already
|
|
# rejected the commit.
|
|
#
|
|
# Install with: scripts/install_hooks.sh
|
|
# The hooks are `language: system` because they are plain bash/python already
|
|
# on this machine; pre-commit must not build an isolated env for them.
|
|
fail_fast: true
|
|
repos:
|
|
- repo: local
|
|
hooks:
|
|
# Gate first: a local crdt_sync override left in place makes CI build a
|
|
# library this checkout has never tested against, silently and in both
|
|
# directions. Blocking the commit is the only thing that prevents it.
|
|
#
|
|
# always_run/pass_filenames rather than `files:`: a poisoned pubspec can
|
|
# arrive by pull, merge or --no-verify, and a staged-only trigger would
|
|
# then never look at it again for any commit that does not happen to
|
|
# touch pubspec.yaml. The old hand-written hook checked it every time;
|
|
# narrowing that to staged-only silently widened the hole this gate
|
|
# exists to close.
|
|
- id: no-crdt-sync-override
|
|
name: no local crdt_sync dependency_override
|
|
entry: scripts/check_no_crdt_sync_override.sh
|
|
language: system
|
|
pass_filenames: false
|
|
always_run: true
|
|
|
|
# The shared 250-line cap. Checks only the files in the commit, so an
|
|
# unrelated commit never breaks; exits 1 rather than warning. No
|
|
# baseline and no allowlist -- those are suppressions. The exemption
|
|
# list (generated/vendored/data files) lives with the checker in
|
|
# ~/utils/file_length/config.py so the gate and the survey cannot
|
|
# disagree.
|
|
- id: file-length
|
|
name: file length <= 250 lines
|
|
entry: scripts/check_file_length.sh
|
|
language: system
|