montana/Монтана-Протокол/Код/scripts/pre-commit.sh

97 lines
4.4 KiB
Bash
Raw Normal View History

#!/bin/bash
# spec, разделы [C-7] No-shortcut на apply_*, [C-8] Mandatory SC trace block,
# [C-10] Mandatory deviation tracker
#
# Установка: создать симлинк из этого файла в .git/hooks/pre-commit:
# ln -sf "$(pwd)/scripts/pre-commit.sh" .git/hooks/pre-commit
# (либо запустить scripts/install-hooks.sh)
#
# Этот hook применяется ТОЛЬКО на коммиты в crates/, не на role/spec правки.
set -e
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
CHANGED=$(git diff --cached --name-only --diff-filter=ACM)
CONSENSUS_CHANGED=$(echo "$CHANGED" | grep -E 'crates/(mt-(state|account|entry|consensus|lottery|timechain)|montana-node)/.*\.rs$' || true)
# Gate 1 [C-8] SC trace block — проверяется в commit-msg hook (он получает
# commit message file как $1; pre-commit запускается ДО записи message
# и не имеет доступа к нему). См. scripts/commit-msg.sh.
# Gate 2 [C-10]: SPEC DEVIATION в коде требует DEV-N reference
BAD_DEV=$(git diff --cached -- 'crates/**/*.rs' 2>/dev/null | grep "^+.*SPEC DEVIATION" | grep -v "DEV-[0-9]" || true)
if [ -n "$BAD_DEV" ]; then
echo "ОТКАЗ [C-10]: SPEC DEVIATION без DEV-N reference"
echo "$BAD_DEV"
echo
echo "Каждое // SPEC DEVIATION: ОБЯЗАНО ссылаться на конкретный DEV-NNN entry в docs/SPEC_DEVIATIONS.md"
exit 1
fi
# Gate 3 [C-10]: deviation count в коде vs SPEC_DEVIATIONS.md
if [ -f docs/SPEC_DEVIATIONS.md ]; then
CODE_DEVS=$(grep -rcE "SPEC DEVIATION DEV-[0-9]+" crates/ 2>/dev/null | grep -v ":0$" | awk -F: '{s+=$2} END {print s+0}')
DOC_DEVS=$(grep -c "^## DEV-" docs/SPEC_DEVIATIONS.md 2>/dev/null || echo 0)
if [ "$CODE_DEVS" -gt "$DOC_DEVS" ]; then
echo "ОТКАЗ [C-10]: $CODE_DEVS SPEC DEVIATION в коде, $DOC_DEVS entries в docs/SPEC_DEVIATIONS.md"
echo "Каждый DEV-NNN в коде должен иметь entry в SPEC_DEVIATIONS.md"
exit 1
fi
fi
# Gate 4 [C-7]: запрет прямого mut на consensus state в node/example crates.
# Контекст ±3 строки: cargo fmt может переместить `// SPEC DEVIATION DEV-N`
# comment на отдельную строку (struct literal multi-line) — hook должен видеть
# deviation marker в окрестности, не только на той же строке.
# Gate 4 scope: production binary code только. cargo example bins
# (crates/*/examples/*.rs) — shakedown demos, не consensus path.
BAD_MUT=$(git diff --cached -U3 -- 'crates/montana-node/**/*.rs' ':!crates/*/examples/*.rs' 2>/dev/null \
| awk '
/^\+/ && /\.(insert|remove)\(/ && /(accounts|nodes|candidates|account_table|node_table|candidate_pool)/ {
lines[NR] = $0
target_line[NR] = 1
}
/SPEC DEVIATION DEV-/ {
dev_line[NR] = 1
}
END {
for (n in target_line) {
has_dev = 0
for (k = n - 3; k <= n + 3; k++) {
if (dev_line[k]) { has_dev = 1; break }
}
if (!has_dev) print lines[n]
}
}
' \
|| true)
if [ -n "$BAD_MUT" ]; then
echo "ОТКАЗ [C-7]: прямой mut-доступ к consensus state таблице вне apply_* функции"
echo "$BAD_MUT"
echo
echo "Прямой insert/remove на AccountTable/NodeTable/CandidatePool разрешён только внутри apply_* функций соответствующего crate"
echo "Если deviation legitimate — добавьте // SPEC DEVIATION DEV-NNN с обоснованием в docs/SPEC_DEVIATIONS.md в пределах ±3 строк от insert/remove"
exit 1
fi
# Gate 5: четыре обязательные команды (workspace level)
# Этот gate тяжёлый — запускается только если consensus-critical изменены
if [ -n "$CONSENSUS_CHANGED" ]; then
echo "Pre-commit: проверка fmt..."
cargo fmt --all -- --check >/dev/null 2>&1 || {
echo "ОТКАЗ: cargo fmt --all -- --check не прошёл"
cargo fmt --all -- --check
exit 1
}
echo "Pre-commit: проверка clippy..."
cargo clippy --all-targets -- -D warnings >/dev/null 2>&1 || {
echo "ОТКАЗ: cargo clippy не прошёл"
cargo clippy --all-targets -- -D warnings 2>&1 | tail -30
exit 1
}
fi
echo "Pre-commit: все проверки пройдены"