#!/bin/sh
# pre-receive — skillgate as a SERVER-SIDE gate. The hard layer, self-hosted.
#
# The README's "hard" layer is CI + branch protection. But GitHub does not enforce
# branch protection on PRIVATE repos under a Free personal account, so that layer
# is unavailable to a lot of solo/private users. A bare repo on a box the agent
# can't reach closes that gap for free:
#
#   - `git push --no-verify` skips CLIENT hooks only; a server-side pre-receive
#     ALWAYS runs. The agent cannot turn it off from its side.
#   - the definition of done lives on THIS box ($SKILLGATE_DIR/.skillgate), so the
#     agent (who can push but not log in) cannot weaken it by editing what it pushes.
#
# Rejects any push whose tree fails `skillgate check`.
set -eu

SKILLGATE_DIR="${SKILLGATE_DIR:-/opt/skillgate}"   # holds the authoritative .skillgate/
# Pin the gate version so a push can't pull a different skillgate off npm than the
# box was provisioned with (reproducible + smaller supply-chain surface on the hot
# path). Override with SKILLGATE_VERSION=... if you deliberately want another.
SKILLGATE_VERSION="${SKILLGATE_VERSION:-0.1.0}"
ZERO=0000000000000000000000000000000000000000

rc=0
while read -r oldrev newrev refname; do
  [ "$newrev" = "$ZERO" ] && continue              # branch deletion → allow
  work="$(mktemp -d)"
  git archive "$newrev" | tar -x -C "$work"        # materialize the pushed tree
  # Enforce THIS box's pinned definition of done, not whatever was pushed.
  rm -rf "$work/.skillgate"
  cp -r "$SKILLGATE_DIR/.skillgate" "$work/.skillgate"
  if ! ( cd "$work" && npx -y "@reneza/skillgate@$SKILLGATE_VERSION" check ) >&2; then
    echo "" >&2
    echo ">> PUSH REJECTED on $refname — skillgate gate failed (see above)." >&2
    echo ">> This gate runs on the server; 'git push --no-verify' cannot skip it." >&2
    rc=1
  fi
  rm -rf "$work"
done
exit $rc
