#!/usr/bin/env bash
# pre-push — the gate runs before anything leaves this machine.
#
# Installed by `npm ci` (tools/install-git-hooks.mjs sets core.hooksPath to
# .githooks). It runs `npm run gate` — the validate job of ci.yml minus the
# two steps that need the network or the install itself — and refuses the
# push if any step fails. A tag push or a deletion sends no code and skips it.
#
# `git push --no-verify` skips every pre-push hook. AGENTS.md says who may use
# that and when; an assistant may not.
set -euo pipefail
remote="${1:-}"
url="${2:-}"

# Tags carry no code and a deletion sends nothing; only a branch with commits
# is gated. So `--no-verify` never has a legitimate use here.
sending=0
while read -r local_ref local_sha _remote_ref _remote_sha; do
  [[ "$local_ref" == refs/tags/* ]] && continue
  [[ "$local_sha" =~ ^0+$ ]] || sending=1
done
[[ "$sending" -eq 1 ]] || exit 0

# In a linked worktree git exports GIT_DIR into this hook. Left in place, it
# makes every git command the gate runs answer for this repository from any
# directory — a test that creates a throwaway repository would then act on
# the real one. The gate must run exactly as it does from a shell.
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_PREFIX GIT_COMMON_DIR

echo "pre-push: running the gate before pushing to ${remote} (${url}) — AGENTS.md"
if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then
  echo "pre-push: note — the working tree has uncommitted changes; the gate validates the tree as it is, not only the commits being pushed"
fi
if ! npm run --silent gate; then
  echo "pre-push: the gate failed; nothing was pushed. Fix the failure and push again." >&2
  exit 1
fi
