mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 21:27:02 +01:00
A completely absent `prettier` binary went unnoticed long enough for unformatted markdown to reach main. Two independent causes, both fixed here. No pre-push hook existed. The three hooks declaring `stages: [pre-push]` in .pre-commit-config.yaml — prettier, ci-mirror, pytest-coverage — therefore never ran locally at all. Adds one. The pre-commit hook failed open. shfmt and shellcheck were wrapped in `if command -v`, and the entire pre-commit framework was skipped with a warning when its binary was missing. A silently skipped gate is indistinguishable from a passing one, so a missing tool now aborts. - lib/common.sh: require_tool tries `sudo -n pacman -S` and aborts with the exact command when a tool cannot be installed non-interactively; verify_toolchain applies it to all eight binaries the hooks invoke. - The pre-push hook resolves git's stdin refs into --from-ref/--to-ref. Without a range, `pre-commit run --hook-stage pre-push` checks *staged* files, and nothing is staged at push time, so prettier would have received zero files and passed vacuously. - ensure_hooks_path repairs core.hooksPath drift on every hook run. - meta/scripts/install_hooks.sh covers the one case the hooks cannot self-heal — a fresh clone where the config was never set, so nothing runs. `--check` reports state and exits non-zero when incomplete. Note `pre-commit install` must never be used here: it writes into .git/hooks/, which git ignores entirely while core.hooksPath is set. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
146 lines
3.8 KiB
Bash
Executable File
146 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# ============================================================================
|
|
# install_hooks.sh — make this clone's git hooks active and its toolchain complete.
|
|
#
|
|
# Run once per machine / per fresh clone:
|
|
# ./meta/scripts/install_hooks.sh
|
|
#
|
|
# This is the bootstrap that the hooks themselves cannot perform: if
|
|
# core.hooksPath is unset and .git/hooks is empty, git runs nothing at all, so
|
|
# no hook is around to repair the wiring. Everything after that first run is
|
|
# self-healing — each hook re-asserts core.hooksPath and verifies the toolchain.
|
|
#
|
|
# Note: `pre-commit install` is deliberately NOT used. It writes into
|
|
# .git/hooks/, which git ignores entirely while core.hooksPath is set, and
|
|
# newer pre-commit refuses outright. The hooks in linux_configuration/.githooks/
|
|
# invoke `pre-commit run` directly instead, and being tracked they cannot go
|
|
# missing on a clone.
|
|
# ============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
readonly SCRIPT_NAME
|
|
|
|
usage() {
|
|
echo "Usage: $SCRIPT_NAME [--check]"
|
|
echo "Options:"
|
|
echo " --check Verify wiring and toolchain, change nothing, exit 1 if incomplete"
|
|
echo " -h,--help Show this help"
|
|
exit 0
|
|
}
|
|
|
|
CHECK_ONLY=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--check)
|
|
CHECK_ONLY=true
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
cd "$repo_root"
|
|
|
|
# shellcheck source=linux_configuration/.githooks/lib/common.sh
|
|
source "${repo_root}/linux_configuration/.githooks/lib/common.sh"
|
|
|
|
# Every hook that must exist and be executable for the gate to be complete.
|
|
readonly MANAGED_HOOKS=(pre-commit pre-push)
|
|
|
|
# Report on wiring/toolchain without changing anything; non-zero when incomplete.
|
|
run_check() {
|
|
local failures=0 current hook path
|
|
|
|
current="$(git config --get core.hooksPath || true)"
|
|
if [[ $current == "$HOOKS_PATH_REL" ]]; then
|
|
echo "✓ core.hooksPath = ${HOOKS_PATH_REL}"
|
|
else
|
|
echo "✗ core.hooksPath is '${current:-<unset>}', expected '${HOOKS_PATH_REL}'" >&2
|
|
failures=$((failures + 1))
|
|
fi
|
|
|
|
for hook in "${MANAGED_HOOKS[@]}"; do
|
|
path="${HOOKS_PATH_REL}/${hook}"
|
|
if [[ -x $path ]]; then
|
|
echo "✓ ${hook} hook present and executable"
|
|
else
|
|
echo "✗ ${hook} hook missing or not executable: ${path}" >&2
|
|
failures=$((failures + 1))
|
|
fi
|
|
done
|
|
|
|
local entry cmd
|
|
for entry in "${REQUIRED_TOOLS[@]}"; do
|
|
cmd="${entry%%:*}"
|
|
if command -v "$cmd" >/dev/null 2>&1; then
|
|
echo "✓ ${cmd}"
|
|
else
|
|
echo "✗ ${cmd} not installed" >&2
|
|
failures=$((failures + 1))
|
|
fi
|
|
done
|
|
|
|
if ((failures > 0)); then
|
|
echo "" >&2
|
|
echo "${failures} problem(s). Run ./meta/scripts/install_hooks.sh to fix." >&2
|
|
return 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "Hooks are wired and the toolchain is complete."
|
|
}
|
|
|
|
# Point git at the tracked hooks directory and make the hooks runnable.
|
|
install_wiring() {
|
|
local hook path
|
|
|
|
ensure_hooks_path
|
|
echo "✓ core.hooksPath = ${HOOKS_PATH_REL}"
|
|
|
|
for hook in "${MANAGED_HOOKS[@]}"; do
|
|
path="${HOOKS_PATH_REL}/${hook}"
|
|
if [[ ! -f $path ]]; then
|
|
hook_abort "Aborted: ${path} is missing from the working tree. The hooks are tracked in git — restore them with 'git checkout -- ${path}'."
|
|
fi
|
|
chmod +x "$path"
|
|
echo "✓ ${hook} hook executable"
|
|
done
|
|
}
|
|
|
|
main() {
|
|
if [[ $CHECK_ONLY == true ]]; then
|
|
run_check
|
|
return
|
|
fi
|
|
|
|
echo "Wiring git hooks..."
|
|
install_wiring
|
|
|
|
echo ""
|
|
echo "Verifying toolchain..."
|
|
verify_toolchain
|
|
echo "✓ all ${#REQUIRED_TOOLS[@]} required tools present"
|
|
|
|
echo ""
|
|
echo "Pre-building pre-commit hook environments..."
|
|
pre-commit install-hooks
|
|
|
|
echo ""
|
|
echo "Done. Commits run the pre-commit stage; pushes run prettier, ci-mirror,"
|
|
echo "and pytest-coverage. Verify any time with:"
|
|
echo " ./meta/scripts/install_hooks.sh --check"
|
|
}
|
|
|
|
main "$@"
|