#!/usr/bin/env bash
# Pre-push gate for pi-devbox: shellcheck every shell script before it leaves
# this clone. Thin wrapper — all logic lives in scripts/lint-shell.sh, which is
# the SAME script the CI release gate runs. One copy, not two: a duplicated
# check that drifts is the failure this repo keeps paying for.
#
# Install per clone:  git config core.hooksPath hooks
# Bypass this gate:   git push --no-verify   (a guard, not a wall)
#
# WHY THIS HOOK EXISTS
#   v1.8.14's first release attempt died at scripts/smoke-test.sh:770 after
#   build-base had already spent ~46 minutes. shellcheck had ALREADY caught the
#   defect — SC2289 at severity error, on the very push that introduced it — and
#   the lint job stayed red for 24 hours, unread, across three runs. The fix at
#   the time was to gate the release on the same script (the `lint-gate` job).
#   This hook is the cheaper end of that: the same finding, before the push,
#   in seconds rather than after a 40 s CI gate or a 46 min build.
#
# WHY IT COULD NOT EXIST UNTIL NOW
#   Measured on v1.8.14 (2026-09-09): shellcheck was absent from the devbox
#   image by all three routes — PATH, dpkg and a filesystem search. So
#   lint-shell.sh exited 2 in every container, and a hook calling it would have
#   refused EVERY push rather than gating anything. `shellcheck` was added to
#   Dockerfile.base in the same change that added this file; on an image built
#   before that, enable this hook and you will simply be told the gate cannot
#   run. That is the correct behaviour, but it is not a working hook — so do not
#   set core.hooksPath on a container older than the release that bakes it.
#
# NOTE ON SCOPE: this lints the WORKING TREE, not the exact commit range being
# pushed. That is deliberate and matches what the CI gate does to the tagged
# tree. It means a defect you have staged-but-not-committed is also reported,
# which is noisy in the safe direction.
set -euo pipefail

HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$HOOK_DIR/.." && pwd)"
LINTER="$REPO_ROOT/scripts/lint-shell.sh"
tag="[lint-shell]"

# Same rule the gate itself applies, applied one level up: a missing check is
# not a pass. If the script is gone, the push is refused rather than waved
# through on the assumption that CI will catch it.
if [ ! -r "$LINTER" ]; then
    echo "$tag refusing the push: $LINTER is missing, so the gate cannot" >&2
    echo "$tag run. A gate that cannot run must not pass." >&2
    exit 2
fi

# Point the message at the actual remedy when the binary is absent, because the
# linter's own message ("install it or run this in CI") is written for a CI
# runner and is misleading inside a container the developer cannot apt-install
# into persistently.
if ! command -v shellcheck >/dev/null 2>&1; then
    echo "$tag refusing the push: shellcheck is not installed, so the gate" >&2
    echo "$tag cannot run. A gate that cannot run must not pass." >&2
    echo "$tag" >&2
    echo "$tag This container predates the image that bakes shellcheck." >&2
    echo "$tag Either recreate onto an image that has it, or unset the hook:" >&2
    echo "$tag   git config --unset core.hooksPath" >&2
    echo "$tag To push this once without the gate: git push --no-verify" >&2
    exit 2
fi

exec bash "$LINTER" "$REPO_ROOT"
