textmachine/docs/scripts/githooks/pre-commit

105 lines
10 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# Зонный pre-commit фрагмент docs/. Ставится тем же диспетчером .git/hooks/pre-commit, что и
# фрагмент фронта (диспетчер перебирает */scripts/githooks/pre-commit).
#
# ┌─ ЧТО ЭТО ДЕЛАЕТ И ЧЕГО НЕ ДЕЛАЕТ — читать до того, как ругаться на него ───────────────────┐
# │ ДЕЛАЕТ: при коммите, задевающем ЛЮБОЙ док, проверяет file:line-якоря живых доков; при │
# │ коммите с D-логом или PROGRESS дополнительно сверяет три носителя номера головы и │
# │ производные числа доков (счёт очереди, зон, вес открытых строк регистра платформы). │
# │ НЕ ДЕЛАЕТ: не блокирует коммит НИКОГДА — только печатает предупреждение и выходит 0. │
# │ Не трогает содержимое, не правит файлы, не лезет в сеть, не запускает тесты. │
# │ ЕСЛИ МЕШАЕТ: `git commit --no-verify` обходит всю цепочку; выключить только этот фрагмент — │
# │ `chmod -x docs/scripts/githooks/pre-commit`. Ни то, ни другое ничего не ломает. │
# │ ⚠ НО: обход — это ДЕЙСТВИЕ, а не мнение. Считаешь, что хук неправ, шумит, мешает или ловит │
# │ не то — НАПИШИ ВЛАДЕЛЬЦУ (он передаст оркестратору), а не обходи молча и не подпирай │
# │ хаком/воркэраундом. Тихая деградация гейта не оставляет следа: следующая сессия получит │
# │ его же в прежнем виде и потратит то же время, а мы не узнаем, что он врал. Жалоба на │
# │ гейт — полезный сигнал и повод его починить; молчаливый обход — потеря сигнала. │
# │ ПОЧЕМУ ВООБЩЕ: голова CURRENT-STATE отставала ТРИЖДЫ у трёх РАЗНЫХ оркестраторов │
# │ (D39.81 · D39.83 · D39.112 п.5б). Одна ошибка у независимых сессий на одном поле — │
# │ свойство поля, а не сессий, поэтому проверка механическая, а не нормой прозой. │
# │ ПОЧЕМУ ПРЕДУПРЕЖДАЕТ, А НЕ БЛОКИРУЕТ: фрагмент общий, он сработает у сессии, которой я не │
# │ писал промт. Останавливать чужой коммит — решение владельца, не моё (оркестратор №15). │
# └────────────────────────────────────────────────────────────────────────────────────────────┘
#
# Ложные тревоги, которых здесь СОЗНАТЕЛЬНО избегают, — это важнее, чем полнота: хук, который
# врёт, учат игнорировать, и тогда он хуже отсутствующего.
# · числа считаются по СОДЕРЖИМОМУ КОММИТА (`--from-index`), а не по рабочему дереву, иначе
# частичный стейдж (своя правка в общем файле) давал бы ругань на то, что в коммит не поедет;
# · голова сверяется ТОЛЬКО когда в коммите есть D-лог, то есть когда идёт ратификация — норма
# требует бампать голову в том же коммите, что аппенд ноты, и вот этот случай и ловится;
# коммит, который правит PROGRESS по другому поводу, про голову не спрашивают;
# · линт якорей (D39.148) читает СКАНИРУЕМЫЕ доки по содержимому коммита — иначе незакоммиченное
# дерево живого полигона в docs/experiments/ давало бы ругань на чужой WIP; ЦЕЛИ якорей берутся
# из дерева осознанно: код не входит в docs-коммит, и его истина — дерево.
staged=$(git diff --cached --name-only)
[ -z "$staged" ] && exit 0
dlog='docs/architecture/05-decisions-log.md'
prog='docs/PROGRESS.md'
# ⚠ БЫСТРОГО ВЫХОДА ПО «коммит не задевает docs/» БОЛЬШЕ НЕТ (21.08, разбор с внешним ревьюером).
# Он ампутировал ЦЕЛЕВУЮ сторону дельта-сверки якорей: якорь протухает, когда двигается ЦЕЛЬ, а цели
# у нас в основном КОД — то есть главный двигатель протухания приезжал ровно тем коммитом, на котором
# хук молча выходил. Плюс расширение области линта на platform/docs и frontend/docs в старый триггер
# не попадало вовсе. Цена отмены нулевая: линт ~0.2 с и warn-only; блок производных ЧИСЕЛ по-прежнему
# гейчен составом коммита ниже (он дорогой и осмыслен только при ратификации).
warn=0
# 1) Голова. Только при ратификации (D-лог в коммите).
if printf '%s\n' "$staged" | grep -qx "$dlog"; then
after() {
if printf '%s\n' "$staged" | grep -qx "$1"; then git show ":$1" 2>/dev/null; else git show "HEAD:$1" 2>/dev/null; fi
}
note=$(after "$dlog" | grep -oE '^## D39\.[0-9]+' | tail -1 | sed 's/^## //')
banner=$(after "$dlog" | head -1 | grep -oE 'D1D39\.[0-9]+' | sed 's/^D1//')
claimed=$(after "$prog" | head -8 | grep -oE 'голова D39\.[0-9]+' | head -1 | sed 's/^голова //')
if [ -n "$note" ] && [ -n "$claimed" ] && [ "$note" != "$claimed" ]; then
echo "pre-commit ⚠ docs: голова разошлась — последняя нота D-лога $note, CURRENT-STATE говорит $claimed." >&2
echo " Норма: голова бампается в ТОМ ЖЕ коммите, что аппенд ноты (промт оркестратора)." >&2
warn=1
fi
if [ -n "$note" ] && [ -n "$banner" ] && [ "$note" != "$banner" ]; then
echo "pre-commit ⚠ docs: шапка-диапазон D-лога $banner при последней ноте $note." >&2
warn=1
fi
fi
# 2) Производные числа — по содержимому коммита, и ТОЛЬКО когда в коммите носители этих чисел
# (гейтовка прежняя, D39.112). Ненайденный литерал скрипт тоже считает расхождением: проверка,
# молча перестающая проверять, хуже отсутствующей.
if printf '%s\n' "$staged" | grep -qE "^($dlog|$prog)$"; then
if command -v python3 >/dev/null 2>&1 && [ -f docs/scripts/counts.py ]; then
if ! out=$(python3 docs/scripts/counts.py --check --from-index 2>&1); then
echo "pre-commit ⚠ docs: производные числа разошлись с пере-счётом:" >&2
# Печатаем строки-находки, а если их нет (скрипт УПАЛ) — весь вывод: предупреждение с пустым
# телом нечитаемо и учит игнорировать хук.
printf '%s\n' "$out" | grep '✗' >&2 || printf '%s\n' "$out" | tail -5 >&2
warn=1
fi
fi
fi
# 3) Якоря file:line живых доков (D39.148 — ревизия D39.126 «--lint только руками»). Сканируемые
# доки читаются по содержимому коммита, поэтому чужой незакоммиченный WIP невидим; ЦЕЛИ якорей
# читаются из дерева — код не часть docs-коммита. ~0.2 с, warn-only.
if command -v python3 >/dev/null 2>&1 && [ -f docs/scripts/counts.py ]; then
if ! out=$(python3 docs/scripts/counts.py --lint --from-index 2>&1); then
echo "pre-commit ⚠ docs: мёртвые или переросшие file:line-якоря:" >&2
# Печатаем строки-находки, а если их нет (скрипт УПАЛ) — весь вывод: предупреждение с пустым
# телом нечитаемо и учит игнорировать хук.
printf '%s\n' "$out" | grep '✗' >&2 || printf '%s\n' "$out" | tail -5 >&2
warn=1
fi
fi
if [ "$warn" -eq 1 ]; then
echo "pre-commit: это ПРЕДУПРЕЖДЕНИЕ — коммит не остановлен (обход: --no-verify)." >&2
echo " Считаешь, что хук неправ или мешает? Напиши ВЛАДЕЛЬЦУ — не обходи молча и не подпирай хаком." >&2
echo " Предупреждение про ЧУЖОЙ док (не твоя зона)? Чинить его НЕ надо и нельзя — сообщи владельцу" >&2
echo " и коммить дальше: якорь в чужом доке уехал из-за твоей правки цели, чинит его владелец зоны." >&2
fi
exit 0