todo-app/scripts/install_hooks.sh
Krzysztof kuhy Rudnicki 9349747629
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Repo tasks: repo/done_check/by/evidence fields, filter chips, CLI close/list/search
Four optional note fields (repo, done_check, by, evidence) in the crdt_sync
record, written only when set, so editing a plain idea stays wire-identical
to v3 (note_repository_sync_compat_test). The log is schemaless: no stored
schema version; the legacy sqlite reader stays at v3.

NoteFilter gains a sources facet (ideas / every repo task / one repo);
search also matches the repo. The notes list shows only open ideas by
default (no badge); the filter sheet adds Ideas, Repo tasks and one chip
per repo when repo tasks exist. The per-note sheet shows the task fields
read-only. BACKLOG.md markers carry the new attributes, entity-escaped.

CLI: --repo --done-check --by on add, --close <id> --evidence (refused
without), --list [--repo] [--status], --search, and every id flag takes a
unique 8-character prefix. --list prints id8, status, priority, repo, title.

install_hooks.sh: the wrapper finds pre-commit's generated hook next to
itself, so commits from a git worktree (where .git is a file) work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 08:26:26 +01:00

88 lines
3.0 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# Install this repo's git hooks.
#
# .git/hooks/ is not tracked, so a fresh clone has no hooks. Run this once
# after cloning.
#
# Two layers, deliberately:
#
# 1. pre-commit (the framework) runs the read-only gates in
# .pre-commit-config.yaml -- the crdt_sync override check and the
# 250-line file-length cap.
# 2. A thin wrapper hook installed here runs pre-commit first and, only if
# it passed, runs scripts/bump_patch_version.sh.
#
# The bump cannot live in .pre-commit-config.yaml. pre-commit stashes
# unstaged changes for the duration of its run and restores them with
# `git apply`; the bump rewrites pubspec.yaml inside that window, so an
# uncommitted edit anywhere near the `version:` line makes the restore
# conflict -- which aborts the commit and DROPS the developer's edit from the
# working tree. Running it after pre-commit has exited keeps the mutation
# outside the stash cycle, and gating it on pre-commit's exit status stops a
# rejected commit from leaving a stray bump staged for the next one.
# ============================================================================
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
readonly REPO_ROOT
readonly HOOK="$REPO_ROOT/.git/hooks/pre-commit"
readonly LEGACY_MARKER='bump_patch_version.sh'
# The old hand-written hook has to go before pre-commit will install over it;
# pre-commit refuses to clobber a foreign hook.
remove_legacy_hook() {
if [[ -e "$HOOK" ]] && grep -q "$LEGACY_MARKER" "$HOOK" \
&& ! grep -q 'pre-commit' "$HOOK"; then
rm -f "$HOOK"
echo "Removed the legacy hand-written pre-commit hook."
fi
}
ensure_pre_commit() {
if command -v pre-commit >/dev/null 2>&1; then
return
fi
echo "Installing pre-commit..."
if command -v pipx >/dev/null 2>&1; then
pipx install pre-commit
else
python3 -m pip install --user pre-commit
fi
}
# Wraps pre-commit's own generated hook so the version bump runs after it,
# and only on success. pre-commit install --hook-type pre-commit writes the
# generated script; we move it aside and call it from ours.
install_wrapper() {
local generated="$REPO_ROOT/.git/hooks/pre-commit.pre-commit-generated"
mv "$HOOK" "$generated"
cat > "$HOOK" <<'HOOK_BODY'
#!/bin/bash
# Installed by scripts/install_hooks.sh — see that file for why the version
# bump runs here rather than as a pre-commit hook.
set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
readonly ROOT
# The read-only gates. A non-zero exit here rejects the commit, and the bump
# below never runs, so a rejected attempt leaves no stray version change.
"$(dirname "${BASH_SOURCE[0]}")/pre-commit.pre-commit-generated" "$@"
exec bash "$ROOT/scripts/bump_patch_version.sh"
HOOK_BODY
chmod 755 "$HOOK"
}
main() {
ensure_pre_commit
remove_legacy_hook
pre-commit install
install_wrapper
echo "Installed pre-commit hooks: $HOOK"
}
main "$@"